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 String old_uri=baseRequest.getRequestURI();
226         final String old_context_path=baseRequest.getContextPath();
227         final String old_servlet_path=baseRequest.getServletPath();
228         final String old_path_info=baseRequest.getPathInfo();
229         final String old_query=baseRequest.getQueryString();
230         final Attributes old_attr=baseRequest.getAttributes();
231         final DispatcherType old_type=baseRequest.getDispatcherType();
232         MultiMap<String> old_params=baseRequest.getParameters();
233         
234         try
235         {
236             baseRequest.setDispatcherType(dispatch);
237             
238             if (_named!=null)
239                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
240             else 
241             {
242                 
243                 // process any query string from the dispatch URL
244                 String query=_dQuery;
245                 if (query!=null)
246                 {
247                     // force parameter extraction
248                     if (old_params==null)
249                     {
250                         baseRequest.extractParameters();
251                         old_params=baseRequest.getParameters();
252                     }
253                     
254                     baseRequest.mergeQueryString(query);
255                 }
256                 
257                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
258                 
259                 //If we have already been forwarded previously, then keep using the established 
260                 //original value. Otherwise, this is the first forward and we need to establish the values.
261                 //Note: the established value on the original request for pathInfo and
262                 //for queryString is allowed to be null, but cannot be null for the other values.
263                 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
264                 {
265                     attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
266                     attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
267                     attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
268                     attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
269                     attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
270                 }
271                 else
272                 {
273                     attr._pathInfo=old_path_info;
274                     attr._query=old_query;
275                     attr._requestURI=old_uri;
276                     attr._contextPath=old_context_path;
277                     attr._servletPath=old_servlet_path;
278                 }     
279                 
280                 baseRequest.setRequestURI(_uri);
281                 baseRequest.setContextPath(_contextHandler.getContextPath());
282                 baseRequest.setAttributes(attr);
283                 
284                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
285                 
286                 if (baseRequest.getResponse().isWriting())
287                 {
288                     try {response.getWriter().close();}
289                     catch(IllegalStateException e) 
290                     { 
291                         response.getOutputStream().close(); 
292                     }
293                 }
294                 else
295                 {
296                     try {response.getOutputStream().close();}
297                     catch(IllegalStateException e) 
298                     { 
299                         response.getWriter().close(); 
300                     }
301                 }
302             }
303         }
304         finally
305         {
306             baseRequest.setRequestURI(old_uri);
307             baseRequest.setContextPath(old_context_path);
308             baseRequest.setServletPath(old_servlet_path);
309             baseRequest.setPathInfo(old_path_info);
310             baseRequest.setAttributes(old_attr);
311             baseRequest.setParameters(old_params);
312             baseRequest.setQueryString(old_query);
313             baseRequest.setDispatcherType(old_type);
314         }
315     }
316 
317 
318     /* ------------------------------------------------------------ */
319     /* ------------------------------------------------------------ */
320     /* ------------------------------------------------------------ */
321     private class ForwardAttributes implements Attributes
322     {
323         final Attributes _attr;
324         
325         String _requestURI;
326         String _contextPath;
327         String _servletPath;
328         String _pathInfo;
329         String _query;
330         
331         ForwardAttributes(Attributes attributes)
332         {
333             _attr=attributes;
334         }
335         
336         /* ------------------------------------------------------------ */
337         public Object getAttribute(String key)
338         {
339             if (Dispatcher.this._named==null)
340             {
341                 if (key.equals(FORWARD_PATH_INFO))    
342                     return _pathInfo;
343                 if (key.equals(FORWARD_REQUEST_URI))  
344                     return _requestURI;
345                 if (key.equals(FORWARD_SERVLET_PATH)) 
346                     return _servletPath;
347                 if (key.equals(FORWARD_CONTEXT_PATH)) 
348                     return _contextPath;
349                 if (key.equals(FORWARD_QUERY_STRING)) 
350                     return _query;
351             }
352             
353             if (key.startsWith(__INCLUDE_PREFIX))
354                 return null;
355             
356             return _attr.getAttribute(key);
357         }
358         
359         /* ------------------------------------------------------------ */
360         public Enumeration getAttributeNames()
361         {
362             HashSet set=new HashSet();
363             Enumeration e=_attr.getAttributeNames();
364             while(e.hasMoreElements())
365             {
366                 String name=(String)e.nextElement();
367                 if (!name.startsWith(__INCLUDE_PREFIX) &&
368                     !name.startsWith(__FORWARD_PREFIX))
369                     set.add(name);
370             }
371             
372             if (_named==null)
373             {
374                 if (_pathInfo!=null)
375                     set.add(FORWARD_PATH_INFO);
376                 else
377                     set.remove(FORWARD_PATH_INFO);
378                 set.add(FORWARD_REQUEST_URI);
379                 set.add(FORWARD_SERVLET_PATH);
380                 set.add(FORWARD_CONTEXT_PATH);
381                 if (_query!=null)
382                     set.add(FORWARD_QUERY_STRING);
383                 else
384                     set.remove(FORWARD_QUERY_STRING);
385             }
386 
387             return Collections.enumeration(set);
388         }
389         
390         /* ------------------------------------------------------------ */
391         public void setAttribute(String key, Object value)
392         {
393             if (_named==null && key.startsWith("javax.servlet."))
394             {
395                 if (key.equals(FORWARD_PATH_INFO))         
396                     _pathInfo=(String)value;
397                 else if (key.equals(FORWARD_REQUEST_URI))  
398                     _requestURI=(String)value;
399                 else if (key.equals(FORWARD_SERVLET_PATH)) 
400                     _servletPath=(String)value;
401                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
402                     _contextPath=(String)value;
403                 else if (key.equals(FORWARD_QUERY_STRING)) 
404                     _query=(String)value;
405                 
406                 else if (value==null)
407                     _attr.removeAttribute(key);
408                 else
409                     _attr.setAttribute(key,value); 
410             }
411             else if (value==null)
412                 _attr.removeAttribute(key);
413             else
414                 _attr.setAttribute(key,value);
415         }
416         
417         /* ------------------------------------------------------------ */
418         @Override
419         public String toString() 
420         {
421             return "FORWARD+"+_attr.toString();
422         }
423 
424         /* ------------------------------------------------------------ */
425         public void clearAttributes()
426         {
427             throw new IllegalStateException();
428         }
429 
430         /* ------------------------------------------------------------ */
431         public void removeAttribute(String name)
432         {
433             setAttribute(name,null);
434         }
435     }
436 
437     /* ------------------------------------------------------------ */
438     private class IncludeAttributes implements Attributes
439     {
440         final Attributes _attr;
441         
442         String _requestURI;
443         String _contextPath;
444         String _servletPath;
445         String _pathInfo;
446         String _query;
447         
448         IncludeAttributes(Attributes attributes)
449         {
450             _attr=attributes;
451         }
452         
453         /* ------------------------------------------------------------ */
454         /* ------------------------------------------------------------ */
455         /* ------------------------------------------------------------ */
456         public Object getAttribute(String key)
457         {
458             if (Dispatcher.this._named==null)
459             {
460                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
461                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
462                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
463                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
464                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
465             }
466             else if (key.startsWith(__INCLUDE_PREFIX)) 
467                     return null;
468             
469             
470             return _attr.getAttribute(key);
471         }
472         
473         /* ------------------------------------------------------------ */
474         public Enumeration getAttributeNames()
475         {
476             HashSet set=new HashSet();
477             Enumeration e=_attr.getAttributeNames();
478             while(e.hasMoreElements())
479             {
480                 String name=(String)e.nextElement();
481                 if (!name.startsWith(__INCLUDE_PREFIX))
482                     set.add(name);
483             }
484             
485             if (_named==null)
486             {
487                 if (_pathInfo!=null)
488                     set.add(INCLUDE_PATH_INFO);
489                 else
490                     set.remove(INCLUDE_PATH_INFO);
491                 set.add(INCLUDE_REQUEST_URI);
492                 set.add(INCLUDE_SERVLET_PATH);
493                 set.add(INCLUDE_CONTEXT_PATH);
494                 if (_query!=null)
495                     set.add(INCLUDE_QUERY_STRING);
496                 else
497                     set.remove(INCLUDE_QUERY_STRING);
498             }
499             
500             return Collections.enumeration(set);
501         }
502         
503         /* ------------------------------------------------------------ */
504         public void setAttribute(String key, Object value)
505         {
506             if (_named==null && key.startsWith("javax.servlet."))
507             {
508                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
509                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
510                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
511                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
512                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
513                 else if (value==null)
514                     _attr.removeAttribute(key);
515                 else
516                     _attr.setAttribute(key,value); 
517             }
518             else if (value==null)
519                 _attr.removeAttribute(key);
520             else
521                 _attr.setAttribute(key,value);
522         }
523         
524         /* ------------------------------------------------------------ */
525         @Override
526         public String toString() 
527         {
528             return "INCLUDE+"+_attr.toString();
529         }
530 
531         /* ------------------------------------------------------------ */
532         public void clearAttributes()
533         {
534             throw new IllegalStateException();
535         }
536 
537         /* ------------------------------------------------------------ */
538         public void removeAttribute(String name)
539         {
540             setAttribute(name,null);
541         }
542     }
543 }