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         // TODO - allow stream or writer????
137         
138         DispatcherType old_type = baseRequest.getDispatcherType();
139         Attributes old_attr=baseRequest.getAttributes();
140         MultiMap old_params=baseRequest.getParameters();
141         try
142         {
143             baseRequest.setDispatcherType(DispatcherType.INCLUDE);
144             baseRequest.getConnection().include();
145             if (_named!=null)
146                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
147             else 
148             {
149                 String query=_dQuery;
150                 
151                 if (query!=null)
152                 {
153                     MultiMap parameters=new MultiMap();
154                     UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding());
155                     
156                     if (old_params!=null && old_params.size()>0)
157                     {
158                         // Merge parameters.
159                         Iterator iter = old_params.entrySet().iterator();
160                         while (iter.hasNext())
161                         {
162                             Map.Entry entry = (Map.Entry)iter.next();
163                             String name=(String)entry.getKey();
164                             Object values=entry.getValue();
165                             for (int i=0;i<LazyList.size(values);i++)
166                                 parameters.add(name, LazyList.get(values, i));
167                         }
168                         
169                     }
170                     baseRequest.setParameters(parameters);
171                 }
172                 
173                 IncludeAttributes attr = new IncludeAttributes(old_attr); 
174                 
175                 attr._requestURI=_uri;
176                 attr._contextPath=_contextHandler.getContextPath();
177                 attr._servletPath=null; // set by ServletHandler
178                 attr._pathInfo=_path;
179                 attr._query=query;
180                 
181                 baseRequest.setAttributes(attr);
182                 
183                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
184             }
185         }
186         finally
187         {
188             baseRequest.setAttributes(old_attr);
189             baseRequest.getConnection().included();
190             baseRequest.setParameters(old_params);
191             baseRequest.setDispatcherType(old_type);
192         }
193     }
194 
195     
196     /* ------------------------------------------------------------ */
197     /* 
198      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
199      */
200     protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
201     {
202         Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
203         Response base_response=baseRequest.getResponse();
204         base_response.fwdReset();
205         request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed
206         
207         String old_uri=baseRequest.getRequestURI();
208         String old_context_path=baseRequest.getContextPath();
209         String old_servlet_path=baseRequest.getServletPath();
210         String old_path_info=baseRequest.getPathInfo();
211         String old_query=baseRequest.getQueryString();
212         Attributes old_attr=baseRequest.getAttributes();
213         MultiMap old_params=baseRequest.getParameters();
214         DispatcherType old_type=baseRequest.getDispatcherType();
215         
216         try
217         {
218             baseRequest.setDispatcherType(dispatch);
219             
220             if (_named!=null)
221                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
222             else 
223             {
224                 String query=_dQuery;
225                 
226                 if (query!=null)
227                 {
228                     MultiMap parameters=new MultiMap();
229                     UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding());
230                  
231                     boolean rewrite_old_query = false;
232 
233                     if( old_params == null )
234                     {
235                         baseRequest.getParameterNames();    // force parameters to be evaluated
236                         old_params = baseRequest.getParameters();
237                     }
238                     
239                     if (old_params!=null && old_params.size()>0)
240                     {
241                         // Merge parameters; new parameters of the same name take precedence.
242                         Iterator iter = old_params.entrySet().iterator();
243                         while (iter.hasNext())
244                         {
245                             Map.Entry entry = (Map.Entry)iter.next();
246                             String name=(String)entry.getKey();
247                             
248                             if (parameters.containsKey(name))
249                             {
250                                 rewrite_old_query = true;
251                             }
252                             else
253                             {
254                                 Object values=entry.getValue();
255                                 for (int i=0;i<LazyList.size(values);i++)
256                                 {
257                                     parameters.add(name, LazyList.get(values, i));
258                                 }
259                             }
260                         }
261                     }
262                     
263                     if (old_query != null && old_query.length()>0)
264                     {
265                         if ( rewrite_old_query )
266                         {
267                             StringBuilder overridden_query_string = new StringBuilder();
268                             MultiMap overridden_old_query = new MultiMap();
269                             UrlEncoded.decodeTo(old_query,overridden_old_query,request.getCharacterEncoding());
270     
271                             MultiMap overridden_new_query = new MultiMap(); 
272                             UrlEncoded.decodeTo(query,overridden_new_query,request.getCharacterEncoding());
273 
274                             Iterator iter = overridden_old_query.entrySet().iterator();
275                             while (iter.hasNext())
276                             {
277                                 Map.Entry entry = (Map.Entry)iter.next();
278                                 String name=(String)entry.getKey();
279                                 if(!overridden_new_query.containsKey(name))
280                                 {
281                                     Object values=entry.getValue();
282                                     for (int i=0;i<LazyList.size(values);i++)
283                                     {
284                                         overridden_query_string.append("&").append(name).append("=").append(LazyList.get(values, i));
285                                     }
286                                 }
287                             }
288                             
289                             query = query + overridden_query_string;
290                         }
291                         else 
292                         {
293                             query=query+"&"+old_query;
294                         }
295                    }
296 
297                     baseRequest.setParameters(parameters);
298                     baseRequest.setQueryString(query);
299                 }
300                 
301                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
302                 
303                 //If we have already been forwarded previously, then keep using the established 
304                 //original value. Otherwise, this is the first forward and we need to establish the values.
305                 //Note: the established value on the original request for pathInfo and
306                 //for queryString is allowed to be null, but cannot be null for the other values.
307                 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
308                 {
309                     attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
310                     attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
311                     attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
312                     attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
313                     attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
314                 }
315                 else
316                 {
317                     attr._pathInfo=old_path_info;
318                     attr._query=old_query;
319                     attr._requestURI=old_uri;
320                     attr._contextPath=old_context_path;
321                     attr._servletPath=old_servlet_path;
322                 }                
323    
324               
325                 
326                 baseRequest.setRequestURI(_uri);
327                 baseRequest.setContextPath(_contextHandler.getContextPath());
328                 baseRequest.setAttributes(attr);
329                 baseRequest.setQueryString(query);
330                 
331                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
332                 
333                 if (baseRequest.getConnection().getResponse().isWriting())
334                 {
335                     try {response.getWriter().close();}
336                     catch(IllegalStateException e) { response.getOutputStream().close(); }
337                 }
338                 else
339                 {
340                     try {response.getOutputStream().close();}
341                     catch(IllegalStateException e) { response.getWriter().close(); }
342                 }
343             }
344         }
345         finally
346         {
347             baseRequest.setRequestURI(old_uri);
348             baseRequest.setContextPath(old_context_path);
349             baseRequest.setServletPath(old_servlet_path);
350             baseRequest.setPathInfo(old_path_info);
351             baseRequest.setAttributes(old_attr);
352             baseRequest.setParameters(old_params);
353             baseRequest.setQueryString(old_query);
354             baseRequest.setDispatcherType(old_type);
355         }
356     }
357 
358 
359     /* ------------------------------------------------------------ */
360     /* ------------------------------------------------------------ */
361     /* ------------------------------------------------------------ */
362     private class ForwardAttributes implements Attributes
363     {
364         final Attributes _attr;
365         
366         String _requestURI;
367         String _contextPath;
368         String _servletPath;
369         String _pathInfo;
370         String _query;
371         
372         ForwardAttributes(Attributes attributes)
373         {
374             _attr=attributes;
375         }
376         
377         /* ------------------------------------------------------------ */
378         public Object getAttribute(String key)
379         {
380             if (Dispatcher.this._named==null)
381             {
382                 if (key.equals(FORWARD_PATH_INFO))    
383                     return _pathInfo;
384                 if (key.equals(FORWARD_REQUEST_URI))  
385                     return _requestURI;
386                 if (key.equals(FORWARD_SERVLET_PATH)) 
387                     return _servletPath;
388                 if (key.equals(FORWARD_CONTEXT_PATH)) 
389                     return _contextPath;
390                 if (key.equals(FORWARD_QUERY_STRING)) 
391                     return _query;
392             }
393             
394             if (key.startsWith(__INCLUDE_PREFIX))
395                 return null;
396             
397             return _attr.getAttribute(key);
398         }
399         
400         /* ------------------------------------------------------------ */
401         public Enumeration getAttributeNames()
402         {
403             HashSet set=new HashSet();
404             Enumeration e=_attr.getAttributeNames();
405             while(e.hasMoreElements())
406             {
407                 String name=(String)e.nextElement();
408                 if (!name.startsWith(__INCLUDE_PREFIX) &&
409                     !name.startsWith(__FORWARD_PREFIX))
410                     set.add(name);
411             }
412             
413             if (_named==null)
414             {
415                 if (_pathInfo!=null)
416                     set.add(FORWARD_PATH_INFO);
417                 else
418                     set.remove(FORWARD_PATH_INFO);
419                 set.add(FORWARD_REQUEST_URI);
420                 set.add(FORWARD_SERVLET_PATH);
421                 set.add(FORWARD_CONTEXT_PATH);
422                 if (_query!=null)
423                     set.add(FORWARD_QUERY_STRING);
424                 else
425                     set.remove(FORWARD_QUERY_STRING);
426             }
427 
428             return Collections.enumeration(set);
429         }
430         
431         /* ------------------------------------------------------------ */
432         public void setAttribute(String key, Object value)
433         {
434             if (_named==null && key.startsWith("javax.servlet."))
435             {
436                 if (key.equals(FORWARD_PATH_INFO))         
437                     _pathInfo=(String)value;
438                 else if (key.equals(FORWARD_REQUEST_URI))  
439                     _requestURI=(String)value;
440                 else if (key.equals(FORWARD_SERVLET_PATH)) 
441                     _servletPath=(String)value;
442                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
443                     _contextPath=(String)value;
444                 else if (key.equals(FORWARD_QUERY_STRING)) 
445                     _query=(String)value;
446                 
447                 else if (value==null)
448                     _attr.removeAttribute(key);
449                 else
450                     _attr.setAttribute(key,value); 
451             }
452             else if (value==null)
453                 _attr.removeAttribute(key);
454             else
455                 _attr.setAttribute(key,value);
456         }
457         
458         /* ------------------------------------------------------------ */
459         public String toString() 
460         {
461             return "FORWARD+"+_attr.toString();
462         }
463 
464         /* ------------------------------------------------------------ */
465         public void clearAttributes()
466         {
467             throw new IllegalStateException();
468         }
469 
470         /* ------------------------------------------------------------ */
471         public void removeAttribute(String name)
472         {
473             setAttribute(name,null);
474         }
475     }
476 
477     /* ------------------------------------------------------------ */
478     private class IncludeAttributes implements Attributes
479     {
480         final Attributes _attr;
481         
482         String _requestURI;
483         String _contextPath;
484         String _servletPath;
485         String _pathInfo;
486         String _query;
487         
488         IncludeAttributes(Attributes attributes)
489         {
490             _attr=attributes;
491         }
492         
493         /* ------------------------------------------------------------ */
494         /* ------------------------------------------------------------ */
495         /* ------------------------------------------------------------ */
496         public Object getAttribute(String key)
497         {
498             if (Dispatcher.this._named==null)
499             {
500                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
501                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
502                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
503                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
504                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
505             }
506             else if (key.startsWith(__INCLUDE_PREFIX)) 
507                     return null;
508             
509             
510             return _attr.getAttribute(key);
511         }
512         
513         /* ------------------------------------------------------------ */
514         public Enumeration getAttributeNames()
515         {
516             HashSet set=new HashSet();
517             Enumeration e=_attr.getAttributeNames();
518             while(e.hasMoreElements())
519             {
520                 String name=(String)e.nextElement();
521                 if (!name.startsWith(__INCLUDE_PREFIX))
522                     set.add(name);
523             }
524             
525             if (_named==null)
526             {
527                 if (_pathInfo!=null)
528                     set.add(INCLUDE_PATH_INFO);
529                 else
530                     set.remove(INCLUDE_PATH_INFO);
531                 set.add(INCLUDE_REQUEST_URI);
532                 set.add(INCLUDE_SERVLET_PATH);
533                 set.add(INCLUDE_CONTEXT_PATH);
534                 if (_query!=null)
535                     set.add(INCLUDE_QUERY_STRING);
536                 else
537                     set.remove(INCLUDE_QUERY_STRING);
538             }
539             
540             return Collections.enumeration(set);
541         }
542         
543         /* ------------------------------------------------------------ */
544         public void setAttribute(String key, Object value)
545         {
546             if (_named==null && key.startsWith("javax.servlet."))
547             {
548                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
549                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
550                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
551                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
552                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
553                 else if (value==null)
554                     _attr.removeAttribute(key);
555                 else
556                     _attr.setAttribute(key,value); 
557             }
558             else if (value==null)
559                 _attr.removeAttribute(key);
560             else
561                 _attr.setAttribute(key,value);
562         }
563         
564         /* ------------------------------------------------------------ */
565         public String toString() 
566         {
567             return "INCLUDE+"+_attr.toString();
568         }
569 
570         /* ------------------------------------------------------------ */
571         public void clearAttributes()
572         {
573             throw new IllegalStateException();
574         }
575 
576         /* ------------------------------------------------------------ */
577         public void removeAttribute(String name)
578         {
579             setAttribute(name,null);
580         }
581     }
582 }