views:

335

answers:

2

Using spring DefaultAnnotationHandlerMapping how can I lookup the Controller that would ultimately handle a given url.

I currently have this, but feels like there ought to be a cleaner way than iterating over 100s of request mappings:

public static Object getControllerForAction(String actionURL) {
    ApplicationContext context = getApplicationContext();
    AnnotationHandlerMapping mapping = (AnnotationHandlerMapping) context.getBean("annotationMapper");
    PathMatcher pathMatcher = mapping.getPathMatcher();
    for (Object key: mapping.getHandlerMap().keySet()) {
        if (pathMatcher.match((String) key, actionURL)){
            return mapping.getHandlerMap().get(key);
        }
    }
    return null;
}
+1  A: 

All mappers implement HandlerMapping interface which has a getHandler() method:

ApplicationContext context = getApplicationContext();
AnnotationHandlerMapping mapping = (AnnotationHandlerMapping) context.getBean("annotationMapper");
Object controller = mapping.getHandler().getHandler();

HandlerMapping.getHandler() returns HandlerExecutionChain, calling getHandler() on that would return you the actual handler which - for controller mapper - would be the controller you're looking for.

ChssPly76
The problem with HandlerMapping.getHandler(javax.servlet.http.HttpServletRequest httpServletRequest) is that it takes in a request and I need to handle a situation before the request for the url is created (in a jsp tag).
CNelson
Fair enough, although you didn't say that in your question. In that case I would either go with the way you have it now or take a look at **skaffman**'s suggestion below but be aware of possible issues with NULL request object
ChssPly76
+2  A: 

For the purposes of this question, all of the interesting methods in DefaultAnnotationHandlerMapping and its superclasses are protected, and so not visible to external code. However, it would be trivial to write a custom subclass of DefaultAnnotationHandlerMapping which overrides these methods and makes them public.

Since you need to be able to supply a path rather than a request object, I would suggest lookupHandler of AbstractUrlHandlerMapping would be a good candidate for this. It still needs you to supply it with a request object as well as the path, but that request object is only used to pass to the validateHandler() method, which does nothing, so you could probably supply a null there.

skaffman
No need for subclasses. As I said in my answer, getHandler() is public since it's declared on HandlerMapping interface. It does return `HandlerExecutionChain` but it's easy to unwrap.
ChssPly76
As the OP pointed out, getHandler() is no use for his needs. I've modified my answer to accomodate.
skaffman
To be fair, he pointed that out after both our answers :-) At any rate, validateHandler() is overriden in `DefaultAnnotationHandlerMapping` and passing request as null is NOT an option because it's very much used there.
ChssPly76
skaffman, thank you, your answer does seem to be working with passing null. I'll comment if I run into problems with validateHandler.
CNelson
Ever get the feeling that the Spring guys just didn't want us to do this? :) It looks like null request won't cut it here, and so you could try passing in a MockHttpServletRequest instead, and populate that mock with the things required by validateHandler(). Awkward, but do-able.
skaffman