PDA

View Full Version : mvc:interceptors behavior change between 3.1.0.M1 and 3.1.0.M2



eric.toom
Jun 13th, 2011, 07:13 AM
Hi.

I debated the correct forum for this posting, and since it was after attempting to move from 3.1.0.M1 to M2 that exposed this behavior I decided to post here. Upon upgrading an internal application under development to M2, I noticed that I had started having conflicts with the OpenSessionInViewInterceptor that I defined in a mvc:interceptors section between the HibernateFlowExecutionListener defined in my flows context. Prior to this release in 3.0.x and 3.1.0.M1 I was able to use the following configurations successfully.

In application-mvc.xml


<import resource="application-flows.xml" />

<mvc:annotation-driven conversion-service="mvcConversionService" />

<mvc:view-controller path="/accessDenied.htm" view-name="accessDenied" />

<mvc:resources location="classpath:/assets/" mapping="/assets/**" cache-period="31556926" />

<mvc:interceptors>
<bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSes sionInViewInterceptor">
<property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
</mvc:interceptors>

In application-flows.xml


<bean id="flowHandlerMapping" class="org.springframework.webflow.mvc.servlet.FlowHandle rMapping">
<property name="flowRegistry" ref="flowRegistry"/>
<property name="order" value="1"/>
</bean>

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-listeners>
<webflow:listener ref="securityFlowExecutionListener" />
<webflow:listener ref="hibernateFlowExecutionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>

<bean id="securityFlowExecutionListener"
class="org.springframework.webflow.security.SecurityFlowE xecutionListener" />

<bean id="hibernateFlowExecutionListener" class="org.springframework.webflow.persistence.HibernateF lowExecutionListener">
<constructor-arg ref="sessionFactory" />
<constructor-arg ref="transactionManager" />
</bean>

Now that I have the OSIVL and HFEL classes both attempting to bind sessions, it's obviously failing. Is this something that was "fixed" in this release that was "broke" in the previous 3.x branches (assuming that would be mvc:interceptors not wrapping around non-annotation based handler mappings or from imported contexts)?

In the 2.5 days when I spelled out the annotation handlers by hand, I solved this issue by only applying the OSIVL to the controller based handler mappings directly, is this something I can do while taking advantage of the mvc:annotation-driven tag? Or how should I best configure my application to take advantage of OSIVL and the capability to use persistent-context in my web flows?

Rossen Stoyanchev
Jun 13th, 2011, 08:31 AM
Now that I have the OSIVL and HFEL classes both attempting to bind sessions, it's obviously failing. Is this something that was "fixed" in this release that was "broke" in the previous 3.x branches (assuming that would be mvc:interceptors not wrapping around non-annotation based handler mappings or from imported contexts)?

Indeed that is the case. A mapped interceptor declared in the context is to be detected by all handler mappings. This wasn't the case before Spring 3.1 M2 (SPR-8352 (https://jira.springsource.org/browse/SPR-8352)). As a result now the FlowHandlerMapping detects it as well.


In the 2.5 days when I spelled out the annotation handlers by hand, I solved this issue by only applying the OSIVL to the controller based handler mappings directly, is this something I can do while taking advantage of the mvc:annotation-driven tag?

If you could, specify the paths the interceptor applies to:


<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/logged/**" />
<mvc:mapping path="/foo/logged" />
<ref bean="log4jInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>


That might be inconvenient though depending on your URLs. A better alternative would be to switch to the code-based configuration available in M2 via @EnableWebMvc. See my post (http://blog.springsource.com/2011/06/13/spring-3-1-m2-spring-mvc-enhancements-2) on that. Unlike the MVC namespace, the code-based configuration does not create MappedInterceptor beans. Instead it uses the setInterceptors(..) property on the HandlerMappings it creates.

eric.toom
Jun 14th, 2011, 07:52 AM
Thank you for the quick response yesterday Rossen. I spent several hours attempting to get my internal project running with the @EnableWebMvc, and while it worked to get the OSIV interceptor to not fire on my flow requests, I couldn't get the normal @MVC controllers to work properly. I ended up getting to a point where it was telling me "Name for argument type [java.lang.Long] not available, and parameter name information not found in class file either.". I stashed all of the java based configuration in a branch and ended up writing a simple wrapper around the OSIV interceptor with a HandlerInterceptor to determine the handler type to determine if I should delegate this request into the OSIV or not in order to get back on track with other development tasks.

When I get some more time, I plan on going back through the documentation, blogs, and the Greenhouse application to get one of my apps set up with Java configurations.

Rossen Stoyanchev
Jun 14th, 2011, 09:04 AM
"Name for argument type [java.lang.Long] not available, and parameter name information not found in class file either.".

It sounds like it was trying to figure out the default name of a controller method argument (@PathVariable, @RequestMapping, etc) and it couldn't get it from the variable name. Either the application is compiled without debug symbols or it could be a bug. Assuming it's not the former, if you could, provide more details on what the controller signature looks like.

eric.toom
Jun 15th, 2011, 11:12 AM
I assume I have misconfigured the java based setup since the same code / environment setup works with mvc:annotation-driven and does not work in the java based configurations. Here is the method signature of the controller method I was calling.



@RequestMapping("/view.htm")
public void viewProductDetails(@RequestParam("productId") Long productId,
@RequestParam(value = "suppressActions", required = false) Boolean suppressActions,
@RequestParam(value = "flowExecutionUrl", required = false) String flowExecutionUrl,
Model model) {


In doing some debugging between my branch that has the java configurations I noticed that in RequestParamMethodArgumentResolver.createNamedValu eInfo that on line 94 RequestParam annotation = parameter.getParameterAnnotation(RequestParam.clas s); is returning null.

When I am running in the mvc:annotation driven branch, it returns the RequestParam annotation which contains the missing information. I have included the toString on that param below.


@org.springframework.web.bind.annotation.RequestPa ram(value=productId, required=true, defaultValue=


???

)

As for the compiling with debug flags, I have not done anything specific to turn that on or off in any of my projects. I am using myeclipse with its server integration to Tomcat 7, which has several options turned on to generate information for debugging.

Rossen Stoyanchev
Jun 15th, 2011, 12:34 PM
In doing some debugging between my branch that has the java configurations I noticed that in RequestParamMethodArgumentResolver.createNamedValu eInfo that on line 94 RequestParam annotation = parameter.getParameterAnnotation(RequestParam.clas s); is returning null.

When I am running in the mvc:annotation driven branch, it returns the RequestParam annotation which contains the missing information.


It seems that it's not detecting the @RequestParam annotation. I wonder if there is a proxy on that class in one case but not in the other. This could be checked on line 94 in RequestParamMethodArgumentResolver by printing out the content of:
parameter.getMethod().toGenericString() (side note: I have left a note to have this debug info added: https://jira.springsource.org/browse/SPR-8447)



As for the compiling with debug flags, I have not done anything specific to turn that on or off in any of my projects. I am using myeclipse with its server integration to Tomcat 7, which has several options turned on to generate information for debugging.

I mentioned debug flags because if the @RequestParam did not contain a value, it should fall back on the argument name. It sounds like it couldn't get that either, which is strange. The first problem however is that it's not detecting the @RequestParam.

eric.toom
Jun 15th, 2011, 04:40 PM
Your assumption is a good one.




java configuration
public final void com.arag.product.web.ProductController$$EnhancerBy CGLIB$$4c3acd1c.viewProductDetails(java.lang.Long, java.lang.Boolean,java.lang.String,org.springframe work.ui.ModelMap)

mvc:annotation-driven
public void com.arag.product.web.ProductController.viewProduct Details(java.lang.Long,java.lang.Boolean,java.lang .String,org.springframework.ui.ModelMap)

Rossen Stoyanchev
Jun 15th, 2011, 04:48 PM
Eric, would you mind opening a ticket in JIRA? If you can add a snippet or mention how the controller is declared in the code-based config (i.e. @ComponentScan, @Bean method, etc.) Thanks for investigating this!

eric.toom
Jun 16th, 2011, 11:45 AM
I have spent some time trying to create a simple project that would expose the issue I was having. In doing so, I finally figured out what was causing me the issues. Basically, during a prototyping session the controller under question was doing some direct manipulation of the session factory, and a @Transactional annotation was added to the controller.

In the mvc:annotation-driven branch, I use two main contexts, one that is loaded through the context loader listener and scans non-controller components and a context that is associated to the dispatcher servlet that scans for controller components so I imagine the transaction annotation didn't conflict with it. In looking at the logs during initialization this branch uses the actual class and not a proxy when mapping the url to the method.

In the java configuration branch, I based the configurations off of the Greenhouse sample application, which scanned all of the components (controller and non-controller) in the same application context that was associated with the context loader listener. The dispatcher servlet was given an empty context. In this branch, the transaction annotation caused the initialization to map the url to the proxy created by the transaction annotation scanning (I would presume). I would assume that if I had created two contexts as I did in the xml configuration it would work fine. I have also confirmed that removing the @Transactional from the controller fixed the issues I was having.

At this point I have not created a JIRA entry for this because I assumed I would get some combination of 1) why would you want to do this or 2) create two contexts if you insist on doing this.

If you would still like me to create a ticket, I would be happy to, and I have a small sample application that shows the problem for troubleshooting purposes.

Rossen Stoyanchev
Jun 17th, 2011, 07:17 AM
Thanks for the detailed investigation! Yes, log the issue please.

eric.toom
Jun 17th, 2011, 09:05 AM
https://jira.springsource.org/browse/SPR-8464