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