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