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 }