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