getServletPath() output JSP name instead of action name when a request is forwarded inside Servlet application in EAP 7

Solution Unverified - Updated

Environment

  • Red Hat JBoss Enterprise Application Platform (EAP)
    • 7.x

Issue

We are trying to migrate our application from JBoss EAP 6.x to JBoss EAP 7.x. We found that EAP 6.x and EAP 7.x return different results of Content from docs.oracle.com is not included.HttpServletRequest.getServletPath() (and also Content from docs.oracle.com is not included.getRequestURI() and getRequestURL()](https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getRequestURL--)) after invoking Content from docs.oracle.com is not included.RequestDispatcher.forward(). This behavior change causes an issue with our application using Struts and Siteminder framework.

  • In JBoss EAP 6.4:

    getServletPath() = /example.action 
    getRequestURI() = /myapp/example.action
    
  • In JBoss EAP 7.x:

    getServletPath() = /jsp/forward/example.jsp 
    getRequestURI() = /myapp/jsp/forward/example.jsp 
    

Also, you can observe the different behavior with the following code in the servlet application without using Struts framework:

System.out.println("getServletPath() before forward = " + request.getServletPath());
System.out.println("getRequestURI() before forward = " + request.getRequestURI());
System.out.println("getRequestURL() before forward = " + request.getRequestURL());
request.getRequestDispatcher("/jsp/forward/example.jsp").forward(request, response);
System.out.println("getServletPath() after forward = " + request.getServletPath());
System.out.println("getRequestURI() after forward = " + request.getRequestURI());
System.out.println("getRequestURL() after forward = " + request.getRequestURL());
  • In JBoss EAP 6.4:

    getServletPath() before forward = /example
    getRequestURI() before forward = /myapp/example
    getRequestURL() before forward = http://<hostname>:<port>/myapp/example
    getServletPath() after forward = /example 
    getRequestURI() after forward = /myapp/example
    getRequestURL() after forward = http://<hostname>:<port>/myapp/example
    
  • In JBoss EAP 7.x:

    getServletPath() before forward = /example 
    getRequestURI() before forward = /myapp/example
    getRequestURL() before forward = http://<hostname>:<port>/myapp/example
    getServletPath() after forward = /jsp/forward/example.jsp 
    getRequestURI() after forward = /myapp/jsp/forward/example.jsp 
    getRequestURL() after forward = http://<hostname>:<port>/myapp/jsp/forward/example.jsp
    

Is it possible to make JBoss EAP 7 behave the same as JBoss EAP 6?

Resolution

The current EAP 7's behavior does not violate Servlet specification. However, we've added a compatible switch to restore the previous EAP 6's behavior since EAP 7.2.1 or later.

Update to JBoss EAP 7.2 CP1 and set the system property io.undertow.servlet.dispatch.preserve_path_of_forward to true in your standalone.conf (or add the system property in your configuration as described in this knowledge article):

JAVA_OPTS="$JAVA_OPTS -Dio.undertow.servlet.dispatch.preserve_path_of_forward=true"

In EAP 7.3.0 or later, set preserve-path-on-forward="true" to <servlet-container> in the undertow subsystem instead of the above system property. You can configure this by the following JBoss-CLI command:

/subsystem=undertow/servlet-container=default:write-attribute(name=preserve-path-on-forward,value=true)

then this will configure like the following in the undertow subsystem:

            <servlet-container name="default" ...(snip)... preserve-path-on-forward="true">
                ...(snip)... 
            </servlet-container>

If you would like to change the behavior for the specific application only, update to EAP 7.2 CP2 or later, then implement and package your own Content from undertow.io is not included.ServletExtension which invokes DeploymentInfo.setPreservePathOnForward(true).

For example: create the following PreservePathOnForwardServletExtension class:

package org.jboss.support.example.

import javax.servlet.ServletContext;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.ServletExtension;

public class PreservePathOnForwardServletExtension implements ServletExtension {
    @Override
    public void handleDeployment(DeploymentInfo deploymentInfo, ServletContext servletContext) {
        deploymentInfo.setPreservePathOnForward(true);
    }
}

and package the above class and META-INF/services/io.undertow.servlet.ServletExtension which contains the following:

org.jboss.support.example.PreservePathOnForwardServletExtension

in your application.

Root Cause

Components
Category

This solution is part of Red Hat’s fast-track publication program, providing a huge library of solutions that Red Hat engineers have created while supporting our customers. To give you the knowledge you need the instant it becomes available, these articles may be presented in a raw and unedited form.