Bug 57216 - Servlet mapping not found when creating RequestDispatcher for forward()
Summary: Servlet mapping not found when creating RequestDispatcher for forward()
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 8
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 8.0.14
Hardware: PC Linux
: P2 regression (vote)
Target Milestone: ----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-11-14 23:40 UTC by tomcatuser2008
Modified: 2014-11-20 01:21 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description tomcatuser2008 2014-11-14 23:40:16 UTC
We have a servlet filter which creates a RequestDispatcher used to forward the request to a servlet:

  String processingPath = processPath(req.getServletPath());
  RequestDispatcher dispatch = request.getRequestDispatcher(processingPath);
  dispatch.forward(request, response);

In Tomcat 8 the forward() fails because the servlet path doesn't match the servlet mapping definition.  This only occurs when the context path is "/".

We configure our Context in server.xml:

<Host name="localhost"  appBase="" createDirs="false" unpackWARs="false" autoDeploy="false" deployOnStartup="false">
  <Context path="/" ...>
    ...
  </Context>
</Host>

In ApplicationContext.getRequestDispatcher(String path) the context path is prepended to the resource uri:

  uriCC.append(context.getPath(), 0, context.getPath().length());

Then in Mapper.map() a ContextVersion is retrieved from contextObjectToContextVersionMap and passed to internalMapWrapper(), which tries to remove the context path:

  int length = contextVersion.path.length();
    ...
  servletPath = pathOffset + length;
    ...
  path.setOffset(servletPath);

But the path is empty, so the offset is zero, and we end up with an extra "/" at the start of the servlet path. This causes the servlet mapping to not be matched.

This is ultimately due to these lines in MapperListener.registerContext():

        String contextPath = context.getPath();
        if ("/".equals(contextPath)) {
            contextPath = "";
        }

        mapper.addContextVersion(host.getName(), host, contextPath,
                context.getWebappVersion(), context, welcomeFiles, resources,
                wrappers);

In earlier Tomcat versions, the same context object was used to get the context path in both ApplicationContext.getRequestDispatcher() and Mapper.map().  In Tomcat 8 ApplicationContext.getRequestDispatcher() uses a StandardContext object (with path="/"), while Mapper.map() uses a ContextVersion object (with path="").
Comment 1 Chuck Caldarale 2014-11-15 14:19:27 UTC
(In reply to tomcatuser2008 from comment #0)

> We configure our Context in server.xml:

Pretty much always a bad idea.

> <Host name="localhost"  appBase="" createDirs="false" unpackWARs="false"

An empty appBase is asking for trouble.

>   <Context path="/" ...>

This is an invalid path setting.  To quote from section 3.5 of the servlet spec:

Context Path: The path prefix associated with the ServletContext that this Servlet is a part of. If this context is the "default" context rooted at the base of the Web server's URL name space, this path will be an empty string. Otherwise, if the context is not rooted at the root of the server's name space, the path starts with a / character but does not end with a / character.

You need to correct your configuration.  The fact that something useful happened in prior versions is an accident.
Comment 2 Mark Thomas 2014-11-17 08:09:33 UTC
Context path values of "/" are now logged as invalid and converted to "".
Comment 3 Konstantin Kolinko 2014-11-18 14:43:52 UTC
I backported the path checks to Tomcat 7 (r1640351).
It will be in 7.0.58 onwards.
Comment 4 Konstantin Kolinko 2014-11-18 14:53:42 UTC
For a record:
The check and correction of context paths originates from r1080714 in 7.0.12 (3 years ago). Thus the fix was backported to Tomcat 7 as well.
Comment 5 tomcatuser2008 2014-11-20 01:21:46 UTC
Thanks for your prompt responses.  We'll change our config to <Context path="" ...>.