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):AbstractHttpConnection.getCurrentConnection().getRequest();
134       
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):AbstractHttpConnection.getCurrentConnection().getRequest();
215         Response base_response=baseRequest.getResponse();
216         response.resetBuffer();
217         base_response.fwdReset();
218        
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.getAsyncContinuation().isAsyncStarted())
291                     commitResponse(response,baseRequest);
292             }
293         }
294         finally
295         {
296             baseRequest.setHandled(old_handled);
297             baseRequest.setRequestURI(old_uri);
298             baseRequest.setContextPath(old_context_path);
299             baseRequest.setServletPath(old_servlet_path);
300             baseRequest.setPathInfo(old_path_info);
301             baseRequest.setAttributes(old_attr);
302             baseRequest.setParameters(old_params);
303             baseRequest.setQueryString(old_query);
304             baseRequest.setDispatcherType(old_type);
305         }
306     }
307 
308 
309     /* ------------------------------------------------------------ */
310     private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
311     {
312         if (baseRequest.getResponse().isWriting())
313         {
314             try
315             {
316                 response.getWriter().close();
317             }
318             catch (IllegalStateException e)
319             {
320                 response.getOutputStream().close();
321             }
322         }
323         else
324         {
325             try
326             {
327                 response.getOutputStream().close();
328             }
329             catch (IllegalStateException e)
330             {
331                 response.getWriter().close();
332             }
333         }
334     }
335 
336 
337     /* ------------------------------------------------------------ */
338     /* ------------------------------------------------------------ */
339     /* ------------------------------------------------------------ */
340     private class ForwardAttributes implements Attributes
341     {
342         final Attributes _attr;
343         
344         String _requestURI;
345         String _contextPath;
346         String _servletPath;
347         String _pathInfo;
348         String _query;
349         
350         ForwardAttributes(Attributes attributes)
351         {
352             _attr=attributes;
353         }
354         
355         /* ------------------------------------------------------------ */
356         public Object getAttribute(String key)
357         {
358             if (Dispatcher.this._named==null)
359             {
360                 if (key.equals(FORWARD_PATH_INFO))    
361                     return _pathInfo;
362                 if (key.equals(FORWARD_REQUEST_URI))  
363                     return _requestURI;
364                 if (key.equals(FORWARD_SERVLET_PATH)) 
365                     return _servletPath;
366                 if (key.equals(FORWARD_CONTEXT_PATH)) 
367                     return _contextPath;
368                 if (key.equals(FORWARD_QUERY_STRING)) 
369                     return _query;
370             }
371             
372             if (key.startsWith(__INCLUDE_PREFIX))
373                 return null;
374             
375             return _attr.getAttribute(key);
376         }
377         
378         /* ------------------------------------------------------------ */
379         public Enumeration getAttributeNames()
380         {
381             HashSet set=new HashSet();
382             Enumeration e=_attr.getAttributeNames();
383             while(e.hasMoreElements())
384             {
385                 String name=(String)e.nextElement();
386                 if (!name.startsWith(__INCLUDE_PREFIX) &&
387                     !name.startsWith(__FORWARD_PREFIX))
388                     set.add(name);
389             }
390             
391             if (_named==null)
392             {
393                 if (_pathInfo!=null)
394                     set.add(FORWARD_PATH_INFO);
395                 else
396                     set.remove(FORWARD_PATH_INFO);
397                 set.add(FORWARD_REQUEST_URI);
398                 set.add(FORWARD_SERVLET_PATH);
399                 set.add(FORWARD_CONTEXT_PATH);
400                 if (_query!=null)
401                     set.add(FORWARD_QUERY_STRING);
402                 else
403                     set.remove(FORWARD_QUERY_STRING);
404             }
405 
406             return Collections.enumeration(set);
407         }
408         
409         /* ------------------------------------------------------------ */
410         public void setAttribute(String key, Object value)
411         {
412             if (_named==null && key.startsWith("javax.servlet."))
413             {
414                 if (key.equals(FORWARD_PATH_INFO))         
415                     _pathInfo=(String)value;
416                 else if (key.equals(FORWARD_REQUEST_URI))  
417                     _requestURI=(String)value;
418                 else if (key.equals(FORWARD_SERVLET_PATH)) 
419                     _servletPath=(String)value;
420                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
421                     _contextPath=(String)value;
422                 else if (key.equals(FORWARD_QUERY_STRING)) 
423                     _query=(String)value;
424                 
425                 else if (value==null)
426                     _attr.removeAttribute(key);
427                 else
428                     _attr.setAttribute(key,value); 
429             }
430             else if (value==null)
431                 _attr.removeAttribute(key);
432             else
433                 _attr.setAttribute(key,value);
434         }
435         
436         /* ------------------------------------------------------------ */
437         @Override
438         public String toString() 
439         {
440             return "FORWARD+"+_attr.toString();
441         }
442 
443         /* ------------------------------------------------------------ */
444         public void clearAttributes()
445         {
446             throw new IllegalStateException();
447         }
448 
449         /* ------------------------------------------------------------ */
450         public void removeAttribute(String name)
451         {
452             setAttribute(name,null);
453         }
454     }
455 
456     /* ------------------------------------------------------------ */
457     private class IncludeAttributes implements Attributes
458     {
459         final Attributes _attr;
460         
461         String _requestURI;
462         String _contextPath;
463         String _servletPath;
464         String _pathInfo;
465         String _query;
466         
467         IncludeAttributes(Attributes attributes)
468         {
469             _attr=attributes;
470         }
471         
472         /* ------------------------------------------------------------ */
473         /* ------------------------------------------------------------ */
474         /* ------------------------------------------------------------ */
475         public Object getAttribute(String key)
476         {
477             if (Dispatcher.this._named==null)
478             {
479                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
480                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
481                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
482                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
483                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
484             }
485             else if (key.startsWith(__INCLUDE_PREFIX)) 
486                     return null;
487             
488             
489             return _attr.getAttribute(key);
490         }
491         
492         /* ------------------------------------------------------------ */
493         public Enumeration getAttributeNames()
494         {
495             HashSet set=new HashSet();
496             Enumeration e=_attr.getAttributeNames();
497             while(e.hasMoreElements())
498             {
499                 String name=(String)e.nextElement();
500                 if (!name.startsWith(__INCLUDE_PREFIX))
501                     set.add(name);
502             }
503             
504             if (_named==null)
505             {
506                 if (_pathInfo!=null)
507                     set.add(INCLUDE_PATH_INFO);
508                 else
509                     set.remove(INCLUDE_PATH_INFO);
510                 set.add(INCLUDE_REQUEST_URI);
511                 set.add(INCLUDE_SERVLET_PATH);
512                 set.add(INCLUDE_CONTEXT_PATH);
513                 if (_query!=null)
514                     set.add(INCLUDE_QUERY_STRING);
515                 else
516                     set.remove(INCLUDE_QUERY_STRING);
517             }
518             
519             return Collections.enumeration(set);
520         }
521         
522         /* ------------------------------------------------------------ */
523         public void setAttribute(String key, Object value)
524         {
525             if (_named==null && key.startsWith("javax.servlet."))
526             {
527                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
528                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
529                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
530                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
531                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
532                 else if (value==null)
533                     _attr.removeAttribute(key);
534                 else
535                     _attr.setAttribute(key,value); 
536             }
537             else if (value==null)
538                 _attr.removeAttribute(key);
539             else
540                 _attr.setAttribute(key,value);
541         }
542         
543         /* ------------------------------------------------------------ */
544         @Override
545         public String toString() 
546         {
547             return "INCLUDE+"+_attr.toString();
548         }
549 
550         /* ------------------------------------------------------------ */
551         public void clearAttributes()
552         {
553             throw new IllegalStateException();
554         }
555 
556         /* ------------------------------------------------------------ */
557         public void removeAttribute(String name)
558         {
559             setAttribute(name,null);
560         }
561     }
562 }