org.apache.catalina.core.ApplicationDispatcher Maven / Gradle / Ivy
Show all versions of payara-micro Show documentation
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates.]
package org.apache.catalina.core;
import fish.payara.nucleus.requesttracing.RequestTracingService;
import org.apache.catalina.*;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.MappingImpl;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.RequestFacade;
import org.apache.catalina.connector.ResponseFacade;
import org.apache.catalina.util.InstanceSupport;
import org.glassfish.grizzly.http.server.util.Mapper;
import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.util.CharChunk;
import org.glassfish.grizzly.http.util.MessageBytes;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletMapping;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.apache.catalina.InstanceEvent.EventType.AFTER_DISPATCH_EVENT;
/**
* Standard implementation of RequestDispatcher
that allows a
* request to be forwarded to a different resource to create the ultimate
* response, or to include the output of another resource in the response
* from this resource. This implementation allows application level servlets
* to wrap the request and/or response objects that are passed on to the
* called resource, as long as the wrapping classes extend
* jakarta.servlet.ServletRequestWrapper
and
* jakarta.servlet.ServletResponseWrapper
.
*
* @author Craig R. McClanahan
* @version $Revision: 1.16 $ $Date: 2007/02/26 22:57:08 $
*/
public final class ApplicationDispatcher
implements RequestDispatcher {
private static final Logger log = LogFacade.getLogger();
private static final ResourceBundle rb = log.getResourceBundle();
// This attribute corresponds to a String[] which acts like a stack
// containing the last two pushed elements
public static final String LAST_DISPATCH_REQUEST_PATH_ATTR =
"org.apache.catalina.core.ApplicationDispatcher.lastDispatchRequestPathAttr";
protected class PrivilegedDispatch implements PrivilegedExceptionAction {
private ServletRequest request;
private ServletResponse response;
private DispatcherType dispatcherType;
PrivilegedDispatch(ServletRequest request, ServletResponse response,
DispatcherType dispatcherType) {
this.request = request;
this.response = response;
this.dispatcherType = dispatcherType;
}
@Override
public Void run() throws java.lang.Exception {
doDispatch(request, response, dispatcherType);
return null;
}
}
protected class PrivilegedInclude implements PrivilegedExceptionAction {
private final ServletRequest request;
private final ServletResponse response;
PrivilegedInclude(ServletRequest request, ServletResponse response) {
this.request = request;
this.response = response;
}
@Override
public Void run() throws ServletException, IOException {
doInclude(request,response);
return null;
}
}
/**
* Used to pass state when the request dispatcher is used. Using instance
* variables causes threading issues and state is too complex to pass and
* return single ServletRequest or ServletResponse objects.
*/
private static class State {
/**
* Outermost request that will be passed on to the invoked servlet
*/
ServletRequest outerRequest = null;
/**
* Outermost response that will be passed on to the invoked servlet.
*/
ServletResponse outerResponse = null;
/**
* Request wrapper we have created and installed (if any).
*/
ServletRequest wrapRequest = null;
/**
* Response wrapper we have created and installed (if any).
*/
ServletResponse wrapResponse = null;
/**
* The type of dispatch we are performing
*/
DispatcherType dispatcherType;
/**
* Outermost HttpServletRequest in the chain
*/
HttpServletRequest hrequest = null;
/**
* Outermost HttpServletResponse in the chain
*/
HttpServletResponse hresponse = null;
State(ServletRequest request, ServletResponse response,
DispatcherType dispatcherType) {
this.outerRequest = request;
this.outerResponse = response;
this.dispatcherType = dispatcherType;
}
}
// ----------------------------------------------------------- Constructors
/**
* Construct a new instance of this class, configured according to the
* specified parameters. If both servletPath and pathInfo are
* null
, it will be assumed that this RequestDispatcher
* was acquired by name, rather than by path.
*
* @param wrapper The Wrapper associated with the resource that will
* be forwarded to or included (required)
* @param requestURI The request URI to this resource (if any)
* @param servletPath The revised servlet path to this resource (if any)
* @param pathInfo The revised extra path information to this resource
* (if any)
* @param queryString Query string parameters included with this request
* (if any)
* @param name Servlet name (if a named dispatcher was created)
* else null
*/
public ApplicationDispatcher
(Wrapper wrapper, HttpServletMapping mappingForDispatch, String requestURI, String servletPath,
String pathInfo, String queryString, String name) {
super();
// Save all of our configuration parameters
this.wrapper = wrapper;
this.mappingForDispatch = mappingForDispatch;
this.context = (Context) wrapper.getParent();
this.requestURI = requestURI;
this.servletPath = servletPath;
this.pathInfo = pathInfo;
this.queryString = queryString;
this.name = name;
requestTracing = org.glassfish.internal.api.Globals.getDefaultHabitat().getService(RequestTracingService.class);
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "servletPath= " + this.servletPath + ", pathInfo= "
+ this.pathInfo + ", queryString= " + queryString + ", name= "
+ this.name + "");
}
// ----------------------------------------------------- Instance Variables
//START OF 6364900
/**
* is this dispatch cross context
*/
private Boolean crossContextFlag = null;
//END OF 6364900
/**
* The Context this RequestDispatcher is associated with.
*/
private Context context = null;
/**
* Descriptive information about this implementation.
*/
private static final String info =
"org.apache.catalina.core.ApplicationDispatcher/1.0";
/**
* The servlet name for a named dispatcher.
*/
private String name = null;
/**
* The extra path information for this RequestDispatcher.
*/
private String pathInfo = null;
/**
* The query string parameters for this RequestDispatcher.
*/
private String queryString = null;
/**
* The request URI for this RequestDispatcher.
*/
private String requestURI = null;
/**
* The servlet path for this RequestDispatcher.
*/
private String servletPath = null;
/**
* The Wrapper associated with the resource that will be forwarded to
* or included.
*/
private Wrapper wrapper;
private HttpServletMapping mappingForDispatch;
private RequestTracingService requestTracing;
// ------------------------------------------------------------- Properties
/**
* Return the descriptive information about this implementation.
*/
public String getInfo() {
return (this.info);
}
// --------------------------------------------------------- Public Methods
/**
* Forwards the given request and response to the resource
* for which this dispatcher was acquired.
*
* Any runtime exceptions, IOException, or ServletException thrown
* by the target will be propagated to the caller.
*
* @param request The request to be forwarded
* @param response The response to be forwarded
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet exception occurs
*/
@Override
public void forward(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
dispatch(request, response, DispatcherType.FORWARD);
}
/**
* Dispatches the given request and response to the resource
* for which this dispatcher was acquired.
*
*
Any runtime exceptions, IOException, or ServletException thrown
* by the target will be propagated to the caller.
*
* @param request The request to be forwarded
* @param response The response to be forwarded
* @param dispatcherType The type of dispatch to be performed
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet exception occurs
* @throws IllegalArgumentException if the dispatcher type is different
* from FORWARD, ERROR, and ASYNC
*/
public void dispatch(ServletRequest request, ServletResponse response,
DispatcherType dispatcherType)
throws ServletException, IOException {
if (DispatcherType.FORWARD != dispatcherType &&
DispatcherType.ERROR != dispatcherType &&
DispatcherType.ASYNC != dispatcherType) {
throw new IllegalArgumentException("Illegal dispatcher type");
}
boolean isCommit = (DispatcherType.FORWARD == dispatcherType ||
DispatcherType.ERROR == dispatcherType);
if (Globals.IS_SECURITY_ENABLED) {
try {
PrivilegedDispatch dp = new PrivilegedDispatch(
request, response, dispatcherType);
AccessController.doPrivileged(dp);
// START SJSAS 6374990
if (isCommit && !request.isAsyncStarted()) {
ApplicationDispatcherForward.commit(request, response,
context, wrapper);
}
// END SJSAS 6374990
} catch (PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
throw (IOException) e;
}
} else {
doDispatch(request, response, dispatcherType);
// START SJSAS 6374990
if (isCommit && !request.isAsyncStarted()) {
ApplicationDispatcherForward.commit(request, response,
context, wrapper);
}
// END SJSAS 6374990
}
}
private void doDispatch(ServletRequest request, ServletResponse response,
DispatcherType dispatcherType)
throws ServletException, IOException {
if (DispatcherType.ASYNC != dispatcherType) {
// Reset any output that has been buffered, but keep
// headers/cookies
if (response.isCommitted()) {
if (log.isLoggable(Level.FINE))
log.log(Level.FINE, "Forward on committed response --> ISE");
throw new IllegalStateException
(rb.getString(LogFacade.ILLEGAL_STATE_EXCEPTION));
}
try {
response.resetBuffer();
} catch (IllegalStateException e) {
if (log.isLoggable(Level.FINE))
log.log(Level.FINE,
"Forward resetBuffer() returned ISE: " + e.toString(), e);
throw e;
}
}
if (DispatcherType.INCLUDE != dispatcherType) {
DispatchTargetsInfo dtInfo =
(DispatchTargetsInfo)request.getAttribute(
LAST_DISPATCH_REQUEST_PATH_ATTR);
if (dtInfo == null) {
dtInfo = new DispatchTargetsInfo();
request.setAttribute(LAST_DISPATCH_REQUEST_PATH_ATTR, dtInfo);
}
if (servletPath == null && pathInfo == null) {
// Handle an HTTP named dispatcher forward
dtInfo.addDispatchTarget(wrapper.getServletName(), true);
} else {
dtInfo.addDispatchTarget(getCombinedPath(), false);
}
}
// Set up to handle the specified request and response
State state = new State(request, response, dispatcherType);
ServletRequest sr = wrapRequest(state);
wrapResponse(state);
// Identify the HTTP-specific request and response objects (if any)
HttpServletRequest hrequest = state.hrequest;
HttpServletResponse hresponse = state.hresponse;
if ((hrequest == null) || (hresponse == null)) {
// Handle a non-HTTP forward
processRequest(request, response, state);
} else if ((servletPath == null) && (pathInfo == null)) {
// Handle an HTTP named dispatcher forward
ApplicationHttpRequest wrequest = (ApplicationHttpRequest)sr;
wrequest.setRequestURI(hrequest.getRequestURI());
wrequest.setContextPath(hrequest.getContextPath());
wrequest.setServletPath(hrequest.getServletPath());
wrequest.setPathInfo(hrequest.getPathInfo());
wrequest.setQueryString(hrequest.getQueryString());
processRequest(request, response, state);
} else {
// Handle an HTTP path-based forward
ApplicationHttpRequest wrequest = (ApplicationHttpRequest)sr;
// If the request is being FORWARD- or ASYNC-dispatched for
// the first time, initialize it with the required request
// attributes
if ((DispatcherType.FORWARD == dispatcherType &&
hrequest.getAttribute(
RequestDispatcher.FORWARD_REQUEST_URI) == null) ||
(DispatcherType.ASYNC == dispatcherType &&
hrequest.getAttribute(
AsyncContext.ASYNC_REQUEST_URI) == null)) {
wrequest.initSpecialAttributes(hrequest.getRequestURI(),
hrequest.getContextPath(),
hrequest.getServletPath(),
hrequest.getPathInfo(),
hrequest.getQueryString());
}
String targetContextPath = context.getPath();
// START IT 10395
RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(wrequest);
String originContextPath = null;
if (reqFacHelper != null) {
originContextPath = reqFacHelper.getContextPath(false);
} else {
originContextPath = wrequest.getContextPath();
}
if (originContextPath != null &&
originContextPath.equals(targetContextPath)) {
targetContextPath = hrequest.getContextPath();
}
// END IT 10395
wrequest.setContextPath(targetContextPath);
wrequest.setRequestURI(requestURI);
wrequest.setServletPath(servletPath);
wrequest.setPathInfo(pathInfo);
if (queryString != null) {
wrequest.setQueryString(queryString);
wrequest.setQueryParams(queryString);
}
processRequest(request, response, state);
}
recycleRequestWrapper(state);
unwrapRequest(state);
unwrapResponse(state);
}
/**
* Prepare the request based on the filter configuration.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet error occurs
*/
private void processRequest(ServletRequest request,
ServletResponse response,
State state)
throws IOException, ServletException {
if (request != null) {
if (state.dispatcherType != DispatcherType.ERROR) {
state.outerRequest.setAttribute(
Globals.DISPATCHER_REQUEST_PATH_ATTR,
getCombinedPath());
}
invoke(state.outerRequest, response, state);
}
}
/**
* Combines the servletPath and the pathInfo.
*
* If pathInfo is null
, it is ignored. If servletPath
* is null
, then null
is returned.
*
* @return The combined path with pathInfo appended to servletInfo
*/
private String getCombinedPath() {
if (servletPath == null) {
return null;
}
if (pathInfo == null) {
return servletPath;
}
return servletPath + pathInfo;
}
/**
* Include the response from another resource in the current response.
* Any runtime exception, IOException, or ServletException thrown by the
* called servlet will be propagated to the caller.
*
* @param request The servlet request that is including this one
* @param response The servlet response to be appended to
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet exception occurs
*/
@Override
public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
if (Globals.IS_SECURITY_ENABLED) {
try {
PrivilegedInclude dp = new PrivilegedInclude(request,response);
AccessController.doPrivileged(dp);
} catch (PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException)
throw (ServletException) e;
throw (IOException) e;
}
} else {
doInclude(request,response);
}
}
private void doInclude(ServletRequest request, ServletResponse response)
throws ServletException, IOException
{
// Set up to handle the specified request and response
State state = new State(request, response, DispatcherType.INCLUDE);
// Create a wrapped response to use for this request
wrapResponse(state);
// Handle a non-HTTP include
/* GlassFish 6386229
if (!(request instanceof HttpServletRequest) ||
!(response instanceof HttpServletResponse)) {
if ( log.isDebugEnabled() )
log.debug(" Non-HTTP Include");
request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
Integer.valueOf(ApplicationFilterFactory.INCLUDE));
request.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
//origServletPath);
servletPath);
try{
invoke(request, state.outerResponse, state);
} finally {
unwrapResponse(state);
}
}
// Handle an HTTP named dispatcher include
else if (name != null) {
*/
// START GlassFish 6386229
// Handle an HTTP named dispatcher include
if (name != null) {
// END GlassFish 6386229
ApplicationHttpRequest wrequest = (ApplicationHttpRequest)wrapRequest(state);
wrequest.setAttribute(Globals.NAMED_DISPATCHER_ATTR, name);
if (servletPath != null)
wrequest.setServletPath(servletPath);
wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
getCombinedPath());
try{
invoke(state.outerRequest, state.outerResponse, state);
} finally {
recycleRequestWrapper(state);
unwrapRequest(state);
unwrapResponse(state);
}
}
// Handle an HTTP path based include
else {
ApplicationHttpRequest wrequest = (ApplicationHttpRequest)wrapRequest(state);
wrequest.initSpecialAttributes(requestURI,
context.getPath(),
servletPath,
pathInfo,
queryString);
wrequest.setQueryParams(queryString);
wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
getCombinedPath());
try{
invoke(state.outerRequest, state.outerResponse, state);
} finally {
recycleRequestWrapper(state);
unwrapRequest(state);
unwrapResponse(state);
}
}
}
// -------------------------------------------------------- Private Methods
/**
* Ask the resource represented by this RequestDispatcher to process
* the associated request, and create (or append to) the associated
* response.
*
* IMPLEMENTATION NOTE: This implementation assumes
* that no filters are applied to a forwarded or included resource,
* because they were already done for the original request.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet error occurs
*/
private void invoke(ServletRequest request, ServletResponse response,
State state)
throws IOException, ServletException {
//START OF 6364900 original invoke has been renamed to doInvoke
boolean crossContext = crossContextFlag != null && crossContextFlag;
if (crossContext) {
context.getManager().lockSession(request);
}
try {
if (crossContext) {
context.getManager().preRequestDispatcherProcess(request,
response);
}
doInvoke(request, response, crossContext, state);
if (crossContext) {
context.getManager().postRequestDispatcherProcess(request,
response);
}
} finally {
if (crossContext) {
context.getManager().unlockSession(request);
}
crossContextFlag = null;
}
//END OF 6364900
}
/**
* Ask the resource represented by this RequestDispatcher to process
* the associated request, and create (or append to) the associated
* response.
*
* IMPLEMENTATION NOTE: This implementation assumes
* that no filters are applied to a forwarded or included resource,
* because they were already done for the original request.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param crossContext true if the request dispatch is crossing context
* boundaries, false otherwise
* @param state the state of this ApplicationDispatcher
*
* @throws IOException if an input/output error occurs
* @throws ServletException if a servlet error occurs
*/
private void doInvoke(ServletRequest request, ServletResponse response,
boolean crossContext, State state)
throws IOException, ServletException {
// Checking to see if the context classloader is the current context
// classloader. If it's not, we're saving it, and setting the context
// classloader to the Context classloader
ClassLoader oldCCL = null;
if (crossContext) {
oldCCL = Thread.currentThread().getContextClassLoader();
ClassLoader contextClassLoader = context.getLoader().getClassLoader();
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
HttpServletResponse hresponse = state.hresponse;
Servlet servlet = null;
IOException ioException = null;
ServletException servletException = null;
RuntimeException runtimeException = null;
boolean unavailable = false;
// Check for the servlet being marked unavailable
if (wrapper.isUnavailable()) {
String msg = MessageFormat.format(rb.getString(LogFacade.UNAVAILABLE_SERVLET),
wrapper.getName());
log.log(Level.WARNING, msg);
if (hresponse == null) {
; // NOTE - Not much we can do generically
} else {
long available = wrapper.getAvailable();
if ((available > 0L) && (available < Long.MAX_VALUE))
hresponse.setDateHeader("Retry-After", available);
hresponse.sendError
(HttpServletResponse.SC_SERVICE_UNAVAILABLE, msg);
}
unavailable = true;
}
// Allocate a servlet instance to process this request
String allocateServletMsg =
MessageFormat.format(rb.getString(LogFacade.ALLOCATE_SERVLET_EXCEPTION),
wrapper.getName());
try {
if (!unavailable) {
servlet = wrapper.allocate();
}
} catch (ServletException e) {
log.log(Level.SEVERE, allocateServletMsg,
StandardWrapper.getRootCause(e));
servletException = e;
servlet = null;
} catch (Throwable e) {
log.log(Level.SEVERE, allocateServletMsg, e);
servletException = new ServletException(allocateServletMsg, e);
servlet = null;
}
// Get the FilterChain Here
ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain = factory.createFilterChain(
request, wrapper, servlet);
InstanceSupport support = ((StandardWrapper) wrapper).getInstanceSupport();
// Call the service() method for the allocated servlet instance
String servletServiceExceptionMsg =
MessageFormat.format(rb.getString(LogFacade.SERVLET_SERVICE_EXCEPTION),
wrapper.getName());
RequestFacadeHelper reqFacHelper = RequestFacadeHelper.getInstance(request);
try {
String jspFile = wrapper.getJspFile();
if (jspFile != null) {
request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
}
support.fireInstanceEvent(
InstanceEvent.EventType.BEFORE_DISPATCH_EVENT,
servlet, request, response);
// for includes/forwards
/* IASRI 4665318
if ((servlet != null) && (filterChain != null)) {
*/
// START IASRI 4665318
if (servlet != null) {
// END IASRI 4665318
// START OF S1AS 4703023
if (reqFacHelper != null) {
reqFacHelper.incrementDispatchDepth();
if (reqFacHelper.isMaxDispatchDepthReached()) {
String msg = MessageFormat.format(rb.getString(LogFacade.MAX_DISPATCH_DEPTH_REACHED), new Object[]{Request.getMaxDispatchDepth()});
throw new ServletException(msg);
}
}
// END OF S1AS 4703023
/* IASRI 4665318
filterChain.doFilter(request, response);
*/
// START IASRI 4665318
if (filterChain != null) {
filterChain.setWrapper((StandardWrapper)wrapper);
filterChain.doFilter(request, response);
} else {
((StandardWrapper)wrapper).service(
request, response, servlet);
}
// END IASRI 4665318
}
// Servlet Service Method is called by the FilterChain
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
} catch (ClientAbortException e) {
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
ioException = e;
} catch (IOException e) {
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
log.log(Level.WARNING, servletServiceExceptionMsg, e);
ioException = e;
} catch (UnavailableException e) {
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
log.log(Level.WARNING, servletServiceExceptionMsg, e);
servletException = e;
wrapper.unavailable(e);
} catch (ServletException e) {
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
Throwable rootCause = StandardWrapper.getRootCause(e);
if (!(rootCause instanceof ClientAbortException)) {
log.log(Level.WARNING, servletServiceExceptionMsg, rootCause);
}
servletException = e;
} catch (RuntimeException e) {
support.fireInstanceEvent(AFTER_DISPATCH_EVENT,
servlet, request, response);
log.log(Level.WARNING, servletServiceExceptionMsg, e);
runtimeException = e;
// START OF S1AS 4703023
} finally {
if (reqFacHelper != null) {
reqFacHelper.decrementDispatchDepth();
}
// END OF S1AS 4703023
}
// Release the filter chain (if any) for this request
try {
if (filterChain != null)
filterChain.release();
} catch (Throwable e) {
String msg = MessageFormat.format(rb.getString(LogFacade.RELEASE_FILTERS_EXCEPTION_SEVERE),
wrapper.getName());
log.log(Level.SEVERE, msg, e);
// FIXME Exception handling needs to be simpiler to what is
// in the StandardWrapperValue
}
// Deallocate the allocated servlet instance
String deallocateServletExceptionMsg =
MessageFormat.format(rb.getString(LogFacade.ALLOCATE_SERVLET_EXCEPTION),
wrapper.getName());
try {
if (servlet != null) {
wrapper.deallocate(servlet);
}
} catch (ServletException e) {
log.log(Level.SEVERE, deallocateServletExceptionMsg, e);
servletException = e;
} catch (Throwable e) {
log.log(Level.SEVERE, deallocateServletExceptionMsg, e);
servletException = new ServletException(deallocateServletExceptionMsg, e);
}
// Reset the old context class loader
if (oldCCL != null)
Thread.currentThread().setContextClassLoader(oldCCL);
// Rethrow an exception if one was thrown by the invoked servlet
if (ioException != null)
throw ioException;
if (servletException != null)
throw servletException;
if (runtimeException != null)
throw runtimeException;
}
/**
* Log a message on the Logger associated with our Context (if any)
*
* @param message Message to be logged
*
private void log(String message) {
org.apache.catalina.Logger logger = context.getLogger();
if (logger != null) {
logger.log("ApplicationDispatcher[" + context.getPath() +
"]: " + message);
} else {
if (log.isLoggable(Level.INFO)) {
String msg = MessageFormat.format(rb.getString(LogFacade.APPLICATION_DISPATCHER_INFO),
context.getPath(), message);
log.log(Level.INFO, msg);
}
}
}
/**
* Log a message on the Logger associated with our Container (if any)
*
* @param message Message to be logged
* @param t Associated exception
*
private void log(String message, Throwable t) {
org.apache.catalina.Logger logger = context.getLogger();
if (logger != null) {
logger.log("ApplicationDispatcher[" + context.getPath() +
"] " + message, t, org.apache.catalina.Logger.WARNING);
} else {
String msg = MessageFormat.format(rb.getString(LogFacade.APPLICATION_DISPATCHER_WARNING),
context.getPath(), message);
log.log(Level.WARNING, msg, t);
}
} */
/**
* Unwrap the request if we have wrapped it.
*/
private void unwrapRequest(State state) {
if (state.wrapRequest == null)
return;
ServletRequest previous = null;
ServletRequest current = state.outerRequest;
while (current != null) {
// If we run into the container request we are done
if ((current instanceof org.apache.catalina.Request)
|| (current instanceof RequestFacade))
break;
// Remove the current request if it is our wrapper
if (current == state.wrapRequest) {
ServletRequest next =
((ServletRequestWrapper) current).getRequest();
if (previous == null)
state.outerRequest = next;
else
((ServletRequestWrapper) previous).setRequest(next);
break;
}
// Advance to the next request in the chain
previous = current;
current = ((ServletRequestWrapper) current).getRequest();
}
}
/**
* Unwrap the response if we have wrapped it.
*/
private void unwrapResponse(State state) {
if (state.wrapResponse == null)
return;
ServletResponse previous = null;
ServletResponse current = state.outerResponse;
while (current != null) {
// If we run into the container response we are done
if ((current instanceof org.apache.catalina.Response) ||
(current instanceof ResponseFacade))
break;
// Remove the current response if it is our wrapper
if (current == state.wrapResponse) {
ServletResponse next =
((ServletResponseWrapper) current).getResponse();
if (previous == null)
state.outerResponse = next;
else
((ServletResponseWrapper) previous).setResponse(next);
break;
}
// Advance to the next response in the chain
previous = current;
current = ((ServletResponseWrapper) current).getResponse();
}
}
/**
* Create and return a request wrapper that has been inserted in the
* appropriate spot in the request chain.
*/
private ServletRequest wrapRequest(State state) {
// Locate the request we should insert in front of
ServletRequest previous = null;
ServletRequest current = state.outerRequest;
while (current != null) {
if (state.hrequest == null && (current instanceof HttpServletRequest)) {
state.hrequest = (HttpServletRequest)current;
}
if ("org.apache.catalina.servlets.InvokerHttpRequest".
equals(current.getClass().getName())) {
break; // KLUDGE - Make nested RD.forward() using invoker work
}
if (!(current instanceof ServletRequestWrapper)) {
break;
}
// If we find container-generated wrapper, break out
if (current instanceof ApplicationHttpRequest) {
break;
}
if (current instanceof ApplicationRequest) {
break;
}
previous = current;
current = ((ServletRequestWrapper) current).getRequest();
}
// Instantiate a new wrapper at this point and insert it in the chain
ServletRequest wrapper = null;
if ((current instanceof ApplicationHttpRequest) ||
(current instanceof Request) ||
(current instanceof HttpServletRequest)) {
// Compute a crossContext flag
HttpServletRequest hcurrent = (HttpServletRequest) current;
boolean crossContext = false;
if ((state.outerRequest instanceof ApplicationHttpRequest) ||
(state.outerRequest instanceof Request) ||
(state.outerRequest instanceof HttpServletRequest)) {
HttpServletRequest houterRequest =
(HttpServletRequest) state.outerRequest;
Object contextPath = houterRequest.getAttribute(
RequestDispatcher.INCLUDE_CONTEXT_PATH);
if (contextPath == null) {
// Forward
contextPath = houterRequest.getContextPath();
}
crossContext = !(context.getPath().equals(contextPath));
}
//START OF 6364900
crossContextFlag = crossContext;
//END OF 6364900
if (this.name != null) {
this.mappingForDispatch = computeNamedDispatchHttpServletMapping(context, hcurrent);
}
wrapper = new ApplicationHttpRequest
(hcurrent, context, crossContext, mappingForDispatch, state.dispatcherType);
} else {
wrapper = new ApplicationRequest(current);
}
if (previous == null) {
state.outerRequest = wrapper;
} else {
((ServletRequestWrapper) previous).setRequest(wrapper);
}
state.wrapRequest = wrapper;
return wrapper;
}
private HttpServletMapping computeNamedDispatchHttpServletMapping(Context context, HttpServletRequest hcurrent) {
HttpServletMapping result = null;
Mapper mapper = context.getMapper();
if (null == mapper) {
return null;
}
MessageBytes uriMB = MessageBytes.newInstance();
CharChunk cc = uriMB.getCharChunk();
MappingData mappingData = new MappingData();
String requestURI = hcurrent.getRequestURI();
if (null == requestURI) {
return null;
}
try {
cc.append(requestURI, 0, requestURI.length());
mapper.map(uriMB, mappingData);
} catch (Exception ex) {
return null;
}
result = new MappingImpl(mappingData);
return result;
}
/**
* Create and return a response wrapper that has been inserted in the
* appropriate spot in the response chain.
*/
private ServletResponse wrapResponse(State state) {
// Locate the response we should insert in front of
ServletResponse previous = null;
ServletResponse current = state.outerResponse;
while (current != null) {
if(state.hresponse == null && (current instanceof HttpServletResponse)) {
state.hresponse = (HttpServletResponse)current;
if (DispatcherType.INCLUDE != state.dispatcherType) // Forward only needs hresponse
return null;
}
if (!(current instanceof ServletResponseWrapper))
break;
if (current instanceof ApplicationHttpResponse)
break;
if (current instanceof ApplicationResponse)
break;
previous = current;
current = ((ServletResponseWrapper) current).getResponse();
}
// Instantiate a new wrapper at this point and insert it in the chain
ServletResponse wrapper = null;
if ((current instanceof ApplicationHttpResponse) ||
(current instanceof HttpResponse) ||
(current instanceof HttpServletResponse))
wrapper =
new ApplicationHttpResponse((HttpServletResponse) current,
DispatcherType.INCLUDE == state.dispatcherType);
else
wrapper = new ApplicationResponse(current,
DispatcherType.INCLUDE == state.dispatcherType);
if (previous == null)
state.outerResponse = wrapper;
else
((ServletResponseWrapper) previous).setResponse(wrapper);
state.wrapResponse = wrapper;
return (wrapper);
}
private void recycleRequestWrapper(State state) {
if (state.wrapRequest instanceof ApplicationHttpRequest) {
((ApplicationHttpRequest) state.wrapRequest).recycle();
}
}
}