Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/catalina/core/ApplicationDispatcher.java


1   /*
2    * Copyright 1999,2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  
18  package org.apache.catalina.core;
19  
20  import java.io.IOException;
21  import java.io.PrintWriter;
22  import java.security.AccessController;
23  import java.security.PrivilegedActionException;
24  import java.security.PrivilegedExceptionAction;
25  
26  import javax.servlet.RequestDispatcher;
27  import javax.servlet.Servlet;
28  import javax.servlet.ServletException;
29  import javax.servlet.ServletOutputStream;
30  import javax.servlet.ServletRequest;
31  import javax.servlet.ServletRequestWrapper;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.ServletResponseWrapper;
34  import javax.servlet.UnavailableException;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.apache.catalina.Context;
39  import org.apache.catalina.Globals;
40  import org.apache.catalina.InstanceEvent;
41  import org.apache.catalina.Wrapper;
42  import org.apache.catalina.connector.ClientAbortException;
43  import org.apache.catalina.connector.Request;
44  import org.apache.catalina.connector.RequestFacade;
45  import org.apache.catalina.connector.Response;
46  import org.apache.catalina.connector.ResponseFacade;
47  import org.apache.catalina.util.InstanceSupport;
48  import org.apache.catalina.util.StringManager;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  
52  /**
53   * Standard implementation of <code>RequestDispatcher</code> that allows a
54   * request to be forwarded to a different resource to create the ultimate
55   * response, or to include the output of another resource in the response
56   * from this resource.  This implementation allows application level servlets
57   * to wrap the request and/or response objects that are passed on to the
58   * called resource, as long as the wrapping classes extend
59   * <code>javax.servlet.ServletRequestWrapper</code> and
60   * <code>javax.servlet.ServletResponseWrapper</code>.
61   *
62   * @author Craig R. McClanahan
63   * @version $Revision: 303947 $ $Date: 2005-06-09 01:50:26 -0400 (Thu, 09 Jun 2005) $
64   */
65  
66  final class ApplicationDispatcher
67      implements RequestDispatcher {
68  
69  
70      protected class PrivilegedForward implements PrivilegedExceptionAction {
71          private ServletRequest request;
72          private ServletResponse response;
73  
74          PrivilegedForward(ServletRequest request, ServletResponse response)
75          {
76              this.request = request;
77              this.response = response;
78          }
79  
80          public Object run() throws java.lang.Exception {
81              doForward(request,response);
82              return null;
83          }
84      }
85  
86      protected class PrivilegedInclude implements PrivilegedExceptionAction {
87          private ServletRequest request;
88          private ServletResponse response;
89  
90          PrivilegedInclude(ServletRequest request, ServletResponse response)
91          {
92              this.request = request;
93              this.response = response;
94          }
95  
96          public Object run() throws ServletException, IOException {
97              doInclude(request,response);
98              return null;
99          }
100     }
101 
102     // ----------------------------------------------------------- Constructors
103 
104 
105     /**
106      * Construct a new instance of this class, configured according to the
107      * specified parameters.  If both servletPath and pathInfo are
108      * <code>null</code>, it will be assumed that this RequestDispatcher
109      * was acquired by name, rather than by path.
110      *
111      * @param wrapper The Wrapper associated with the resource that will
112      *  be forwarded to or included (required)
113      * @param requestURI The request URI to this resource (if any)
114      * @param servletPath The revised servlet path to this resource (if any)
115      * @param pathInfo The revised extra path information to this resource
116      *  (if any)
117      * @param queryString Query string parameters included with this request
118      *  (if any)
119      * @param name Servlet name (if a named dispatcher was created)
120      *  else <code>null</code>
121      */
122     public ApplicationDispatcher
123         (Wrapper wrapper, String requestURI, String servletPath,
124          String pathInfo, String queryString, String name) {
125 
126         super();
127 
128         // Save all of our configuration parameters
129         this.wrapper = wrapper;
130         this.context = (Context) wrapper.getParent();
131         this.requestURI = requestURI;
132         this.servletPath = servletPath;
133         this.origServletPath = servletPath;
134         this.pathInfo = pathInfo;
135         this.queryString = queryString;
136         this.name = name;
137         if (wrapper instanceof StandardWrapper)
138             this.support = ((StandardWrapper) wrapper).getInstanceSupport();
139         else
140             this.support = new InstanceSupport(wrapper);
141 
142         if ( log.isDebugEnabled() )
143             log.debug("servletPath=" + this.servletPath + ", pathInfo=" +
144                 this.pathInfo + ", queryString=" + queryString +
145                 ", name=" + this.name);
146 
147     }
148 
149 
150     // ----------------------------------------------------- Instance Variables
151 
152     private static Log log = LogFactory.getLog(ApplicationDispatcher.class);
153 
154     /**
155      * The request specified by the dispatching application.
156      */
157     private ServletRequest appRequest = null;
158 
159 
160     /**
161      * The response specified by the dispatching application.
162      */
163     private ServletResponse appResponse = null;
164 
165 
166     /**
167      * The Context this RequestDispatcher is associated with.
168      */
169     private Context context = null;
170 
171 
172     /**
173      * Are we performing an include() instead of a forward()?
174      */
175     private boolean including = false;
176 
177 
178     /**
179      * Descriptive information about this implementation.
180      */
181     private static final String info =
182         "org.apache.catalina.core.ApplicationDispatcher/1.0";
183 
184 
185     /**
186      * The servlet name for a named dispatcher.
187      */
188     private String name = null;
189 
190 
191     /**
192      * The outermost request that will be passed on to the invoked servlet.
193      */
194     private ServletRequest outerRequest = null;
195 
196 
197     /**
198      * The outermost response that will be passed on to the invoked servlet.
199      */
200     private ServletResponse outerResponse = null;
201 
202 
203     /**
204      * The extra path information for this RequestDispatcher.
205      */
206     private String pathInfo = null;
207 
208 
209     /**
210      * The query string parameters for this RequestDispatcher.
211      */
212     private String queryString = null;
213 
214 
215     /**
216      * The request URI for this RequestDispatcher.
217      */
218     private String requestURI = null;
219 
220     /**
221      * The servlet path for this RequestDispatcher.
222      */
223     private String servletPath = null;
224 
225     private String origServletPath = null;
226     
227     /**
228      * The StringManager for this package.
229      */
230     private static final StringManager sm =
231       StringManager.getManager(Constants.Package);
232 
233 
234     /**
235      * The InstanceSupport instance associated with our Wrapper (used to
236      * send "before dispatch" and "after dispatch" events.
237      */
238     private InstanceSupport support = null;
239 
240 
241     /**
242      * The Wrapper associated with the resource that will be forwarded to
243      * or included.
244      */
245     private Wrapper wrapper = null;
246 
247 
248     /**
249      * The request wrapper we have created and installed (if any).
250      */
251     private ServletRequest wrapRequest = null;
252 
253 
254     /**
255      * The response wrapper we have created and installed (if any).
256      */
257     private ServletResponse wrapResponse = null;
258 
259 
260     // ------------------------------------------------------------- Properties
261 
262 
263     /**
264      * Return the descriptive information about this implementation.
265      */
266     public String getInfo() {
267 
268         return (info);
269 
270     }
271 
272 
273     // --------------------------------------------------------- Public Methods
274 
275 
276     /**
277      * Forward this request and response to another resource for processing.
278      * Any runtime exception, IOException, or ServletException thrown by the
279      * called servlet will be propogated to the caller.
280      *
281      * @param request The servlet request to be forwarded
282      * @param response The servlet response to be forwarded
283      *
284      * @exception IOException if an input/output error occurs
285      * @exception ServletException if a servlet exception occurs
286      */
287     public void forward(ServletRequest request, ServletResponse response)
288         throws ServletException, IOException
289     {
290         if (System.getSecurityManager() != null) {
291             try {
292                 PrivilegedForward dp = new PrivilegedForward(request,response);
293                 AccessController.doPrivileged(dp);
294             } catch (PrivilegedActionException pe) {
295                 Exception e = pe.getException();
296                 if (e instanceof ServletException)
297                     throw (ServletException) e;
298                 throw (IOException) e;
299             }
300         } else {
301             doForward(request,response);
302         }
303     }
304 
305     private void doForward(ServletRequest request, ServletResponse response)
306         throws ServletException, IOException
307     {
308         
309         // Reset any output that has been buffered, but keep headers/cookies
310         if (response.isCommitted()) {
311             if ( log.isDebugEnabled() )
312                 log.debug("  Forward on committed response --> ISE");
313             throw new IllegalStateException
314                 (sm.getString("applicationDispatcher.forward.ise"));
315         }
316         try {
317             response.resetBuffer();
318         } catch (IllegalStateException e) {
319             if ( log.isDebugEnabled() )
320                 log.debug("  Forward resetBuffer() returned ISE: " + e);
321             throw e;
322         }
323 
324         // Set up to handle the specified request and response
325         setup(request, response, false);
326 
327         // Identify the HTTP-specific request and response objects (if any)
328         HttpServletRequest hrequest = null;
329         if (request instanceof HttpServletRequest)
330             hrequest = (HttpServletRequest) request;
331         HttpServletResponse hresponse = null;
332         if (response instanceof HttpServletResponse)
333             hresponse = (HttpServletResponse) response;
334 
335         // Handle a non-HTTP forward by passing the existing request/response
336         if ((hrequest == null) || (hresponse == null)) {
337 
338             if ( log.isDebugEnabled() )
339                 log.debug(" Non-HTTP Forward");
340             
341             processRequest(hrequest,hresponse);
342 
343         }
344 
345         // Handle an HTTP named dispatcher forward
346         else if ((servletPath == null) && (pathInfo == null)) {
347 
348             if ( log.isDebugEnabled() )
349                 log.debug(" Named Dispatcher Forward");
350             
351             ApplicationHttpRequest wrequest =
352                 (ApplicationHttpRequest) wrapRequest();
353             wrequest.setRequestURI(hrequest.getRequestURI());
354             wrequest.setContextPath(hrequest.getContextPath());
355             wrequest.setServletPath(hrequest.getServletPath());
356             wrequest.setPathInfo(hrequest.getPathInfo());
357             wrequest.setQueryString(hrequest.getQueryString());
358 
359             processRequest(request,response);
360 
361             wrequest.recycle();
362             unwrapRequest();
363 
364         }
365 
366         // Handle an HTTP path-based forward
367         else {
368 
369             if ( log.isDebugEnabled() )
370                 log.debug(" Path Based Forward");
371 
372             ApplicationHttpRequest wrequest =
373                 (ApplicationHttpRequest) wrapRequest();
374             String contextPath = context.getPath();
375 
376             if (hrequest.getAttribute(Globals.FORWARD_REQUEST_URI_ATTR) == null) {
377                 wrequest.setAttribute(Globals.FORWARD_REQUEST_URI_ATTR,
378                                       hrequest.getRequestURI());
379                 wrequest.setAttribute(Globals.FORWARD_CONTEXT_PATH_ATTR,
380                                       hrequest.getContextPath());
381                 wrequest.setAttribute(Globals.FORWARD_SERVLET_PATH_ATTR,
382                                       hrequest.getServletPath());
383                 wrequest.setAttribute(Globals.FORWARD_PATH_INFO_ATTR,
384                                       hrequest.getPathInfo());
385                 wrequest.setAttribute(Globals.FORWARD_QUERY_STRING_ATTR,
386                                       hrequest.getQueryString());
387             }
388  
389             wrequest.setContextPath(contextPath);
390             wrequest.setRequestURI(requestURI);
391             wrequest.setServletPath(servletPath);
392             wrequest.setPathInfo(pathInfo);
393             if (queryString != null) {
394                 wrequest.setQueryString(queryString);
395                 wrequest.setQueryParams(queryString);
396             }
397 
398             processRequest(request,response);
399 
400             wrequest.recycle();
401             unwrapRequest();
402 
403         }
404 
405         // This is not a real close in order to support error processing
406         if ( log.isDebugEnabled() )
407             log.debug(" Disabling the response for futher output");
408 
409         if  (response instanceof ResponseFacade) {
410             ((ResponseFacade) response).finish();
411         } else {
412             // Servlet SRV.6.2.2. The Resquest/Response may have been wrapped
413             // and may no longer be instance of RequestFacade 
414             if (log.isDebugEnabled()){
415                 log.debug( " The Response is vehiculed using a wrapper: " 
416                            + response.getClass().getName() );
417             }
418 
419             // Close anyway
420             try {
421                 PrintWriter writer = response.getWriter();
422                 writer.close();
423             } catch (IllegalStateException e) {
424                 try {
425                     ServletOutputStream stream = response.getOutputStream();
426                     stream.close();
427                 } catch (IllegalStateException f) {
428                     ;
429                 } catch (IOException f) {
430                     ;
431                 }
432             } catch (IOException e) {
433                 ;
434             }
435         }
436 
437     }
438 
439     
440 
441     /**
442      * Prepare the request based on the filter configuration.
443      * @param request The servlet request we are processing
444      * @param response The servlet response we are creating
445      *
446      * @exception IOException if an input/output error occurs
447      * @exception ServletException if a servlet error occurs
448      */
449     private void processRequest(ServletRequest request, 
450                                 ServletResponse response)
451         throws IOException, ServletException {
452                 
453         Integer disInt = (Integer) request.getAttribute
454             (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR);
455         if (disInt != null) {
456             if (disInt.intValue() != ApplicationFilterFactory.ERROR) {
457                 outerRequest.setAttribute
458                     (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
459                      origServletPath);
460                 outerRequest.setAttribute
461                     (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
462                      new Integer(ApplicationFilterFactory.FORWARD));
463                 invoke(outerRequest, response);
464             } else {
465                 invoke(outerRequest, response);
466             }
467         }
468 
469     }
470     
471     
472     
473     /**
474      * Include the response from another resource in the current response.
475      * Any runtime exception, IOException, or ServletException thrown by the
476      * called servlet will be propogated to the caller.
477      *
478      * @param request The servlet request that is including this one
479      * @param response The servlet response to be appended to
480      *
481      * @exception IOException if an input/output error occurs
482      * @exception ServletException if a servlet exception occurs
483      */
484     public void include(ServletRequest request, ServletResponse response)
485         throws ServletException, IOException
486     {
487         if (System.getSecurityManager() != null) {
488             try {
489                 PrivilegedInclude dp = new PrivilegedInclude(request,response);
490                 AccessController.doPrivileged(dp);
491             } catch (PrivilegedActionException pe) {
492                 Exception e = pe.getException();
493 
494                 if (e instanceof ServletException)
495                     throw (ServletException) e;
496                 throw (IOException) e;
497             }
498         } else {
499             doInclude(request,response);
500         }
501     }
502 
503     private void doInclude(ServletRequest request, ServletResponse response)
504         throws ServletException, IOException
505     {
506         // Set up to handle the specified request and response
507         setup(request, response, true);
508 
509         // Create a wrapped response to use for this request
510         // ServletResponse wresponse = null;
511         ServletResponse wresponse = wrapResponse();
512 
513         // Handle a non-HTTP include
514         if (!(request instanceof HttpServletRequest) ||
515             !(response instanceof HttpServletResponse)) {
516 
517             if ( log.isDebugEnabled() )
518                 log.debug(" Non-HTTP Include");
519             request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
520                                              new Integer(ApplicationFilterFactory.INCLUDE));
521             request.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
522             invoke(request, outerResponse);
523         }
524 
525         // Handle an HTTP named dispatcher include
526         else if (name != null) {
527 
528             if ( log.isDebugEnabled() )
529                 log.debug(" Named Dispatcher Include");
530 
531             ApplicationHttpRequest wrequest =
532                 (ApplicationHttpRequest) wrapRequest();
533             wrequest.setAttribute(Globals.NAMED_DISPATCHER_ATTR, name);
534             if (servletPath != null)
535                 wrequest.setServletPath(servletPath);
536             wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
537                                              new Integer(ApplicationFilterFactory.INCLUDE));
538             wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
539             invoke(outerRequest, outerResponse);
540 
541             wrequest.recycle();
542         }
543 
544         // Handle an HTTP path based include
545         else {
546 
547             if ( log.isDebugEnabled() )
548                 log.debug(" Path Based Include");
549 
550             ApplicationHttpRequest wrequest =
551                 (ApplicationHttpRequest) wrapRequest();
552             String contextPath = context.getPath();
553             if (requestURI != null)
554                 wrequest.setAttribute(Globals.INCLUDE_REQUEST_URI_ATTR,
555                                       requestURI);
556             if (contextPath != null)
557                 wrequest.setAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR,
558                                       contextPath);
559             if (servletPath != null)
560                 wrequest.setAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR,
561                                       servletPath);
562             if (pathInfo != null)
563                 wrequest.setAttribute(Globals.INCLUDE_PATH_INFO_ATTR,
564                                       pathInfo);
565             if (queryString != null) {
566                 wrequest.setAttribute(Globals.INCLUDE_QUERY_STRING_ATTR,
567                                       queryString);
568                 wrequest.setQueryParams(queryString);
569             }
570             
571             wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
572                                              new Integer(ApplicationFilterFactory.INCLUDE));
573             wrequest.setAttribute(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR, origServletPath);
574             invoke(outerRequest, outerResponse);
575 
576             wrequest.recycle();
577         }
578 
579     }
580 
581 
582     // -------------------------------------------------------- Private Methods
583 
584 
585     /**
586      * Ask the resource represented by this RequestDispatcher to process
587      * the associated request, and create (or append to) the associated
588      * response.
589      * <p>
590      * <strong>IMPLEMENTATION NOTE</strong>: This implementation assumes
591      * that no filters are applied to a forwarded or included resource,
592      * because they were already done for the original request.
593      *
594      * @param request The servlet request we are processing
595      * @param response The servlet response we are creating
596      *
597      * @exception IOException if an input/output error occurs
598      * @exception ServletException if a servlet error occurs
599      */
600     private void invoke(ServletRequest request, ServletResponse response)
601             throws IOException, ServletException {
602 
603         // Checking to see if the context classloader is the current context
604         // classloader. If it's not, we're saving it, and setting the context
605         // classloader to the Context classloader
606         ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
607         ClassLoader contextClassLoader = context.getLoader().getClassLoader();
608 
609         if (oldCCL != contextClassLoader) {
610             Thread.currentThread().setContextClassLoader(contextClassLoader);
611         } else {
612             oldCCL = null;
613         }
614 
615         // Initialize local variables we may need
616         HttpServletRequest hrequest = (HttpServletRequest) request;
617         HttpServletResponse hresponse = (HttpServletResponse) response;
618         Servlet servlet = null;
619         IOException ioException = null;
620         ServletException servletException = null;
621         RuntimeException runtimeException = null;
622         boolean unavailable = false;
623 
624         // Check for the servlet being marked unavailable
625         if (wrapper.isUnavailable()) {
626             wrapper.getLogger().warn(
627                     sm.getString("applicationDispatcher.isUnavailable", 
628                     wrapper.getName()));
629             long available = wrapper.getAvailable();
630             if ((available > 0L) && (available < Long.MAX_VALUE))
631                 hresponse.setDateHeader("Retry-After", available);
632             hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
633                     .getString("applicationDispatcher.isUnavailable", wrapper
634                             .getName()));
635             unavailable = true;
636         }
637 
638         // Allocate a servlet instance to process this request
639         try {
640             if (!unavailable) {
641                 servlet = wrapper.allocate();
642             }
643         } catch (ServletException e) {
644             wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
645                              wrapper.getName()), StandardWrapper.getRootCause(e));
646             servletException = e;
647             servlet = null;
648         } catch (Throwable e) {
649             wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
650                              wrapper.getName()), e);
651             servletException = new ServletException
652                 (sm.getString("applicationDispatcher.allocateException",
653                               wrapper.getName()), e);
654             servlet = null;
655         }
656                 
657         // Get the FilterChain Here
658         ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
659         ApplicationFilterChain filterChain = factory.createFilterChain(request,
660                                                                 wrapper,servlet);
661         // Call the service() method for the allocated servlet instance
662         try {
663             String jspFile = wrapper.getJspFile();
664             if (jspFile != null)
665                 request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
666             else
667                 request.removeAttribute(Globals.JSP_FILE_ATTR);
668             support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
669                                       servlet, request, response);
670             // for includes/forwards
671             if ((servlet != null) && (filterChain != null)) {
672                filterChain.doFilter(request, response);
673              }
674             // Servlet Service Method is called by the FilterChain
675             request.removeAttribute(Globals.JSP_FILE_ATTR);
676             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
677                                       servlet, request, response);
678         } catch (ClientAbortException e) {
679             request.removeAttribute(Globals.JSP_FILE_ATTR);
680             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
681                                       servlet, request, response);
682             ioException = e;
683         } catch (IOException e) {
684             request.removeAttribute(Globals.JSP_FILE_ATTR);
685             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
686                                       servlet, request, response);
687             wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
688                              wrapper.getName()), e);
689             ioException = e;
690         } catch (UnavailableException e) {
691             request.removeAttribute(Globals.JSP_FILE_ATTR);
692             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
693                                       servlet, request, response);
694             wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
695                              wrapper.getName()), e);
696             servletException = e;
697             wrapper.unavailable(e);
698         } catch (ServletException e) {
699             request.removeAttribute(Globals.JSP_FILE_ATTR);
700             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
701                                       servlet, request, response);
702             Throwable rootCause = StandardWrapper.getRootCause(e);
703             if (!(rootCause instanceof ClientAbortException)) {
704                 wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
705                         wrapper.getName()), rootCause);
706             }
707             servletException = e;
708         } catch (RuntimeException e) {
709             request.removeAttribute(Globals.JSP_FILE_ATTR);
710             support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
711                                       servlet, request, response);
712             wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException",
713                              wrapper.getName()), e);
714             runtimeException = e;
715         }
716 
717         // Release the filter chain (if any) for this request
718         try {
719             if (filterChain != null)
720                 filterChain.release();
721         } catch (Throwable e) {
722             log.error(sm.getString("standardWrapper.releaseFilters",
723                              wrapper.getName()), e);
724           //FIXME Exception handling needs to be simpiler to what is in the StandardWrapperValue
725         }
726 
727         // Deallocate the allocated servlet instance
728         try {
729             if (servlet != null) {
730                 wrapper.deallocate(servlet);
731             }
732         } catch (ServletException e) {
733             wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException",
734                              wrapper.getName()), e);
735             servletException = e;
736         } catch (Throwable e) {
737             wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException",
738                              wrapper.getName()), e);
739             servletException = new ServletException
740                 (sm.getString("applicationDispatcher.deallocateException",
741                               wrapper.getName()), e);
742         }
743 
744         // Reset the old context class loader
745         if (oldCCL != null)
746             Thread.currentThread().setContextClassLoader(oldCCL);
747         
748         // Unwrap request/response if needed
749         // See Bugzilla 30949
750         unwrapRequest();
751         unwrapResponse();
752 
753         // Rethrow an exception if one was thrown by the invoked servlet
754         if (ioException != null)
755             throw ioException;
756         if (servletException != null)
757             throw servletException;
758         if (runtimeException != null)
759             throw runtimeException;
760 
761     }
762 
763 
764     /**
765      * Set up to handle the specified request and response
766      *
767      * @param request The servlet request specified by the caller
768      * @param response The servlet response specified by the caller
769      * @param including Are we performing an include() as opposed to
770      *  a forward()?
771      */
772     private void setup(ServletRequest request, ServletResponse response,
773                        boolean including) {
774 
775         this.appRequest = request;
776         this.appResponse = response;
777         this.outerRequest = request;
778         this.outerResponse = response;
779         this.including = including;
780 
781     }
782 
783 
784     /**
785      * Unwrap the request if we have wrapped it.
786      */
787     private void unwrapRequest() {
788 
789         if (wrapRequest == null)
790             return;
791 
792         ServletRequest previous = null;
793         ServletRequest current = outerRequest;
794         while (current != null) {
795 
796             // If we run into the container request we are done
797             if ((current instanceof Request)
798                 || (current instanceof RequestFacade))
799                 break;
800 
801             // Remove the current request if it is our wrapper
802             if (current == wrapRequest) {
803                 ServletRequest next =
804                   ((ServletRequestWrapper) current).getRequest();
805                 if (previous == null)
806                     outerRequest = next;
807                 else
808                     ((ServletRequestWrapper) previous).setRequest(next);
809                 break;
810             }
811 
812             // Advance to the next request in the chain
813             previous = current;
814             current = ((ServletRequestWrapper) current).getRequest();
815 
816         }
817 
818     }
819 
820 
821     /**
822      * Unwrap the response if we have wrapped it.
823      */
824     private void unwrapResponse() {
825 
826         if (wrapResponse == null)
827             return;
828 
829         ServletResponse previous = null;
830         ServletResponse current = outerResponse;
831         while (current != null) {
832 
833             // If we run into the container response we are done
834             if ((current instanceof Response)
835                 || (current instanceof ResponseFacade))
836                 break;
837 
838             // Remove the current response if it is our wrapper
839             if (current == wrapResponse) {
840                 ServletResponse next =
841                   ((ServletResponseWrapper) current).getResponse();
842                 if (previous == null)
843                     outerResponse = next;
844                 else
845                     ((ServletResponseWrapper) previous).setResponse(next);
846                 break;
847             }
848 
849             // Advance to the next response in the chain
850             previous = current;
851             current = ((ServletResponseWrapper) current).getResponse();
852 
853         }
854 
855     }
856 
857 
858     /**
859      * Create and return a request wrapper that has been inserted in the
860      * appropriate spot in the request chain.
861      */
862     private ServletRequest wrapRequest() {
863 
864         // Locate the request we should insert in front of
865         ServletRequest previous = null;
866         ServletRequest current = outerRequest;
867         while (current != null) {
868             if ("org.apache.catalina.servlets.InvokerHttpRequest".
869                 equals(current.getClass().getName()))
870                 break; // KLUDGE - Make nested RD.forward() using invoker work
871             if (!(current instanceof ServletRequestWrapper))
872                 break;
873             if (current instanceof ApplicationHttpRequest)
874                 break;
875             if (current instanceof ApplicationRequest)
876                 break;
877             if (current instanceof Request)
878                 break;
879             previous = current;
880             current = ((ServletRequestWrapper) current).getRequest();
881         }
882 
883         // Instantiate a new wrapper at this point and insert it in the chain
884         ServletRequest wrapper = null;
885         if ((current instanceof ApplicationHttpRequest) ||
886             (current instanceof Request) ||
887             (current instanceof HttpServletRequest)) {
888             // Compute a crossContext flag
889             HttpServletRequest hcurrent = (HttpServletRequest) current;
890             boolean crossContext = false;
891             if ((outerRequest instanceof ApplicationHttpRequest) ||
892                 (outerRequest instanceof Request) ||
893                 (outerRequest instanceof HttpServletRequest)) {
894                 HttpServletRequest houterRequest = 
895                     (HttpServletRequest) outerRequest;
896                 Object contextPath = houterRequest.getAttribute
897                     (Globals.INCLUDE_CONTEXT_PATH_ATTR);
898                 if (contextPath == null) {
899                     // Forward
900                     contextPath = houterRequest.getContextPath();
901                 }
902                 crossContext = !(context.getPath().equals(contextPath));
903             }
904             wrapper = new ApplicationHttpRequest
905                 (hcurrent, context, crossContext);
906         } else {
907             wrapper = new ApplicationRequest(current);
908         }
909         if (previous == null)
910             outerRequest = wrapper;
911         else
912             ((ServletRequestWrapper) previous).setRequest(wrapper);
913         wrapRequest = wrapper;
914         return (wrapper);
915 
916     }
917 
918 
919     /**
920      * Create and return a response wrapper that has been inserted in the
921      * appropriate spot in the response chain.
922      */
923     private ServletResponse wrapResponse() {
924 
925         // Locate the response we should insert in front of
926         ServletResponse previous = null;
927         ServletResponse current = outerResponse;
928         while (current != null) {
929             if (!(current instanceof ServletResponseWrapper))
930                 break;
931             if (current instanceof ApplicationHttpResponse)
932                 break;
933             if (current instanceof ApplicationResponse)
934                 break;
935             if (current instanceof Response)
936                 break;
937             previous = current;
938             current = ((ServletResponseWrapper) current).getResponse();
939         }
940 
941         // Instantiate a new wrapper at this point and insert it in the chain
942         ServletResponse wrapper = null;
943         if ((current instanceof ApplicationHttpResponse) ||
944             (current instanceof Response) ||
945             (current instanceof HttpServletResponse))
946             wrapper =
947                 new ApplicationHttpResponse((HttpServletResponse) current,
948                                             including);
949         else
950             wrapper = new ApplicationResponse(current, including);
951         if (previous == null)
952             outerResponse = wrapper;
953         else
954             ((ServletResponseWrapper) previous).setResponse(wrapper);
955         wrapResponse = wrapper;
956         return (wrapper);
957 
958     }
959 
960 
961 }