Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@RequestMapping with "params" on same URL in different classes cause "IllegalStateException: Cannot map handler" in JUnit with SpringJUnit4ClassRunner

I perform refactoring and split controller into 2 controllers with:

@RequestMapping(value = "/graph.htm", method = RequestMethod.POST, params="first")

in first controller and:

@RequestMapping(value = "/graph.htm", method = RequestMethod.POST, params="second")

in second controller so these annotations lie in different files. When I build and use project all is fine (I put input HTML tag in my forms with different names: first and second).

But when I try to run JUnit controller test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-context.xml" })

I get trace:

Caused by: java.lang.IllegalStateException: Cannot map handler 'firstController'
  to URL path [/graph.htm]: There is already handler
  of type [class com.web.controller.SecondController] mapped.
    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:294)
    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.registerHandler(AbstractUrlHandlerMapping.java:266)
    at org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping.detectHandlers(AbstractDetectingUrlHandlerMapping.java:82)
    at org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping.initApplicationContext(AbstractDetectingUrlHandlerMapping.java:58)
    at org.springframework.context.support.ApplicationObjectSupport.initApplicationContext(ApplicationObjectSupport.java:119)
    at org.springframework.web.context.support.WebApplicationObjectSupport.initApplicationContext(WebApplicationObjectSupport.java:72)
    at org.springframework.context.support.ApplicationObjectSupport.setApplicationContext(ApplicationObjectSupport.java:73)
    at org.springframework.context.support.ApplicationContextAwareProcessor.invokeAwareInterfaces(ApplicationContextAwareProcessor.java:117)
    at org.springframework.context.support.ApplicationContextAwareProcessor.postProcessBeforeInitialization(ApplicationContextAwareProcessor.java:92)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:399)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1479)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524)

When I comment out this:

@RequestMapping(value = "/graph.htm", method = RequestMethod.POST, params="second")

in second controller individual test for first controller successfully completed.

To resolve this issue I may use different URLs (value in @RequestMapping) but I don't understand why request mapping resolved for params in my production build of application and fail with SpringJUnit4ClassRunner.

Any help welcome!

PS. I use Spring 3.2.

PPS. I found mostly same issue Can I have the same mapping value with different param in a different Spring controller? but according to answers my production build also must fail?! But I run production build successfully!!

Also refer to:

  • @RequestMapping with 2 params in render method
  • Spring MVC 3: same @RequestMapping in different controllers, with centralised XML URL mapping (hybrid xml/annotations approach)

*PPS.

I check official docs for 3.2:

http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#params%28%29

In a Servlet environment, parameter mappings are considered as restrictions
that are enforced at the type level. The primary path mapping (i.e. the
specified URI value) still has to uniquely identify the target handler, with
parameter mappings simply expressing preconditions for invoking the handler.

So seems I perform illegal coding practice...

like image 780
gavenkoa Avatar asked Jan 28 '13 12:01

gavenkoa


2 Answers

This is what I understand when reading the official doc quoted in your question :

In a Servlet environment, parameter mappings are considered as restrictions that are enforced at the type level. The primary path mapping (i.e. the specified URI value) still has to uniquely identify the target handler within the class, with parameter mappings simply expressing preconditions for invoking the handler.

I added the words "within the class".

And please note the enforced at type level. As I understand, it means that : in a servlet env. declaring the params at method level is quite the same as declaring the params at type level (at least if you only have only one method in your class).

Finally, if you take care to this sentence (same source):

When used at the type level, all method-level mappings inherit this parameter restriction (i.e. the type-level restriction gets checked before the handler method is even resolved).

I think all of this summarize why your are not doing illegal coding.

Regarding unit tests:

What is also important here are the words "In a Servlet environment. Obviously, when running unit tests : your not in a Servlet environment and that's probably why it is failing.

like image 164
ben75 Avatar answered Nov 15 '22 21:11

ben75


My neighbour colleague help me with debugging issue.

We compare production and test environment and found difference in context XML configuration.

Previous test configuration which fail:

    <bean name="handlerMapping"
          class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
    <bean name="handlerAdapter"
          class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

New and working test context configuration:

    <bean name="handlerMapping"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <bean name="handlerAdapter"
          class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

Different spring classes use different mapping schema. Old uses per classes, newer uses per methods!!

like image 21
gavenkoa Avatar answered Nov 15 '22 22:11

gavenkoa