Hi,
I ran into the same issue with Spring 3.0.M4 and debugged it to see what is happening.
I cannot image that what I found is intended to be.
Image the situation having 2 Portlets (A and B) and 3 controllers:
1. Controller will trigger the event (Portlet A):
Code:
@Controller
@RequestMapping("VIEW")
public class TriggerController {
@RequestMapping
protected void action(ActionRequest request, ActionResponse response) {
response.setEvent("searchCommand", searchCommand);
}
}
2. Controller will handle the event and the default render request (Portlet B):
Code:
@Controller
@RequestMapping("VIEW")
public class EventController {
@EventMapping("searchCommand")
public void doSearch(EventRequest request) {
//do something with the event
}
@RequestMapping
public String renderDefault(RenderRequest request, Model model) {
return "myPage";
}
}
3. Controller will handle the render request for "action=details" (Portlet B):
Code:
@Controller
@RequestMapping("VIEW")
public class DetailsController {
@RequestMapping(params = "action=details")
public String renderDetails(ActionRequest request, ActionResponse response, Model model) {
return "details";
}
}
The jsp "myPage.jsp" has a link to go to the details page:
Code:
<a href="<portlet:renderURL><portlet:param name="action" value="details"/></portlet:renderURL>">go to Details</a>
Step 1: The link on myPage is clicked and the DetailsControllers renderDetails method is executed.
Step 2: The action of the TriggerController is requested and triggers the event.
One might image that the EventController is executed, because it's the only one which can handle this event.
But actually the following is happening:
Step 3: The DefaultAnnotationHandlerMapping tries to determine the correct controller for Portlet B to be executed and ends up in this method:
Code:
protected Object getHandlerInternal(PortletRequest request) throws Exception {
K lookupKey = getLookupKey(request);
Object handler = this.handlerMap.get(lookupKey);
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Key [" + lookupKey + "] -> handler [" + handler + "]");
}
if (handler instanceof Map) {
Map<PortletRequestMappingPredicate, Object> predicateMap =
(Map<PortletRequestMappingPredicate, Object>) handler;
List<PortletRequestMappingPredicate> predicates =
new LinkedList<PortletRequestMappingPredicate>(predicateMap.keySet());
Collections.sort(predicates);
for (PortletRequestMappingPredicate predicate : predicates) {
if (predicate.match(request)) {
predicate.validate(request);
return predicateMap.get(predicate);
}
}
return null;
}
return handler;
}
At first it gets all the handler for the lookupKey, which is the portlets mode ("VIEW" in this case). It will continue with both the EventController and the DetailsConrtoller.
In the next step it tries to determine the right one of the found handlers. Therefore the request parameters are evaluated. Since the portlet request has not changed on its own the request parameter is still "action=details".
This will end up in the decision that the DatailsController is the correct one.
Step 4: At this point the DatailsController is chosen to handle the request, which actually is an Event.
Therefore Spring tries to find the method to be invoked to handle the event, Since the DetailsController has no "@EventMapping", there is no method found.
This finally ends up in the exception:
Code:
javax.portlet.UnavailableException: No matching handler method found for portlet request: mode 'view', phase 'EVENT_PHASE', parameters map['org.springframework.web.portlet.mvc.ImplicitModel' -> array<String>['true'], 'action' -> array<String>['details']]
Is this the intended behavior??
If so, I can only think of two workarounds:
1. Implement an event handling method in each controller of the portlet, to avoid getting this excpetion. Respectively put all "@EventMapping" methods in a superclass for all controllers so that it can be handled.
2. Alternatively one could implement all controller methods in one class. This might be acceptable for small portlets, but is not for us.
Both workarounds are quite ugly in my opion.
Can anyone think of something better or is this a bug in Spring???
Thanks in advance.