SpringMVC 3.0 to 3.1 migration of ControllerClassNameHandlerMapping - spring

I have an existing application in Sping 3.0 that uses ControllerClassNameHandlerMapping to map Controller and methods such as:
StartController.class is mapped to http://127.0.0.1/app/start/*
then
StartController.class has a method called init() that is mapped to http://127.0.0.1/app/start/init.html
Here is my configuration:
#Bean
public ControllerClassNameHandlerMapping classNameControllerMappings() {
return new ControllerClassNameHandlerMapping() {{
setCaseSensitive(true);
setDefaultHandler(new UrlFilenameViewController());
setInterceptors(new Object[]
{callProgressionInterceptorHandler(),
callSessionInterceptorHandler(),
localeChangeInterceptor()});
}};
}
Most of my controllers have 5-15 Request Mapped methods in each controller.
But when I upgrade to Spring 3.1+, the Request Mapping becomes ambiguous for each controller and is not mapped correctly.
I have read that one solution is to explicitely add the mthod name:
#RequestMapping(method = RequestMethod.GET)
Will now be:
#RequestMapping(method = RequestMethod.GET, value = "init")
I really do not want to manually add #RequestMapping value to 100+ methods if I dont have to.
Can anyone help with a better solution?
Here is the error I keep getting:
47672 [btpool0-1] ERROR org.springframework.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'addressConfirmationController' bean method
public void com.comcast.ivr.d2.web.controllers.AddressConfirmationController.houseNumber_rc(org.springframework.ui.ModelMap)
to {[],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}: There is already 'addressConfirmationController' bean method
I also added setOrder(1); to ControllerClassNameHandlerMapping and still get this error.
UPDATE:
I saw on http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/mvc.html the following excerpt:
Prior to Spring 3.1, type and method-level request mappings were examined in two separate stages -- a controller was selected first by the DefaultAnnotationHandlerMapping and the actual method to invoke was narrowed down second by the AnnotationMethodHandlerAdapter.
With the new support classes in Spring 3.1, the RequestMappingHandlerMapping is the only place where a decision is made about which method should process the request. Think of controller methods as a collection of unique endpoints with mappings for each method derived from type and method-level #RequestMapping information.
Does this mean I cannot keep the same #RequestMapping without adding mapping details in the #RequestMapping as I did <3.1 ?
I have hundreds of methods that would need to be modified in order for this to happen... :-(

Related

Spring Autowiring error: java.lang.IllegalStateException: Method [name] can only contain 1 method field. Found: [PUT, POST]

I have a method in one of my webservices that accepts both PUT and POST. This is because we started using PUT but later we needed to support POST too (for a new service).
#RequestMapping(
value = "/endpointURL",
method = {RequestMethod.PUT, RequestMethod.POST})
I am trying to create a test application that calls this method, but Spring throws an Autowiring error during startup with the following error:
java.lang.IllegalStateException: Method [name] can only contain 1
method field. Found: [PUT, POST]
Both the Spring and Feign versions are the same in both applications (webservice with this endpoint, and testing application).
Any ideas on how to fix it please?
Thank you!
Method supports various HTTP method as below. Could you post your class source code. I think you should have another problem. Maybe duplicated path or else.
#RequestMapping("/v1/echo")
#RestController
public class EchoApi {
#RequestMapping(value = "/", method = { RequestMethod.PUT, RequestMethod.POST })
public ResponseEntity<String> echo(#RequestBody String body){
System.err.println(body);
return new ResponseEntity<String>(body, HttpStatus.OK);
}
}
In the end it was due to the Feign version we were using. It's fixed after version 10. Will close this topic. Thanks!

Spring Controller fetch query parameter for a wrapper request class

I am trying to build RESTful web service by using spring 4.0
I have a controller:
#Controller
#RequestMapping("test")
public class Controller{
#RequestMapping("fetch",method=RequestMethod.GET)
#ResponseStatus(value=HttpStatus.OK)
#ResponseBody
public ResultResponse fetchController(ResultRequest req){
if((req.getName).equals("John"))
return new ResultResponse(100);
else
return new ResultResponse(0);
}
}
and my ResultRequest.class
public class ResultRequest{
private String name;
//getter,setter
}
If I hit the url to //host//contextPath/test/fetch?name=John
the controller will return the object ResultResponse(100)
my question is, there no #RequestParam or other annotation in the request parameter,
how does the spring controller know to set the query parameter "name" as the property of wrapper class
ResultRequest ?
Thanks
Spring uses implementations of an interface called HandlerMethodArgumentResolver for resolving arguments to pass to handler methods, ie. methods annotated with #RequestMapping.
One of these is a ModelAttributeMethodProcessor. Its javadoc states
Resolves method arguments annotated with #ModelAttribute and handles
return values from methods annotated with #ModelAttribute.
Model attributes are obtained from the model or if not found possibly
created with a default constructor if it is available. Once created,
the attributed is populated with request data via data binding and
also validation may be applied if the argument is annotated with
#javax.validation.Valid.
When this handler is created with annotationNotRequired=true, any
non-simple type argument and return value is regarded as a model
attribute with or without the presence of an #ModelAttribute.
Spring registers two objects of this type. One to handle parameters annotated with #ModelAttribute and one to handle ones without.
Further reading:
Form submit in Spring MVC 3 - explanation
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments

Spring #Transactional value param with SpEL (Spring expression language)

In one of my service classes I have some methods annotated as such :
#Transactional(value="foodb")
public Bar getMeSomething(){
}
I recently learned about #Value with the power of Spring EL to get some values stored in a properties file.
such as
#Value("${my.db.name}")
which works like a charm.
Now I'm trying to do the same with
#Transactional(value="${my.db.name}")
with no success ...
I get the following exception :
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named '${my.db.name}' is defined: No matching PlatformTransactionManager bean found for qualifier '${my.db.name}' - neither qualifier match nor bean name match!
Is what I am trying to do even supported by Spring ?
What can I do to get the my.db.name value inside that #Transactional annotation
Thanks
Nope, it's not supported.
Here's an excerpt from org.springframework.transaction.annotation.SpringTransactionAnnotationParser
public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
rbta.setPropagationBehavior(ann.propagation().value());
rbta.setIsolationLevel(ann.isolation().value());
rbta.setTimeout(ann.timeout());
rbta.setReadOnly(ann.readOnly());
rbta.setQualifier(ann.value()); // <<--- this is where the magic would be
// if it was there, but it isn't

Spring tries to instantiate the interface when injecting a parameter

I hope you can help me with this.
I'm using Spring MVC (3.1.1) in my web application, and am facing a strange situation.
I have this simple #Controller, that makes use of the ServicioUsuario Service, and works fine:
#Controller
#RequestMapping("/ajax")
public class ControladorAjax extends ControladorGenerico {
#Autowired
ServicioUsuario servicioUsuario;
#RequestMapping("/check")
public ResponseEntity<String> check(#RequestParam String email) {
// Declarations and other operations omitted...
// Use servicioUsuario
servicioUsuario.doStuff();
return response;
}
}
However, if I remove the #Autowiring, and try to make Spring inject servicioUsuario as a parameter (i.e. by changing the method signature to: public ResponseEntity<String> check(#RequestParam String email, ServicioUsuario servicioUsuario)) the whole thing breaks, and I get this sort of exceptions in Tomcat's log:
javax.servlet.ServletException: NestedServletException in java.lang.Thread.getStackTrace:: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.package.ServicioUsuario]: Specified class is an interface
I have these interfaces:
com.package
|-> Servicio.java (interface)
|-> ServicioUsuario.java (interface that extends Servicio)
and these clases:
com.package.impl
|-> ServicioImpl.java (implements Servicio)
|-> ServicioUsuarioImpl.java (#Service("servicioUsuario") that extends ServicioImpl implements ServicioUsuario)
and configured Spring to scan both packages with:
<context:component-scan base-package="com.package
com.package.impl" />
Why is Spring trying to instantiate the interface and not the implementing class? Is it something I'm doing wrong?
According to 16.3.3.1 Supported method argument types of the official documentation, this is a complete list of what controller methods can take:
Request or response objects (Servlet API). [...] for example ServletRequest or HttpServletRequest.
Session object (Servlet API): of type HttpSession.
org.springframework.web.context.request.WebRequest or org.springframework.web.context.request.NativeWebRequest.
java.util.Locale for the current request locale [...]
java.io.InputStream / java.io.Reader for access to the request's content. [...]
java.io.OutputStream / java.io.Writer for generating the response's content. [...]
java.security.Principal containing the currently authenticated user.
#PathVariable annotated parameters for access to URI template variables. [...]
#RequestParam annotated parameters for access to specific Servlet request parameters. [...]
#RequestHeader annotated parameters for access to specific Servlet request HTTP headers. [...]
#RequestBody annotated parameters for access to the HTTP request body. [...]
#RequestPart annotated parameters for access to the content of a "multipart/form-data" request part. [...]
HttpEntity<?> parameters for access to the Servlet request HTTP headers and contents. [...]
java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap [...]
org.springframework.web.servlet.mvc.support.RedirectAttributes [...]
Command or form objects to bind request parameters to bean properties (via setters) or directly to fields, [...]
org.springframework.validation.Errors / org.springframework.validation.BindingResult [...]
org.springframework.web.bind.support.SessionStatus [...]
org.springframework.web.util.UriComponentsBuilder [...]
As you can see, Spring beans are not on this (quite impressive) list. Why would you inject services via controller method? They never change. It's enough to inject them once and assign them to a field.

Spring #RequestMapping consumes charset?

I'm trying to use #RequestMapping with the consumes-element. Reading the API-document it works on the Content-Type-header of the request. However, using
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-8", value = "/test")
public void test() {
:
}
or
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=ISO-8859-1", value = "/test")
public void test() {
:
}
doesn't make a difference. The header in the request can look like
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
or
Content-Type: application/x-www-form-urlencoded
test() will be called in all four possible constellations.
However, and this is proof to me that Spring sees and tries to use the charset-part, if I specify
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-x8", value = "/test")
public void test() {
:
}
I get an exception during startup (!) of the web-app:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Initialization of bean failed;
nested exception is java.nio.charset.UnsupportedCharsetException: UTF-x8
Note that the documentation on the produces-element also doesn't mention the use of charset, but according to Google some use it.
Any clues to what's happening here or what I'm doing wrong?
BTW, this is Spring 3.1.1.RELEASE.
I think you have already answered your question, so this is more of a confirmation, from a code point of view, as to why the charset is not taken into account when resolving mappings.
When digging into Spring code, the culprit seems to be the MediaType#includes(). method
More digging reveals that a RequestMappingInfo is created in association to the RequestMapping annotation of the method. This RequestMappingInfo stores a series of AbstractRequestCondition objects, one of them being the ConsumesRequestCondition which holds the MediaType defined in the consumes part of the annotation (i.e. application/x-www-form-urlencoded;charset=UTF-8).
Later when a request is made, this ConsumesRequestCondition has an inner ConsumeMediaTypeExpression class with a matchMediaType() method that extracts the MediaType of the HttpServletRequest and checks it against it's own MediaType to see if it's included.
If you look at the MediaType#includes() implementation (Lines 426 to 428), it returns true when type (i.e. application) and subtype (i.e. x-www-form-urlencoded) are equal, completely disregarding the parameters Map which in this case
holds the remnant "charset","UTF-8" combination.
Digging into the produces track seems to show similar results, but in this case it's the MediaType#isCompatibleWith() method involved, and again, it reaches only to type and subtype if they are equal.
If you found evidence on Google of the produces working for charset request mapping, I would doubt it (unless they changed core Spring stuff)
As to why it was designed this way, well that's another question :)

Resources