resthub / springmvc-router

Adds route mapping capacity to any "Spring MVC based" webapp. Uses playframework.org Router implementation.
http://resthub.github.com/springmvc-router/
Other
167 stars 63 forks source link

NullPointerException at org.resthub.web.springmvc.router.Router$Route.matches(Router.java:735) #41

Closed kernel164 closed 11 years ago

kernel164 commented 11 years ago

null check on path should fix this issue I guess.

StackTrace: java.lang.NullPointerException at jregex.Matcher.setTarget(Matcher.java:201) at jregex.Pattern.matcher(Pattern.java:218) at org.resthub.web.springmvc.router.Router$Route.matches(Router.java:735) at org.resthub.web.springmvc.router.Router.route(Router.java:232) at org.resthub.web.springmvc.router.RouterHandlerMapping.getHandlerInternal(RouterHandlerMapping.java:168) at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:298) at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1091) at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1076) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:896) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827) at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812) at javax.servlet.http.HttpServlet.service(HttpServlet.java:722) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722)

kernel164 commented 11 years ago

I'm using 1.1.0 version.

Router#route(HTTPRequestAdapter request) - request.path is null

app log:

02:23:28.120 [http-bio-8080-exec-8] DEBUG o.s.webflow.mvc.servlet.FlowHandlerMapping - No flow mapping found for request with URI '/myapp/main' 02:23:28.134 [http-bio-8080-exec-8] TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - contextPath: /myapp 02:23:28.134 [http-bio-8080-exec-8] TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - request.path: null, request.querystring: 02:23:28.138 [http-bio-8080-exec-8] TRACE org.resthub.web.springmvc.router.Router - Route: null -

kernel164 commented 11 years ago

I'm using springframework-version is 3.2.2.RELEASE & servlet-api-2.5.jar

I guess servlet-api-2.5.jar/ javax.servlet.http.HttpServletRequest#getPathInfo() returns null which is used to set request.path in HTTPRequestAdapter#parseRequest()

kernel164 commented 11 years ago

I use <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> in web.xml and the url which is causing problem is http://localhost/myapp/main for which springmvc trace log shows.... TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - contextPath: /myapp, servletPath: /main TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - request.path: null, request.querystring:

The fix below makes it work though... Router#route() -> route.matches(request.method, request.path == null ? request.servletPath : request.path, format, host);

bclozel commented 11 years ago

I'll add a new test case for this. The Javadoc says that HttpServletRequest.getPathInfo() can return null if no extra path is set.

But I don't think it should be swapped with servletPath if request.path is null (as it is already aken care of otherwise). I think an empty string will do the trick.

Thanks!

kernel164 commented 11 years ago

I tried your suggestion, but it wasn't working - no idea why. So, currently I'm using the downloaded source in my project with route.matches(request.method, request.path == null ? request.servletPath : request.path, format, host); change.

BTW. The first link that I found when I searched for play! like route concept in spring is this project. Congrats and keep up your good work. :) Thanks again.

bclozel commented 11 years ago

I've tested a fix and a new test case for this.

I'll publish a new 1.1.1 snapshot version for you to test it there (you have to add sonatype's snapshot repository in your pom).

For now Sonatype's Auth+JIRA look broken. I'll let you know when it's published.

Glad this project is useful!

bclozel commented 11 years ago

new snapshot published!

kernel164 commented 11 years ago

I still get the same 404 error.

url - http://localhost:8080/myapp/main response - HTTP Status 404 - /myapp/main logs: 00:58:36.978 [http-bio-8080-exec-10] TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - contextPath: /myapp 00:58:36.978 [http-bio-8080-exec-10] TRACE o.resthub.web.springmvc.router.HTTPRequestAdapter - request.path: /, request.querystring: 00:58:36.979 [http-bio-8080-exec-10] TRACE org.resthub.web.springmvc.router.Router - Route: / - 00:58:36.979 [http-bio-8080-exec-10] TRACE o.r.web.springmvc.router.RouterHandlerMapping - no route found for method[GET] and path[/]

bclozel commented 11 years ago

Mhhh. I don't get it :-\

I thought that:

Could you share your route described in your route.conf?

The servlet+context parts should be omitted from your route definition. So this request should look like:

GET    /    myController.indexAction

That's why I forced the pathinfo value to "/" when it is null. You need at least one char for the URL part of a route definition.

kernel164 commented 11 years ago

my route.conf GET /main mainController.main

I don't have any servlet pointing to /main in web.xml just / pointing to spring-dispatcher

By looking at the spring source code, I found that org.springframework.security.web.util.AntPathRequestMatcher is using

private String getRequestPath(HttpServletRequest request) {
    String url = request.getServletPath();

    if (request.getPathInfo() != null) {
        url += request.getPathInfo();
    }

    url = url.toLowerCase();

    return url;
}

I also checked SimpleUrlHandlerMapping & RequestMappingHandlerMapping they both use UrlPathHelper.getLookupPathForRequest(request) to determine the path. which uses all possible info contextPath, servletPath, requestURI, pathInfo etc to determine the path.

e.g. UrlPathHelper.getPathWithinServletMapping()

        // Special case: URI is different from servlet path.
        // Can happen e.g. with index page: URI="/", servletPath="/index.html"
        // Use path info if available, as it indicates an index page within
        // a servlet mapping. Otherwise, use the full servlet path.
        String pathInfo = request.getPathInfo();
        return (pathInfo != null ? pathInfo : servletPath);
bclozel commented 11 years ago

Thanks a bunch for digging into this.

I found something in the servlet spec:

Specification of Mappings
In the Web application deployment descriptor, the following syntax is used to define mappings:
- A string beginning with a ‘/’ character and ending with a ‘/*’ suffix is used for path mapping.
- A string beginning with a ‘*.’ prefix is used as an extension mapping.
- A string containing only the ’/’ character indicates the "default" servlet of the application.
  In this case the servlet path is the request URI minus the context path and the path info is null.
- All other strings are used for exact matches only.

The third item should ring a bell...

I'm reopening this issue and working on a new fix.

bclozel commented 11 years ago

I've just merged a new fix for this issue; and published a new snapshot. Hopefully third time's the charm.

kernel164 commented 11 years ago

its working fine now. :)

one more small favour. could you pls add debug (DEBUG level) statements in RouterHanderMapping.getHandlerInternal with something like

logger.debug("Looking up handler method for path {} ({} {} {})", route.path, route.method, route.path, route.action); logger.debug("Did not find handler method for [{}]", nrfe.path;

Spring sysouts these debug statements for all of its handlers.

12:56:48.386 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /main 12:56:48.387 [http-bio-8080-exec-10] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Did not find handler method for [/main]

It would help debugging a lot easier. :) Thanks again.

bclozel commented 11 years ago

Fixed with new debug statements and a new snapshot. Let me know if you need something else in 1.1.1 release (will ship soon I guess).