My project includes older un-annotated controllers together with newer annotation-based controllers.
I am using the latest Spring jars (3.0.5) and in my dispatcher-servlet.xml there's <mvc:annotation-driven />.
The problem is that <mvc:annotation-driven /> causes the request mapping (through the name property of the controller beans in the dispatcher-servlet.xml) to my un-annotated controllers not to work... each time I direct the request to an un-annotated controller I am getting an error message such as:
org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping found for HTTP request with URI [/some_path/some_page.htm] in DispatcherServlet with name 'dispatcher'
How can I keep the un-annotated controllers as they are but tell spring to recognize their (old style) mapping?
I am looking for solutions with minimum change to the Java code of the controllers that I already have.
Thanks!
When you add <mvc:annotation-driven /> to your config, it replaces the default set of handler mappings and handler adapters, and those defaults were the ones that handled the old-style controllers.
You have 2 options. First thing to try is to remove <mvc:annotation-driven />. You can still use annotated controllers without this. It does add extra features like Jackson JSON support, but if you don't need those extra features, then you don't need it. So try your app without <mvc:annotation-driven /> and see if it still works.
Failing that, you can reinstate the mappings and adapters for your old controllers. You didn't say how your controllers used to have their URLs mapped, but try adding these to your config:
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean class="org.springframework.web.servlet.handler.ControllerClassNameHandlerMapping"/>
If you used SimpleUrlHandlerMapping, then that should be working already.
You also need to add the HandlerAdapter back in:
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
Don't just add these in blindly. Try them individually, and see what the minimal set is to get your old controllers working alongside the new ones.
I found that by exploding the mvc:annotation-driven into it's actual replacement, it was easier to figure out.
<mvc:annotation-driven /> explodes to this:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
</list>
</property>
</bean>
<!-- Configures a validator for spring to use -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator">
<bean class="com.eps.web.spring.validation.SpringMessageSourceMessageInterpolator" />
</property>
</bean>
<bean id="conversion-service" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
Once the magic was gone, I got this error trying to load an "Old" controller:
javax.servlet.ServletException: No adapter for handler [org.springframework.web.servlet.mvc.ParameterizableViewController#
From that point, I added
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
And all my old controllers worked.
Related
We are using Spring Cache Manager with spring-data-redis 1.5.2. These days we want to upgrade spring-data-redis to latest release i.e:1.6.2.RELEASE.
For some weird reason everything works good with 1.5.2 but when upgrading to 1.6.2 we get
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'cacheManager' defined in ServletContext
resource [/WEB-INF/spring-cache.xml]: Unsatisfied dependency
expressed through constructor argument with index 0 of type
[org.springframework.data.redis.core.RedisOperations]: Ambiguous
constructor argument types - did you specify the correct bean
references as constructor arguments?
This message seems like a mistake as redisTemplate is RedisTemplate which implements RedisOperations.
Any idea how to solve it?
P.S
note that when removing the cache configuration the 1.6.2 version seems to work good. So the issue is with the cache.
Configuration
web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-redis.xml
/WEB-INF/spring-cache.xml
</param-value>
</context-param>
spring-redis.xml
<context:annotation-config />
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration" />
<bean
class="org.springframework.security.web.session.HttpSessionEventPublisher" />
<!-- end of seesion managment configuration -->
<bean id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="port" value="${app.redis.port}" />
<property name="hostName" value="${app.redis.hostname}" />
<property name="password" value="${app.redis.password}" />
<property name="usePool" value="true" />
</bean>
<!-- for publishing string over redis channels -->
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory" />
</bean>
<!-- for storing object into redis key,value -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="redisConnectionFactory" />
</bean>
spring-cache.xml
<!-- This file must load after spring-redis.xml -->
<cache:annotation-driven />
<!-- declare Redis Cache Manager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:template-ref="redisTemplate" />
</beans>
It seems the reason for this bug is that RedisCacheManager has two constructors. Both of them has RedisOperations as parameter. Forsome reason Spring couldnot understand its related to the first constructor and not to the second one. a work around is mention constructor-arg index
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
<constructor-arg index="0" ref="redisTemplate"></constructor-arg>
</bean>
While upgrading from Spring Data Redis 1.5.2.RELEASE to 1.6.2.RELEASE, we need to use the below config for RedisCacheManager. Previous releases were using redis-template-ref instead of redis-operations-ref.
<beans:bean id='cacheManager'
class='org.springframework.data.redis.cache.RedisCacheManager'
c:redis-operations-ref='redisTemplate'>
</beans:bean>
This is an old question, but for those who reach this page.
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" factory-method="create" c:connection-factory-ref="jedisConnectionFactory" p:transaction-aware="true" />
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" />
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="${cache.redis.host}" p:port="${cache.redis.port}" p:use-pool="true">
<constructor-arg ref="jedisPoolConfig"></constructor-arg>
</bean>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig" p:maxTotal="${cache.redis.pool.maxTotal}" p:maxIdle="${cache.redis.pool.maxIdle}" p:maxWaitMillis="${cache.redis.pool.maxWaitMillis}" p:testOnBorrow="true" />
I would like any requests that do not resolve to specific controller mappings to go to a view with a name derived from the request path.
Eg localhost/success should end up rendering a view located at /WEB-INF/view/freemarker/success.ftl. I gather from the Spring documentation that this behaviour should be enabled by default, by it doesn't appear to be working with my setup.
<mvc:annotation-driven />
<mvc:interceptors>
<!-- On pre-handle, resolve the device that originated the web request -->
<bean
class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
</mvc:interceptors>
<!-- Spring Mobile -->
<bean
class="org.springframework.mobile.device.view.LiteDeviceDelegatingViewResolver">
<constructor-arg>
<!-- Freemarker -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="false" />
<property name="prefix" value="" />
<property name="suffix" value=".ftl" />
</bean>
</constructor-arg>
<property name="mobilePrefix" value="mobile/" />
<property name="tabletPrefix" value="tablet/" />
</bean>
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/view/freemarker/" />
</bean>
Turns out there was no HandlerMapping implementation found for these requests, and so DispatcherServlet never called the DefaultRequestToViewNameTranslator.
Adding the following provided the appropriate HandlerMapping, and thus filled in the missing link. The only downside now is that it's throwing exceptions logging at SEVERE for any requests that can't be satisfied, as opposed to DispatcherServlet's more polite WARNING.
<mvc:view-controller path="/*" />
I'm using Spring MVC with Jackson. It requires <mvc:annotation-driven />. It works with it but it brings other issues. For example, after adding <mvc:annotation-driven />, Locale Change interceptor is not working:
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="localeChangeInterceptor" />
</list>
</property>
</bean>
I don't need it except for Jackson, is it possible to use it without <mvc:annotation-driven />? If so, how?
Thanks
try using mvc namespace to declare your interceptor
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" />
</mvc:interceptors>
In my Spring MVC 3.1 application, I think I can't use "<mvc:annotation-driven />". Why? Because I want to apply an interceptor to all mappings except to the "<mvc:resources" elements. So I can't use :
<mvc:annotation-driven />
<mvc:resources order="-10" mapping="/public/**" location="/public/" />
<mvc:resources order="-10" mapping="/favicon.ico" location="/public/favicon.ico" />
<mvc:resources order="-10" mapping="/robots.txt" location="/public/robots.txt" />
<mvc:resources order="-10" mapping="/humans.txt" location="/public/humans.txt" />
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.my.Interceptor" />
</mvc:interceptor>
</mvc:interceptors>
Because I don't want the interceptor to apply to the resources and there is no way (I think) to specify a path for the mapping which would apply the interceptor to everything except this and that.
So I have to add my own RequestMappingHandlerMapping to be able to specify the interceptor on it, and not globally. Because of this and this, it seems I can't simply define my own RequestMappingHandlerMapping while keeping the <mvc:annotation-driven /> element!
So... With some help, I've been able to get rid of the <mvc:annotation-driven /> element and pretty much everything works well now. I have my interceptor applied on everything but my resources. Everything works well, except the flash scope!
#RequestMapping(value="/test", method=RequestMethod.POST)
public String test(Model model, RedirectAttributes redirectAttrs)
{
redirectAttrs.addFlashAttribute("myKey", "my message");
return "redirect:test2";
}
#RequestMapping(value="/test2")
public String test2(Model model, HttpServletRequest request)
{
Map<String, ?> map = RequestContextUtils.getInputFlashMap(request); // map is NULL
System.out.println(model.containsAttribute("myKey")); // Prints FALSE
}
The flash map is NULL and my model doesn't contain my variable. When I try with the <mvc:annotation-driven /> element it works well! So my question is: what is missing from my context to make the flash scope work?
I also did try to set "org.springframework.web" to a DEBUG logging level, and after the redirect there is nothing logged related to a FlashMap or FlashMapManager. It seems some required bean is definitely missing.
Here are the interesting parts of my context file:
<!-- commented! -->
<!-- <mvc:annotation-driven /> -->
<bean id="baseInterceptor" class="com.my.Interceptor" />
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="order" value="0" />
<property name="interceptors">
<list>
<ref bean="conversionServiceExposingInterceptor" />
<ref bean="baseInterceptor" />
</list>
</property>
</bean>
<bean id="myRequestMappingHandlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer">
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService" />
<property name="validator" ref="validator" />
</bean>
</property>
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.FormHttpMessageConverter" />
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
</list>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean id="conversionServiceExposingInterceptor" class="org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor">
<constructor-arg ref="conversionService" />
</bean>
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:/messages/messages" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean>
<mvc:resources order="-10" mapping="/public/**" location="/public/" />
<mvc:resources order="-10" mapping="/favicon.ico" location="/public/favicon.ico" />
<mvc:resources order="-10" mapping="/robots.txt" location="/public/robots.txt" />
<mvc:resources order="-10" mapping="/humans.txt" location="/public/humans.txt" />
What is missing for the flash scope to work?
UPDATE : See my answer for the solution... Nothing was missing actually. Only the Session was not working correctly and I found a way to make it work.
I finally found what was missing for the flash scope to work!
In the action where I access the flash variables (on the page the redirect leads to), I have to use this:
public String test2(Model model, HttpServletRequest request, HttpSession session)
instead of this :
public String test2(Model model, HttpServletRequest request)
It seems that this makes the Session to work correctly and therefore makes the flash scope to work correctly too! Why? I don't know...
It looks like all you need to do is to register a flash map manager with a bean name of flashMapManager and it should get automatically initialized and used by your Dispatcher Servlet:
<bean name="flashMapManager" class="org.springframework.web.servlet.support.DefaultFlashMapManager/>
My application requires several interface implementations which require a Jetty server to do their job. This is, however, not necessarily the case in every implementations of those interfaces so the Jetty server is only a dependency.
Since it would be a huge amount of pain to wrap the entire jetty server with all its logging, connector and Handler configurations, I want to inject the server to those implementations with Spring. I decided that injecting the Server class is not a good idea because an implementation could stop the server even if its required at another location. Currently I inject empty HandlerList classes to those implementations and they register their handlers to avoid those problems.
The Problem: Those handlers might interfere with other handlers for example: implementation one might register a handler for /foo and implementation two too... problem. Has anyone used Jetty in such an environment? And how could this problem be solved?
My XML to clarify my problem:
<bean id="jetty" class="org.eclipse.jetty.server.Server" destroy-method="stop">
<property name="connectors">
<list>
<bean class="org.eclipse.jetty.server.bio.SocketConnector">
<property name="host" value="10.8.0.46" />
<property name="port" value="9999" />
</bean>
</list>
</property>
<property name="handler">
<bean class="org.eclipse.jetty.server.handler.HandlerCollection">
<property name="handlers">
<list>
<ref bean="jetty.handlerList" />
<bean class="org.eclipse.jetty.server.handler.RequestLogHandler">
<property name="requestLog">
<bean class="org.eclipse.jetty.server.NCSARequestLog">
<constructor-arg value="${jetty.logfile}" />
<property name="extended" value="false"/>
</bean>
</property>
</bean>
</list>
</property>
</bean>
</property>
<property name="sendServerVersion" value="false" />
</bean>
<bean id="jetty.handlerList" class="org.eclipse.jetty.server.handler.HandlerList" />
If I require an empty HandlerList I use something like this where com.example.myapp.util.ioc.CreateHandlerListFactory is a org.springframework.beans.factory.FactoryBean which creates a new HandlerList within the given HandlerList.
<constructor-arg>
<bean class="com.example.myapp.util.ioc.CreateHandlerListFactory">
<constructor-arg ref="jetty.handlerList"/>
</bean>
</constructor-arg>
I have a few possible suggestions:
Add an org.eclipse.jetty.servlet.ServletHandler and instead of Jetty-specific Handlers, map standard Servlets instead. You can set the ServletHandler's Servlets either by adding them one-by-one (each wrapped in a ServletHolder) or with setServlets(ServletHolder[] holders). The ServletMappings are set similarly.
You could inject the ServletHandler to each interface implementation to let them add their mapped servlets, or centrally create arrays of ServletHolders and ServletMappings, thereby also preventing duplicated paths by keeping control of the paths out of each interface implementation. The latter would also allow at least the interface implementations to be programmed against the standard Servlet API, making the bulk of your code independent of Jetty.