I recently upgraded to spring 3.2 and noticed that AnnotationMethodHandlerAdapter had been deprecated in favor of RequestMappingHandlerAdapter. So I reconfigured to use the new class, complete with a custom MessageConverter I need. All fine and good.
However, when attempting to hit a URL supported by an annotated Controller, I'm getting an error:
[java] javax.servlet.ServletException: No adapter for handler [my.company.TagController#1c2e7808]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
[java] at org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1128)
[java] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:903)
[java] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
When debugging the dispatcher, and in particular, the Dispatcher.getHandlerAdapter() method, it's finding my HandlerAdapter, but the AbstractHandlerMethodAdapter.supports() that is invoked wants a MethodHandler:
public final boolean supports(Object handler) {
return handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler);
}
and the controller is not a HandlerMethod. The AnnotatedMethodHandlerAdapter's support method is.. well, different (and works still!)
public boolean supports(Object handler) {
return getMethodResolver(handler).hasHandlerMethods();
}
So I apparently cannot simply upgrade to the new class... I'm missing some additional configuration, but the documentation isn't really helping me out. Any ideas?
Thanks.
Use "<mvc:annotation-driven/>" in the spring configuration file instead of writing your own implementation of WebMvcConfigurationSupport
example
<mvc:annotation-driven/>
<context:component-scan base-package="com.springapp.mvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
<bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
</list>
</property>
</bean>
So as it turns out, simple switching the bean definition doesn't work due to the fact that the RequestMappingHandlerAdapter is depending on a whole host of entities being created and configured. Spring, by default, is using a WebMvcConfigurationSupport entity to do all this default configuration, but simply creating my own bean version doesn't help because spring creates its own.
My approach ended up being something along the lines of below, where I left basically all of the configuration up to spring's default, but then added my own converter. The only drawback is that it's switching xml configuration to javaconfig, but in my case, it's ok. There's an article here that describes something similar.
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter handlerAdapter = super.requestMappingHandlerAdapter();
handlerAdapter.getMessageConverters().add(0, getProtobufJsonMessageConverter());
return handlerAdapter;
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
#Configuration
public class WebConfig extends WebMvcConfigurationSupport {
#Override
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
return new XXXXRequestMappingHandlerAdapter();
}
}
Related
I'm very new to Spring MVC and Java EE at all (I came from PHP+Zend2). My english is poor too. I use NetBeans.
My problem is that my custom converter does not work. Here's some code:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="universalDAO" class="dao.UniversalDAO"/>
<bean id="sessionManager" class="utils.SessionManager"/>
<bean id="idToEntityConverterFactory" class="utils.IdToEntityConverterFactory">
<property name="dao" ref="universalDAO"/>
</bean>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<ref bean="idToEntityConverterFactory" />
<bean id="temp" class="utils.TempConverter" />
</list>
</property>
</bean>
<bean name="universalService" class="service.UniversalService">
<property name="universalDAO" ref="universalDAO"/>
</bean>
<bean name="sessionApplicationService" class="service.SessionApplicationService">
<property name="universalDAO" ref="universalDAO"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<bean name="systemUserApplicationService" class="service.SystemUserApplicationService">
<property name="universalDAO" ref="universalDAO"/>
</bean>
<aop:aspectj-autoproxy />
<bean id="loggerAspect" class="aspect.LoggerAspect"/>
</beans>
I also have tried version with:
class="org.springframework.format.support.FormattingConversionServiceFactoryBean"
IdToEntityConverterFactory is a ConverterFactory created with this tutorial but it is not important now. I wrote simpler one not to do mess.
TempConverter.java
package utils;
import entity.Role;
import org.springframework.core.convert.converter.Converter;
public class TempConverter implements Converter<String, Role> {
#Override
public Role convert(String id) {
return new Role();
}
}
Here is .jsp fragment:
<form:select path="${names[item.index]}" items="${valueOptions[names[item.index]]}" />
When I submit the form there appears an error:
Failed to convert property value of type java.lang.String[] to required type java.util.List for property roleList; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [entity.Role] for property roleList[0]: no matching editors or conversion strategy found
I found solution to similar problem here . It has something to do with
<mvc:annotation-driven>
but I don't use such tag anywhere in my application (should I?).
My question is how to make any converter work while binding form data to Java object.
EDIT:
I figured out some workaround. I have overriden initBinder method in my Controller:
#Override
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
binder.setConversionService(conversionService);
}
conversionService had to be previously set in Controller of course:
private ConversionService conversionService;
//...
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
dispatcher-servlet.xml:
<bean class="controller.SystemUserFormController" p:applicationService-ref="systemUserApplicationService" p:sessionManager-ref="sessionManager" p:conversionService-ref="conversionService" />
It works now but it is kind of inconvenience because:
I have to add extra code p:conversionService-ref="conversionService" in every Controller I need converter to be used.
It works out-of-the-box in every toutorial I found on the internet but not for me. I am just curious what am I doing different.
Kindest regards!
Your questions:
I have to add extra code p:conversionService-ref="conversionService" in every Controller I need converter to be used.
You can use #Autowired to inject ConversionService.
You can implement common parent class for your controllers with #InitBinder
You can use abstract parent bean definition <bean abstract="true" ...>
It works out-of-the-box in every toutorial I found on the internet but not for me. I am just curious what am I doing different.
Just use <mvc:annotation-driven>. This easy-to-use configuration is there so that you don't need to configure stuff manually.
How to do it
You can implement WebBindingInitializer. This bean needs to be set up on handler adapter.
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="yourBindingInitializer" />
</bean>
However this approach is kind of painful if you are not already defining hanlder adapter yourself. When you define this bean it disables some DispatcherServlet's default behavior. So you might need to do a bit more than to define this bean.
Off-topic advice
Problem with Spring is that the internet is full of obsolete tutorials. Please use the official guide and reference app. Start using namespace (or even Java) config, autowiring, #Controller components and #RequestMapping.
I belive there is not such think as mvc:annotation-config. There are 2 other things:
context:annotation-config
mvc:annotation-driven
Please tell me if I am wrong
I have tried both and both doesn't work. Here's what what have I done:
Removed p:conversionService-ref="conversionService" from my Controller bean
Added #Autowired annotation to my setter
#Autowired
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
Added context:annotation-config/ (or mvc:annotation-driven/) to applicationContext.xml
Unfortunately setter has never been executed!
My source is here
Quote: "When Spring finds an #Autowired annotation used with setter methods, it tries to perform byType autowiring on the method."
I also have tried using setter with exactly the same type as bean class - still nothing.
I've started working on a web application in Spring Weblow. The idea is to write as much as possible in Java, rather than XML. So I started off with a JavaConfig file for both the MVC configuration and the Web Flow configuration. But I ran into a problem when needing converters for entering and submitting a form with Spring Web Flow.
I did a lot of research on ConversionService and Converters. I found plenty examples of implementing a custom ConversionService and custom Converters, but I found no examples to to add the ConversionService to the Web Flow configuration in JavaConfig (configuration was always XML).
I did try to reproduce the XML config in Java, which nearly worked. In a form page, a list of POJOs (Employee) was represented as a dropdownlist. The input was List<Employee> and the converter (subclass of StringToObject) worked to represent each Employee as a String. But when submitting the form, I got the error that no converter was found for String to Employee. So basically, the custom converter was found and used when rendering the page, but when submitting the form, the same converter could not be found for the reverse process.
I eventually got it fixed by rolling the JavaConfig back to XML config and adding a custom Formatter to the ConversionService of the MVC config. But I'd like to make this work in JavaConfig if it is at all possible.
I believe the problem is that a ConversionService bean (org.springframework.core.convert package) needs to be added to the MVC config, because this bean needs to be set as a delegate ConversionService in the ConversionService bean to be added to the Web Flow Config (the latter from the org.springframework.binding.convert package). But I don't know how to add this core ConversionService in JavaConfig like in the mvc:annotation-driven tag in the code below.
It all boils down to needing the JavaConfig version of the following code:
<mvc:annotation-driven conversion-service="typeConversionService" ... />
<bean id="typeConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="formatters">
<list>
<bean class="some.package.holidays.formatter.EmployeeFormatter">
<constructor-arg ref="employeeService"/>
</bean>
<bean class="org.springframework.format.datetime.DateFormatter">
<constructor-arg value="dd/MM/yyyy"/>
</bean>
</list>
</property>
</bean>
If anyone would know about JavaConfig for Spring Webflow, especially about adding a ConversionService, please let me know, it would be a great help.
I had the same thing to do in a project and this is how I did it. I know it might be late for you, but maybe somebody else needs the answer to this:
#Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
#Autowired
private MvcConfig webMvcConfig;
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(this.webMvcConfig.validator())
.setConversionService(conversionService())
.setDevelopmentMode(true)
.build();
}
#Bean
DefaultConversionService conversionService() {
return new DefaultConversionService(conversionServiceFactoryBean().getObject());
}
#Bean
FormattingConversionServiceFactoryBean conversionServiceFactoryBean() {
FormattingConversionServiceFactoryBean fcs = new FormattingConversionServiceFactoryBean();
Set<Formatter> fmts = new HashSet<>();
fmts.add(this.webMvcConfig.dateFormatter());
fmts.add(this.webMvcConfig.employeeFormatter());
fcs.setFormatters(fmts);
return fcs;
} }
I upvoted the accepted answer but would also like to add this. I kept getting the below error.
'conversionService': Requested bean is currently in creation: Is there an unresolvable circular reference?
To fix this, remove conversionService bean like this. (note the setConversionService difference).
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder()
.setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(localValidatorFactoryBean)
.setConversionService(new DefaultConversionService(conversionServiceFactoryBean().getObject()))
.setDevelopmentMode(true)
.build();
}
#Bean
FormattingConversionServiceFactoryBean conversionServiceFactoryBean() {
FormattingConversionServiceFactoryBean fcs = new FormattingConversionServiceFactoryBean();
Set<Formatter> fmts = new HashSet<>();
fmts.add(this.webMvcConfig.dateFormatter());
fmts.add(this.webMvcConfig.employeeFormatter());
fcs.setFormatters(fmts);
return fcs;
}
I've inherited a project and am trying to get a set of integration tests running against an in-memory h2 database. In order for them to pass some tables, relationships and reference data needs creating.
I can see the problem in that the script referenced in RUNSCRIPT is being executed multiple times and therefore generating Index "XXX_IDX" already exists errors and other violations. So is there a way to force the script to only be run once or do I need a external database? It seems that the script is run on every connection which I assume is by design.
properties file
my.datasource.url=jdbc:h2:mem:my_db;DB_CLOSE_DELAY=-1;MODE=Oracle;MVCC=TRUE;INIT=RUNSCRIPT FROM 'classpath:/create-tables-and-ref-data.sql'
XML config
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="${my.datasource.url}"/>
<!-- other properties for username, password etc... -->
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="myDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
many Java classes in the following pattern
#Component
public class SomethingDAOImpl implements SomethingDAO {
#Autowired
public SomethingDAOImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
#Component
public class SomethingElseDAOImpl implements SomethingElseDAO {
#Autowired
public SomethingElseDAOImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
With the default bean scope being singleton I thought this would just work, but I guess i'm missing something. Also, if I switch to an real Oracle instance that already has the tables and reference data set-up, the tests all pass.
In many cases, it is possible to write the SQL script so that no exceptions are thrown:
create table if not exists test(id int, name varchar(255));
create index if not exists test_idx on test(name);
I ended up using an alternative approach, as I could not write the SQL in a way that could be reapplied without error.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd">
<jdbc:initialize-database data-source="myDataSource" enabled="true" ignore-failures="ALL">
<jdbc:script location="classpath:create-and-alter-tables-first-then-add-test-data.sql" />
</jdbc:initialize-database>
</beans>
which is executed once at context initialization.
Note: other namespaces and beans omitted for brevity.
I have a HandlerInterceptor to add some "global" model variables. It works.
Now, I try to reuse it in Spring Web Flow, for the same reason.
But HandlerInterceptors have the ModelAndView parameter set to NULL under Spring Web Flow (couldn't figure why, but it's a fact).
I have referenced my interceptor in the FlowHandlerMapping bean :
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
<property name="order" value="0" />
<property name="flowRegistry" ref="flowRegistry" />
<property name="interceptors">
<list>
<ref bean="myInterceptor" />
</list>
</property>
</bean>
How can I add variables to the model ?
Is there a workaround, with the request parameter for example ?
Starting with Spring Webflow 2, the ModelAndView object is not generated anymore (see this post (and thread) at the SpringSource forum).
The FlowHandlerAdapter handle() function does not generate a ModedAndView anymore (it just returns null) even if this function is :
public ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object handler)
So overriding this function is pointless, but this function creates a ServletExternalContext object, which holds all the flow variable, by calling its method :
protected ServletExternalContext createServletExternalContext(
HttpServletRequest request, HttpServletResponse response)
By overriding this function you can pretty much do what you want with this flow variables.
To do this, just create a class that extends FlowHandlerAdapter, register it instead of FlowHandlerAdapter and override the createServletExternalContext function.
Basically you use ServletExternalContext.getSessionMap() to access a SharedAttributeMap and register your properties.
As you have access to the HttpServletRequest and HttpServletResponse objects, this method can act petty much like a HandlerInterceptorAdapter.postHandle function.
See an example below.
I left out how to use generic way to reuse the same code for a HandlerInterceptor for the MVC and this object but it's easy to code, by implementing HandlerInterceptor.
MyFlowHandlerAdapter :
package my.package;
public class MyFlowHandlerAdapter extends FlowHandlerAdapter {
#Override
protected ServletExternalContext createServletExternalContext(
HttpServletRequest request,
HttpServletResponse response) {
ServletExternalContext context =
super.createServletExternalContext(request,response);
context.getSessionMap().put("myproperty", "myvalue");
return context;
}
}
You have the FlowHandlerAdapter object defined in you webflow-context.xml file like that :
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
Just replace it with :
<bean class="my.package.MyFlowHandlerAdapter">
<property name="flowExecutor" ref="flowExecutor"/>
</bean>
ModelAndView can be null when the interceptor is processing an Ajax request.
Just check if ModelAndView is null. If not, that's because the interceptor is processing a view-model, so you can add your variables at this time.
I have a simple class like this,
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;
public class Form implements Serializable {
#NotNull
#Length(min = 2, max = 20)
private String lastName;
}
I have messages.properties file in the classpath. It's then loaded via Spring bean as follows,
<bean name="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource">
<ref bean="resourceBundleLocator"/>
</property>
</bean>
<bean name="resourceBundleLocator" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath*:messages.properties</value>
</list>
</property>
</bean>
All I want is to get error messages customized according to the bean validated. In other words I want to have each ConstraintViolation object return the error message I have defined in my property file instead of the default one. Is it possible to add message property with a value like this format {xxx.yyyy.zzzz} and refer that message from the messages.properties file ?
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Form>> inputErrors = validator.validate(form);
If I want to customize the default message for #NotNull and #Length, how can I do that?
My experience on Hibernate validation is zero, Any sample code or step by step guide would be appreciated.
You have this resource bundle file holding the message values.
I think you have to use the path to the i.e. #NotNull annotation
to override the message by hand!
like javax.validation.constraints.NotNull=Notnull error happened!
or something like that!
Look here for that :
Hibernate Validator - The error message
Hope that helps