4

We have encountered a problem using Spring Portlet MVC 3.1 when using multiple controller classes and the DefaultAnnotationHandlerMapping.

Background

  • We are using Spring Portlet MVC 3.1 with annotations for the Render & Action phases
  • We are using JBoss EPP 5.1.1

Issue

  • For a Portlet render request with params, an incorrect page is rendered in the portlet

Cause

  • Spring Portlet MVC is using a different method for @RenderMapping than the expected method with the correct annotations

Technical Analysis

  1. All our controllers contain @RenderMapping and @ActionMapping annotations, and all have “params” arguments to ensure that the expected method is invoked based on a parameter set in our portlet URLs. For default rendering, we have a method that has a @RenderMapping annotation with no “params” argument, which we use to render a blank JSP when the request contains no parameters.

  2. Based on the reading of Chapter 7 and 8 in your book, we learnt that the Dispatcher Portlet tries to get the appropriate handler mapping for the incoming request and send it to the appropriate method in the controller bean configured. Our assumption was that our default @RenderMapping annotation (with no params) would only be invoked after it has checked that there are no other methods in the Controllers with an annotation that matches the specific request parameters.

  3. However, we have debugged to realise that this assumption is incorrect. The DefaultAnnotationHandlerMapping appears to traverse through the available list of annotations in the Controller beans in some pre-defined order. This means that if the controller bean with the default @RenderMapping annotation (with no params)appears before in the list, the method with the default @RenderMapping annotation (with no params) will be invoked rather than the correct which is further down the list.

Manifested Error

We are developing in a Windows environment and deploying to a Linux environment. In Windows we see that the handler cycles through the controller beans in alphabetical order, so we initially solved our problem by adding the @RenderMapping annotated method with no params in the controller with the bean name closest to ‘Z’.

In Linux, however, it appears that the controller beans are detected in a different order. I have attached the Spring logs below to highlight the issue. The no params @RenderMapping annotation is in the YourDetailsController, and as you can see in the Windows log it appears last in the list, whereas in Linux it doesn’t. This means that if we try to access one of the controllers that appears after the YourDetailsController in the list we instead always end up hitting the no params annotation in the YourDetailsController instead.

Questions

  1. Is our assumption incorrect?
  2. Does our diagnosis reflect expected behaviour? Or is it a bug with Spring Portlet MVC?
  3. Is there a different way to get the annotations scanned to form the handlermapping bean list?
  4. Would using xml configuration (instead of annotations) remove our problem?
  5. Would we able to define multiple handler mapping and order so that the default handler mapping is the last handler mapping used by the dispatcher portlet?

Any thoughts or advice you have on this problem would be greatly appreciated.

Mark Chorley
  • 2,087
  • 2
  • 22
  • 29
user1372549
  • 41
  • 1
  • 2

5 Answers5

1

Mike. I'm experiencing the exact same problem. I'm using JDK 7, Spring 3.1.1.RELEASE and Hibernate 4.1.3.Final. I'm developing on Linux (Fedora) and deploying on Linux (Fedora and SL).

I was stuck because I was sure the pieces (controllers) were working one at a time but together the call to a render request was randomly ignored. Sometimes changing something would make things work again on a render request but they never worked all together.

As Walter suggested, when I isolated the controller containing only the default render request in its own package, left only the default render request in it (before I had the delete/view requests) and separated the scan of controllers in the portlet's XML configuration in two with the scanning of the default controller after the others, suddenly everything works like a charm.

It would be interesting to see if this bug is in the Spring tracker...

Andrea
  • 401
  • 3
  • 8
  • @craig-edwards, Craig, thanks for your analysis. In my case it wasn't working in Tomcat. But, as I said, the solution of separating the default action works fine. I must add that I found another apparently weird behavior (before a deeper analysis): if you use \@RequestMapping instead of \@RenderMapping and \@ActionMapping in all methods of the controller with the default action, the problem seems to disappear. You can use request and action mapping annotations in all other controllers. – Andrea Aug 02 '12 at 15:30
1

I'd been bitten by this problem recently, so thought I'd add some additional information based on what I found.

In my case, my default controller (with empty @Controller and @ActionMapping annotations) was always getting invoked, even though there were more specifically annotated controllers/actions (such as @Controller(XXXX) or @ActionMapping(YYYY)). What made my case weirder was that it worked OK in Tomcat/Pluto, but not in WAS/WebSphere Portal Server.

As it turns out, there is a bug introduced in 3.1.x of Spring that means the annotation handlers aren't sorted properly. See https://jira.springsource.org/browse/SPR-9303 and https://jira.springsource.org/browse/SPR-9605. Apparently, this is fixed in 3.1.3.

The big mystery to me was why it was working in Tomcat but not WebSphere? The underlying cause is that Pluto (2.0.3) uses Sun JRE 1.6.0 whereas WebSphere uses IBM JRE 1.5.0. The two JREs have a different implementation of Collections.sort() that results in a different output order when ordering array elements that are reporting they are equal (that is, the result of the compareTo() function). Because of the above Spring bug (which reports some handlers as being equal when it shouldn't) it means that the ordering of the handlers was non-deterministic across the two JREs.

So, in my case, the IBM JRE just happened to put the default controller as the very first element, and so it would be picked up every time. One way that we can affect the ordering of "equal" handlers (where "equal" is a dodgy definition due to the Spring bug) is to change the order that they are found by Spring - which affects the order of the input into the sort routine. That is why, per the above posts, moving the controller from the component scan to being explicitly listed in the XML config works. In my case, it was sufficient to make my default controller's package the last entry in my component scan. I didn't need to move it to the XML config.

Anyway, hope this helps shed a little more light on what is happening.

Craig Edwards
  • 2,118
  • 1
  • 18
  • 23
0

Response received from Ashish Sarin:

Hi Mike,

Though I haven't tested the exact same scenario that you are following in your project, but I can say that it doesn't look like the right approach to define your controllers. If your controllers only make use of @RenderMapping and @ActionMapping annotations, then it might be difficult for the developers to find out the exact controller responsible for handling an incoming portlet request. I would recommend that you make use of @RequestMapping at the type-level to map portlet request to a particular controller, and use request parameters to further narrow down the request to a particular method in the controller.

Let me know if you still face any issue with this approach.

user1372549
  • 41
  • 1
  • 2
0

Mike, Your description is exactly the same issue we are running into. In Windows, we implemented the same workaround (prefixed the controller with the default rendering with a Z) and that solved it. That same code in a Linux environment has the same issues as yours. It looked like it was a times stamp issue ordering as the methods that weren't getting picked, but no luck going that route.

I assumed this was a spring bug.

I think the approach here is ok - we want different controllers handling different functions, but we want a default controller.

I just found one workaround for now. I moved the controller with the default rendering method to a different package so it is not included in the component-scan.

I add that controller manually (in the portletname-portlet.xml file) after the component-scan line, so it adds that as the last controller.

Walter
  • 1
  • 1
  • Hi Walter, it's good to know we're not alone! The order in which Spring cycles through the controller classes to find a matching annotation is the cause of the issue - whether it is a bug or whether we have misinterpreted how it should work is open to debate. We had expected that Spring would find the annotated method that most closely matches the request - in reality it uses the first match it finds (which in our case and yours is different in Windows and Linux). Your suggestion of putting the default controller in a separate package seems like a good one. – user1372549 May 09 '12 at 08:03
0

We use context:component-scan (in nnn-portlet.xml) to divide controllers default render mappings between portlet.