KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > winstone > RequestDispatcher


1 /*
2  * Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
3  * Distributed under the terms of either:
4  * - the common development and distribution license (CDDL), v1.0; or
5  * - the GNU Lesser General Public License, v2.1 or later
6  */

7 package winstone;
8
9 import java.io.IOException JavaDoc;
10 import java.util.ArrayList JavaDoc;
11 import java.util.List JavaDoc;
12 import java.util.Map JavaDoc;
13
14 import javax.servlet.ServletException JavaDoc;
15 import javax.servlet.ServletRequest JavaDoc;
16 import javax.servlet.ServletRequestWrapper JavaDoc;
17 import javax.servlet.ServletResponse JavaDoc;
18 import javax.servlet.ServletResponseWrapper JavaDoc;
19
20 /**
21  * This class implements both the RequestDispatcher and FilterChain components. On
22  * the first call to include() or forward(), it starts the filter chain execution
23  * if one exists. On the final doFilter() or if there is no chain, we call the include()
24  * or forward() again, and the servlet is executed.
25  *
26  * @author <a HREF="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
27  * @version $Id: RequestDispatcher.java,v 1.17 2006/08/25 14:27:03 rickknowles Exp $
28  */

29 public class RequestDispatcher implements javax.servlet.RequestDispatcher JavaDoc,
30         javax.servlet.FilterChain JavaDoc {
31     
32     static final String JavaDoc INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
33     static final String JavaDoc INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
34     static final String JavaDoc INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
35     static final String JavaDoc INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
36     static final String JavaDoc INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
37
38     static final String JavaDoc FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
39     static final String JavaDoc FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
40     static final String JavaDoc FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
41     static final String JavaDoc FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
42     static final String JavaDoc FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
43
44     static final String JavaDoc ERROR_STATUS_CODE = "javax.servlet.error.status_code";
45     static final String JavaDoc ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
46     static final String JavaDoc ERROR_MESSAGE = "javax.servlet.error.message";
47     static final String JavaDoc ERROR_EXCEPTION = "javax.servlet.error.exception";
48     static final String JavaDoc ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
49     static final String JavaDoc ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
50     
51     private WebAppConfiguration webAppConfig;
52     private ServletConfiguration servletConfig;
53     
54     private String JavaDoc servletPath;
55     private String JavaDoc pathInfo;
56     private String JavaDoc queryString;
57     private String JavaDoc requestURI;
58     
59     private Integer JavaDoc errorStatusCode;
60     private Throwable JavaDoc errorException;
61     private String JavaDoc errorSummaryMessage;
62     
63     private AuthenticationHandler authHandler;
64     
65     private Mapping forwardFilterPatterns[];
66     private Mapping includeFilterPatterns[];
67     private FilterConfiguration matchingFilters[];
68     private int matchingFiltersEvaluated;
69     
70     private Boolean JavaDoc doInclude;
71     private boolean isErrorDispatch;
72     private boolean useRequestAttributes;
73     
74     private WebAppConfiguration includedWebAppConfig;
75     private ServletConfiguration includedServletConfig;
76     
77     /**
78      * Constructor. This initializes the filter chain and sets up the details
79      * needed to handle a servlet excecution, such as security constraints,
80      * filters, etc.
81      */

82     public RequestDispatcher(WebAppConfiguration webAppConfig, ServletConfiguration servletConfig) {
83         this.servletConfig = servletConfig;
84         this.webAppConfig = webAppConfig;
85
86         this.matchingFiltersEvaluated = 0;
87     }
88
89     public void setForNamedDispatcher(Mapping forwardFilterPatterns[],
90             Mapping includeFilterPatterns[]) {
91         this.forwardFilterPatterns = forwardFilterPatterns;
92         this.includeFilterPatterns = includeFilterPatterns;
93         this.matchingFilters = null; // set after the call to forward or include
94
this.useRequestAttributes = false;
95         this.isErrorDispatch = false;
96     }
97
98     public void setForURLDispatcher(String JavaDoc servletPath, String JavaDoc pathInfo,
99             String JavaDoc queryString, String JavaDoc requestURIInsideWebapp,
100             Mapping forwardFilterPatterns[], Mapping includeFilterPatterns[]) {
101         this.servletPath = servletPath;
102         this.pathInfo = pathInfo;
103         this.queryString = queryString;
104         this.requestURI = requestURIInsideWebapp;
105
106         this.forwardFilterPatterns = forwardFilterPatterns;
107         this.includeFilterPatterns = includeFilterPatterns;
108         this.matchingFilters = null; // set after the call to forward or include
109
this.useRequestAttributes = true;
110         this.isErrorDispatch = false;
111     }
112
113     public void setForErrorDispatcher(String JavaDoc servletPath, String JavaDoc pathInfo,
114             String JavaDoc queryString, int statusCode, String JavaDoc summaryMessage,
115             Throwable JavaDoc exception, String JavaDoc errorHandlerURI,
116             Mapping errorFilterPatterns[]) {
117         this.servletPath = servletPath;
118         this.pathInfo = pathInfo;
119         this.queryString = queryString;
120         this.requestURI = errorHandlerURI;
121
122         this.errorStatusCode = new Integer JavaDoc(statusCode);
123         this.errorException = exception;
124         this.errorSummaryMessage = summaryMessage;
125         this.matchingFilters = getMatchingFilters(errorFilterPatterns, this.webAppConfig,
126                 servletPath + (pathInfo == null ? "" : pathInfo),
127                 getName(), "ERROR", (servletPath != null));
128         this.useRequestAttributes = true;
129         this.isErrorDispatch = true;
130     }
131
132     public void setForInitialDispatcher(String JavaDoc servletPath, String JavaDoc pathInfo,
133             String JavaDoc queryString, String JavaDoc requestURIInsideWebapp, Mapping requestFilterPatterns[],
134             AuthenticationHandler authHandler) {
135         this.servletPath = servletPath;
136         this.pathInfo = pathInfo;
137         this.queryString = queryString;
138         this.requestURI = requestURIInsideWebapp;
139         this.authHandler = authHandler;
140         this.matchingFilters = getMatchingFilters(requestFilterPatterns, this.webAppConfig,
141                 servletPath + (pathInfo == null ? "" : pathInfo),
142                 getName(), "REQUEST", (servletPath != null));
143         this.useRequestAttributes = false;
144         this.isErrorDispatch = false;
145     }
146
147     public String JavaDoc getName() {
148         return this.servletConfig.getServletName();
149     }
150
151     /**
152      * Includes the execution of a servlet into the current request
153      *
154      * Note this method enters itself twice: once with the initial call, and once again
155      * when all the filters have completed.
156      */

157     public void include(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
158             throws ServletException JavaDoc, IOException JavaDoc {
159
160         // On the first call, log and initialise the filter chain
161
if (this.doInclude == null) {
162             Logger.log(Logger.DEBUG, Launcher.RESOURCES,
163                     "RequestDispatcher.IncludeMessage", new String JavaDoc[] {
164                             getName(), this.requestURI });
165             
166             WinstoneRequest wr = getUnwrappedRequest(request);
167             // Add the query string to the included query string stack
168
wr.addIncludeQueryParameters(this.queryString);
169             
170             // Set request attributes
171
if (useRequestAttributes) {
172                 wr.addIncludeAttributes(this.webAppConfig.getContextPath() + this.requestURI,
173                         this.webAppConfig.getContextPath(), this.servletPath, this.pathInfo, this.queryString);
174             }
175             // Add another include buffer to the response stack
176
WinstoneResponse wresp = getUnwrappedResponse(response);
177             wresp.startIncludeBuffer();
178             
179             this.includedServletConfig = wr.getServletConfig();
180             this.includedWebAppConfig = wr.getWebAppConfig();
181             wr.setServletConfig(this.servletConfig);
182             wr.setWebAppConfig(this.webAppConfig);
183             wresp.setWebAppConfig(this.webAppConfig);
184             
185             this.doInclude = Boolean.TRUE;
186         }
187         
188         if (this.matchingFilters == null) {
189             this.matchingFilters = getMatchingFilters(this.includeFilterPatterns, this.webAppConfig,
190                     this.servletPath + (this.pathInfo == null ? "" : this.pathInfo),
191                     getName(), "INCLUDE", (this.servletPath != null));
192         }
193         try {
194             // Make sure the filter chain is exhausted first
195
if (this.matchingFiltersEvaluated < this.matchingFilters.length) {
196                 doFilter(request, response);
197                 finishInclude(request, response);
198             } else {
199                 try {
200                     this.servletConfig.execute(request, response,
201                             this.webAppConfig.getContextPath() + this.requestURI);
202                 } finally {
203                     if (this.matchingFilters.length == 0) {
204                         finishInclude(request, response);
205                     }
206                 }
207             }
208         } catch (Throwable JavaDoc err) {
209             finishInclude(request, response);
210             if (err instanceof ServletException JavaDoc) {
211                 throw (ServletException JavaDoc) err;
212             } else if (err instanceof IOException JavaDoc) {
213                 throw (IOException JavaDoc) err;
214             } else if (err instanceof Error JavaDoc) {
215                 throw (Error JavaDoc) err;
216             } else {
217                 throw (RuntimeException JavaDoc) err;
218             }
219         }
220     }
221
222     private void finishInclude(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
223             throws IOException JavaDoc {
224         WinstoneRequest wr = getUnwrappedRequest(request);
225         wr.removeIncludeQueryString();
226         
227         // Set request attributes
228
if (useRequestAttributes) {
229             wr.removeIncludeAttributes();
230         }
231         // Remove the include buffer from the response stack
232
WinstoneResponse wresp = getUnwrappedResponse(response);
233         wresp.finishIncludeBuffer();
234         
235         if (this.includedServletConfig != null) {
236             wr.setServletConfig(this.includedServletConfig);
237             this.includedServletConfig = null;
238         }
239         
240         if (this.includedWebAppConfig != null) {
241             wr.setWebAppConfig(this.includedWebAppConfig);
242             wresp.setWebAppConfig(this.includedWebAppConfig);
243             this.includedWebAppConfig = null;
244         }
245     }
246     
247     /**
248      * Forwards to another servlet, and when it's finished executing that other
249      * servlet, cut off execution.
250      *
251      * Note this method enters itself twice: once with the initial call, and once again
252      * when all the filters have completed.
253      */

254     public void forward(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
255             throws ServletException JavaDoc, IOException JavaDoc {
256
257         // Only on the first call to forward, we should set any forwarding attributes
258
if (this.doInclude == null) {
259             Logger.log(Logger.DEBUG, Launcher.RESOURCES,
260                     "RequestDispatcher.ForwardMessage", new String JavaDoc[] {
261                     getName(), this.requestURI });
262             if (response.isCommitted()) {
263                 throw new IllegalStateException JavaDoc(Launcher.RESOURCES.getString(
264                         "RequestDispatcher.ForwardCommitted"));
265             }
266             
267             WinstoneRequest req = getUnwrappedRequest(request);
268             WinstoneResponse rsp = getUnwrappedResponse(response);
269             
270             // Clear the include stack if one has been accumulated
271
rsp.resetBuffer();
272             req.clearIncludeStackForForward();
273             rsp.clearIncludeStackForForward();
274             
275             // Set request attributes (because it's the first step in the filter chain of a forward or error)
276
if (useRequestAttributes) {
277                 req.setAttribute(FORWARD_REQUEST_URI, req.getRequestURI());
278                 req.setAttribute(FORWARD_CONTEXT_PATH, req.getContextPath());
279                 req.setAttribute(FORWARD_SERVLET_PATH, req.getServletPath());
280                 req.setAttribute(FORWARD_PATH_INFO, req.getPathInfo());
281                 req.setAttribute(FORWARD_QUERY_STRING, req.getQueryString());
282                 
283                 if (this.isErrorDispatch) {
284                     req.setAttribute(ERROR_REQUEST_URI, req.getRequestURI());
285                     req.setAttribute(ERROR_STATUS_CODE, this.errorStatusCode);
286                     req.setAttribute(ERROR_MESSAGE,
287                             errorSummaryMessage != null ? errorSummaryMessage : "");
288                     if (req.getServletConfig() != null) {
289                         req.setAttribute(ERROR_SERVLET_NAME, req.getServletConfig().getServletName());
290                     }
291                     
292                     if (this.errorException != null) {
293                         req.setAttribute(ERROR_EXCEPTION_TYPE, this.errorException.getClass());
294                         req.setAttribute(ERROR_EXCEPTION, this.errorException);
295                     }
296                     
297                     // Revert back to the original request and response
298
rsp.setErrorStatusCode(this.errorStatusCode.intValue());
299                     request = req;
300                     response = rsp;
301                 }
302             }
303             
304             req.setServletPath(this.servletPath);
305             req.setPathInfo(this.pathInfo);
306             req.setRequestURI(this.webAppConfig.getContextPath() + this.requestURI);
307             req.setForwardQueryString(this.queryString);
308             req.setWebAppConfig(this.webAppConfig);
309             req.setServletConfig(this.servletConfig);
310             req.setRequestAttributeListeners(this.webAppConfig.getRequestAttributeListeners());
311             
312             rsp.setWebAppConfig(this.webAppConfig);
313
314             // Forwards haven't set up the filter pattern set yet
315
if (this.matchingFilters == null) {
316                 this.matchingFilters = getMatchingFilters(this.forwardFilterPatterns, this.webAppConfig,
317                         this.servletPath + (this.pathInfo == null ? "" : this.pathInfo),
318                         getName(), "FORWARD", (this.servletPath != null));
319             }
320             
321             // Otherwise we are an initial or error dispatcher, so check security if initial -
322
// if we should not continue, return
323
else if (!this.isErrorDispatch && !continueAfterSecurityCheck(request, response)) {
324                 return;
325             }
326             
327             this.doInclude = Boolean.FALSE;
328         }
329
330         // Make sure the filter chain is exhausted first
331
if (this.matchingFiltersEvaluated < this.matchingFilters.length) {
332             doFilter(request, response);
333         } else {
334             this.servletConfig.execute(request, response, this.webAppConfig.getContextPath() + this.requestURI);
335             WinstoneResponse rsp = getUnwrappedResponse(response);
336             rsp.flushBuffer();
337             rsp.getWinstoneOutputStream().setClosed(true);
338         }
339     }
340
341     private boolean continueAfterSecurityCheck(ServletRequest JavaDoc request,
342             ServletResponse JavaDoc response) throws IOException JavaDoc, ServletException JavaDoc {
343         // Evaluate security constraints
344
if (this.authHandler != null) {
345             return this.authHandler.processAuthentication(request, response,
346                     this.servletPath + (this.pathInfo == null ? "" : this.pathInfo));
347         } else {
348             return true;
349         }
350     }
351
352     /**
353      * Handles the processing of the chain of filters, so that we process them
354      * all, then pass on to the main servlet
355      */

356     public void doFilter(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
357             throws ServletException JavaDoc, IOException JavaDoc {
358         // Loop through the filter mappings until we hit the end
359
while (this.matchingFiltersEvaluated < this.matchingFilters.length) {
360             
361             FilterConfiguration filter = this.matchingFilters[this.matchingFiltersEvaluated++];
362             Logger.log(Logger.DEBUG, Launcher.RESOURCES,
363                     "RequestDispatcher.ExecutingFilter", filter.getFilterName());
364             filter.execute(request, response, this);
365             return;
366         }
367
368         // Forward / include as requested in the beginning
369
if (this.doInclude == null)
370             return; // will never happen, because we can't call doFilter before forward/include
371
else if (this.doInclude.booleanValue())
372             include(request, response);
373         else
374             forward(request, response);
375     }
376
377     /**
378      * Caches the filter matching, so that if the same URL is requested twice, we don't recalculate the
379      * filter matching every time.
380      */

381     private static FilterConfiguration[] getMatchingFilters(Mapping filterPatterns[],
382             WebAppConfiguration webAppConfig, String JavaDoc fullPath, String JavaDoc servletName,
383             String JavaDoc filterChainType, boolean isURLBasedMatch) {
384         
385         String JavaDoc cacheKey = null;
386         if (isURLBasedMatch) {
387             cacheKey = filterChainType + ":URI:" + fullPath;
388         } else {
389             cacheKey = filterChainType + ":Servlet:" + servletName;
390         }
391         FilterConfiguration matchingFilters[] = null;
392         Map JavaDoc cache = webAppConfig.getFilterMatchCache();
393         synchronized (cache) {
394             matchingFilters = (FilterConfiguration []) cache.get(cacheKey);
395             if (matchingFilters == null) {
396                 Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
397                         "RequestDispatcher.CalcFilterChain", cacheKey);
398                 List JavaDoc outFilters = new ArrayList JavaDoc();
399                 for (int n = 0; n < filterPatterns.length; n++) {
400                     // Get the pattern and eval it, bumping up the eval'd count
401
Mapping filterPattern = filterPatterns[n];
402
403                     // If the servlet name matches this name, execute it
404
if ((filterPattern.getLinkName() != null)
405                             && (filterPattern.getLinkName().equals(servletName) ||
406                                     filterPattern.getLinkName().equals("*"))) {
407                         outFilters.add(webAppConfig.getFilters().get(filterPattern.getMappedTo()));
408                     }
409                     // If the url path matches this filters mappings
410
else if ((filterPattern.getLinkName() == null) && isURLBasedMatch
411                             && filterPattern.match(fullPath, null, null)) {
412                         outFilters.add(webAppConfig.getFilters().get(filterPattern.getMappedTo()));
413                     }
414                 }
415                 matchingFilters = (FilterConfiguration []) outFilters.toArray(new FilterConfiguration[0]);
416                 cache.put(cacheKey, matchingFilters);
417             } else {
418                 Logger.log(Logger.FULL_DEBUG, Launcher.RESOURCES,
419                         "RequestDispatcher.UseCachedFilterChain", cacheKey);
420             }
421         }
422         return matchingFilters;
423     }
424     
425     /**
426      * Unwrap back to the original container allocated request object
427      */

428     protected WinstoneRequest getUnwrappedRequest(ServletRequest JavaDoc request) {
429         ServletRequest JavaDoc workingRequest = request;
430         while (workingRequest instanceof ServletRequestWrapper JavaDoc) {
431             workingRequest = ((ServletRequestWrapper JavaDoc) workingRequest).getRequest();
432         }
433         return (WinstoneRequest) workingRequest;
434     }
435
436     /**
437      * Unwrap back to the original container allocated response object
438      */

439     protected WinstoneResponse getUnwrappedResponse(ServletResponse JavaDoc response) {
440         ServletResponse JavaDoc workingResponse = response;
441         while (workingResponse instanceof ServletResponseWrapper JavaDoc) {
442             workingResponse = ((ServletResponseWrapper JavaDoc) workingResponse).getResponse();
443         }
444         return (WinstoneResponse) workingResponse;
445     }
446 }
447
Popular Tags