View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  import java.util.Collections;
18  import java.util.Enumeration;
19  import java.util.HashSet;
20  import java.util.Iterator;
21  import java.util.Map;
22  
23  import javax.servlet.RequestDispatcher;
24  import javax.servlet.ServletException;
25  import javax.servlet.ServletRequest;
26  import javax.servlet.ServletResponse;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletResponse;
29  
30  import org.eclipse.jetty.server.handler.ContextHandler;
31  import org.eclipse.jetty.util.Attributes;
32  import org.eclipse.jetty.util.LazyList;
33  import org.eclipse.jetty.util.MultiMap;
34  import org.eclipse.jetty.util.UrlEncoded;
35  
36  /* ------------------------------------------------------------ */
37  /** Servlet RequestDispatcher.
38   * 
39   * 
40   */
41  public class Dispatcher implements RequestDispatcher
42  {
43      public static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
44      public static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
45      public static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
46      public static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
47      public static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
48      public static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
49      public static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
50      public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
51      public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
52      public static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
53  
54      public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
55      public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
56      public static final String ERROR_MESSAGE = "javax.servlet.error.message";
57      public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
58      public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
59      public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
60  
61      /** Dispatch include attribute names */
62      public final static String __INCLUDE_PREFIX="javax.servlet.include.";
63  
64      /** Dispatch include attribute names */
65      public final static String __FORWARD_PREFIX="javax.servlet.forward.";
66  
67      /** JSP attributes */
68      public final static String __JSP_FILE="org.apache.catalina.jsp_file";
69  
70      /* ------------------------------------------------------------ */
71      private final ContextHandler _contextHandler;
72      private final String _uri;
73      private final String _path;
74      private final String _dQuery;
75      private final String _named;
76      
77      /* ------------------------------------------------------------ */
78      /**
79       * @param contextHandler
80       * @param uri
81       * @param pathInContext
82       * @param query
83       */
84      public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
85      {
86          _contextHandler=contextHandler;
87          _uri=uri;
88          _path=pathInContext;
89          _dQuery=query;
90          _named=null;
91      }
92  
93  
94      /* ------------------------------------------------------------ */
95      /** Constructor. 
96       * @param contextHandler
97       * @param name
98       */
99      public Dispatcher(ContextHandler contextHandler,String name)
100         throws IllegalStateException
101     {
102         _contextHandler=contextHandler;
103         _named=name;
104         _uri=null;
105         _path=null;
106         _dQuery=null;
107     }
108     
109     /* ------------------------------------------------------------ */
110     /* 
111      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
112      */
113     public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
114     {
115         forward(request, response, DispatcherType.FORWARD);
116     }
117     
118     /* ------------------------------------------------------------ */
119     /* 
120      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
121      */
122     public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
123     {
124         forward(request, response, DispatcherType.ERROR);
125     }
126     
127     /* ------------------------------------------------------------ */
128     /* 
129      * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
130      */
131     public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
132     {
133         Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
134         request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed
135         
136         if (!(request instanceof HttpServletRequest))
137             request = new ServletRequestHttpWrapper(request);
138         if (!(response instanceof HttpServletResponse))
139             response = new ServletResponseHttpWrapper(response);
140             
141         
142         // TODO - allow stream or writer????
143         
144         final DispatcherType old_type = baseRequest.getDispatcherType();
145         final Attributes old_attr=baseRequest.getAttributes();
146         MultiMap old_params=baseRequest.getParameters();
147         try
148         {
149             baseRequest.setDispatcherType(DispatcherType.INCLUDE);
150             baseRequest.getConnection().include();
151             if (_named!=null)
152                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
153             else 
154             {
155                 String query=_dQuery;
156                 
157                 if (query!=null)
158                 {
159                     // force parameter extraction
160                     if (old_params==null)
161                     {
162                         baseRequest.extractParameters();
163                         old_params=baseRequest.getParameters();
164                     }
165                     
166                     MultiMap parameters=new MultiMap();
167                     UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
168                     
169                     if (old_params!=null && old_params.size()>0)
170                     {
171                         // Merge parameters.
172                         Iterator iter = old_params.entrySet().iterator();
173                         while (iter.hasNext())
174                         {
175                             Map.Entry entry = (Map.Entry)iter.next();
176                             String name=(String)entry.getKey();
177                             Object values=entry.getValue();
178                             for (int i=0;i<LazyList.size(values);i++)
179                                 parameters.add(name, LazyList.get(values, i));
180                         }
181                     }
182                     baseRequest.setParameters(parameters);
183                 }
184                 
185                 IncludeAttributes attr = new IncludeAttributes(old_attr); 
186                 
187                 attr._requestURI=_uri;
188                 attr._contextPath=_contextHandler.getContextPath();
189                 attr._servletPath=null; // set by ServletHandler
190                 attr._pathInfo=_path;
191                 attr._query=query;
192                 
193                 baseRequest.setAttributes(attr);
194                 
195                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
196             }
197         }
198         finally
199         {
200             baseRequest.setAttributes(old_attr);
201             baseRequest.getConnection().included();
202             baseRequest.setParameters(old_params);
203             baseRequest.setDispatcherType(old_type);
204         }
205     }
206 
207     
208     /* ------------------------------------------------------------ */
209     /* 
210      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
211      */
212     protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
213     {
214         Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
215         Response base_response=baseRequest.getResponse();
216         response.resetBuffer();
217         base_response.fwdReset();
218         request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed
219 
220         if (!(request instanceof HttpServletRequest))
221             request = new ServletRequestHttpWrapper(request);
222         if (!(response instanceof HttpServletResponse))
223             response = new ServletResponseHttpWrapper(response);
224         
225         final boolean old_handled=baseRequest.isHandled();
226         final String old_uri=baseRequest.getRequestURI();
227         final String old_context_path=baseRequest.getContextPath();
228         final String old_servlet_path=baseRequest.getServletPath();
229         final String old_path_info=baseRequest.getPathInfo();
230         final String old_query=baseRequest.getQueryString();
231         final Attributes old_attr=baseRequest.getAttributes();
232         final DispatcherType old_type=baseRequest.getDispatcherType();
233         MultiMap<String> old_params=baseRequest.getParameters();
234         
235         try
236         {
237             baseRequest.setHandled(false);
238             baseRequest.setDispatcherType(dispatch);
239             
240             if (_named!=null)
241                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
242             else 
243             {
244                 
245                 // process any query string from the dispatch URL
246                 String query=_dQuery;
247                 if (query!=null)
248                 {
249                     // force parameter extraction
250                     if (old_params==null)
251                     {
252                         baseRequest.extractParameters();
253                         old_params=baseRequest.getParameters();
254                     }
255                     
256                     baseRequest.mergeQueryString(query);
257                 }
258                 
259                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
260                 
261                 //If we have already been forwarded previously, then keep using the established 
262                 //original value. Otherwise, this is the first forward and we need to establish the values.
263                 //Note: the established value on the original request for pathInfo and
264                 //for queryString is allowed to be null, but cannot be null for the other values.
265                 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
266                 {
267                     attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
268                     attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
269                     attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
270                     attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
271                     attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
272                 }
273                 else
274                 {
275                     attr._pathInfo=old_path_info;
276                     attr._query=old_query;
277                     attr._requestURI=old_uri;
278                     attr._contextPath=old_context_path;
279                     attr._servletPath=old_servlet_path;
280                 }     
281                 
282                 baseRequest.setRequestURI(_uri);
283                 baseRequest.setContextPath(_contextHandler.getContextPath());
284                 baseRequest.setServletPath(null);
285                 baseRequest.setPathInfo(_uri);
286                 baseRequest.setAttributes(attr);
287                 
288                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
289                 
290                 if (baseRequest.getResponse().isWriting())
291                 {
292                     try {response.getWriter().close();}
293                     catch(IllegalStateException e) 
294                     { 
295                         response.getOutputStream().close(); 
296                     }
297                 }
298                 else
299                 {
300                     try {response.getOutputStream().close();}
301                     catch(IllegalStateException e) 
302                     { 
303                         response.getWriter().close(); 
304                     }
305                 }
306             }
307         }
308         finally
309         {
310             baseRequest.setHandled(old_handled);
311             baseRequest.setRequestURI(old_uri);
312             baseRequest.setContextPath(old_context_path);
313             baseRequest.setServletPath(old_servlet_path);
314             baseRequest.setPathInfo(old_path_info);
315             baseRequest.setAttributes(old_attr);
316             baseRequest.setParameters(old_params);
317             baseRequest.setQueryString(old_query);
318             baseRequest.setDispatcherType(old_type);
319         }
320     }
321 
322 
323     /* ------------------------------------------------------------ */
324     /* ------------------------------------------------------------ */
325     /* ------------------------------------------------------------ */
326     private class ForwardAttributes implements Attributes
327     {
328         final Attributes _attr;
329         
330         String _requestURI;
331         String _contextPath;
332         String _servletPath;
333         String _pathInfo;
334         String _query;
335         
336         ForwardAttributes(Attributes attributes)
337         {
338             _attr=attributes;
339         }
340         
341         /* ------------------------------------------------------------ */
342         public Object getAttribute(String key)
343         {
344             if (Dispatcher.this._named==null)
345             {
346                 if (key.equals(FORWARD_PATH_INFO))    
347                     return _pathInfo;
348                 if (key.equals(FORWARD_REQUEST_URI))  
349                     return _requestURI;
350                 if (key.equals(FORWARD_SERVLET_PATH)) 
351                     return _servletPath;
352                 if (key.equals(FORWARD_CONTEXT_PATH)) 
353                     return _contextPath;
354                 if (key.equals(FORWARD_QUERY_STRING)) 
355                     return _query;
356             }
357             
358             if (key.startsWith(__INCLUDE_PREFIX))
359                 return null;
360             
361             return _attr.getAttribute(key);
362         }
363         
364         /* ------------------------------------------------------------ */
365         public Enumeration getAttributeNames()
366         {
367             HashSet set=new HashSet();
368             Enumeration e=_attr.getAttributeNames();
369             while(e.hasMoreElements())
370             {
371                 String name=(String)e.nextElement();
372                 if (!name.startsWith(__INCLUDE_PREFIX) &&
373                     !name.startsWith(__FORWARD_PREFIX))
374                     set.add(name);
375             }
376             
377             if (_named==null)
378             {
379                 if (_pathInfo!=null)
380                     set.add(FORWARD_PATH_INFO);
381                 else
382                     set.remove(FORWARD_PATH_INFO);
383                 set.add(FORWARD_REQUEST_URI);
384                 set.add(FORWARD_SERVLET_PATH);
385                 set.add(FORWARD_CONTEXT_PATH);
386                 if (_query!=null)
387                     set.add(FORWARD_QUERY_STRING);
388                 else
389                     set.remove(FORWARD_QUERY_STRING);
390             }
391 
392             return Collections.enumeration(set);
393         }
394         
395         /* ------------------------------------------------------------ */
396         public void setAttribute(String key, Object value)
397         {
398             if (_named==null && key.startsWith("javax.servlet."))
399             {
400                 if (key.equals(FORWARD_PATH_INFO))         
401                     _pathInfo=(String)value;
402                 else if (key.equals(FORWARD_REQUEST_URI))  
403                     _requestURI=(String)value;
404                 else if (key.equals(FORWARD_SERVLET_PATH)) 
405                     _servletPath=(String)value;
406                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
407                     _contextPath=(String)value;
408                 else if (key.equals(FORWARD_QUERY_STRING)) 
409                     _query=(String)value;
410                 
411                 else if (value==null)
412                     _attr.removeAttribute(key);
413                 else
414                     _attr.setAttribute(key,value); 
415             }
416             else if (value==null)
417                 _attr.removeAttribute(key);
418             else
419                 _attr.setAttribute(key,value);
420         }
421         
422         /* ------------------------------------------------------------ */
423         @Override
424         public String toString() 
425         {
426             return "FORWARD+"+_attr.toString();
427         }
428 
429         /* ------------------------------------------------------------ */
430         public void clearAttributes()
431         {
432             throw new IllegalStateException();
433         }
434 
435         /* ------------------------------------------------------------ */
436         public void removeAttribute(String name)
437         {
438             setAttribute(name,null);
439         }
440     }
441 
442     /* ------------------------------------------------------------ */
443     private class IncludeAttributes implements Attributes
444     {
445         final Attributes _attr;
446         
447         String _requestURI;
448         String _contextPath;
449         String _servletPath;
450         String _pathInfo;
451         String _query;
452         
453         IncludeAttributes(Attributes attributes)
454         {
455             _attr=attributes;
456         }
457         
458         /* ------------------------------------------------------------ */
459         /* ------------------------------------------------------------ */
460         /* ------------------------------------------------------------ */
461         public Object getAttribute(String key)
462         {
463             if (Dispatcher.this._named==null)
464             {
465                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
466                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
467                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
468                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
469                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
470             }
471             else if (key.startsWith(__INCLUDE_PREFIX)) 
472                     return null;
473             
474             
475             return _attr.getAttribute(key);
476         }
477         
478         /* ------------------------------------------------------------ */
479         public Enumeration getAttributeNames()
480         {
481             HashSet set=new HashSet();
482             Enumeration e=_attr.getAttributeNames();
483             while(e.hasMoreElements())
484             {
485                 String name=(String)e.nextElement();
486                 if (!name.startsWith(__INCLUDE_PREFIX))
487                     set.add(name);
488             }
489             
490             if (_named==null)
491             {
492                 if (_pathInfo!=null)
493                     set.add(INCLUDE_PATH_INFO);
494                 else
495                     set.remove(INCLUDE_PATH_INFO);
496                 set.add(INCLUDE_REQUEST_URI);
497                 set.add(INCLUDE_SERVLET_PATH);
498                 set.add(INCLUDE_CONTEXT_PATH);
499                 if (_query!=null)
500                     set.add(INCLUDE_QUERY_STRING);
501                 else
502                     set.remove(INCLUDE_QUERY_STRING);
503             }
504             
505             return Collections.enumeration(set);
506         }
507         
508         /* ------------------------------------------------------------ */
509         public void setAttribute(String key, Object value)
510         {
511             if (_named==null && key.startsWith("javax.servlet."))
512             {
513                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
514                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
515                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
516                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
517                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
518                 else if (value==null)
519                     _attr.removeAttribute(key);
520                 else
521                     _attr.setAttribute(key,value); 
522             }
523             else if (value==null)
524                 _attr.removeAttribute(key);
525             else
526                 _attr.setAttribute(key,value);
527         }
528         
529         /* ------------------------------------------------------------ */
530         @Override
531         public String toString() 
532         {
533             return "INCLUDE+"+_attr.toString();
534         }
535 
536         /* ------------------------------------------------------------ */
537         public void clearAttributes()
538         {
539             throw new IllegalStateException();
540         }
541 
542         /* ------------------------------------------------------------ */
543         public void removeAttribute(String name)
544         {
545             setAttribute(name,null);
546         }
547     }
548 }