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