View Javadoc

1   // ========================================================================
2   // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at 
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  
15  package org.mortbay.jetty.servlet;
16  
17  import java.io.IOException;
18  import java.util.Collections;
19  import java.util.Enumeration;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.Map;
23  
24  import javax.servlet.RequestDispatcher;
25  import javax.servlet.ServletException;
26  import javax.servlet.ServletRequest;
27  import javax.servlet.ServletResponse;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import org.mortbay.jetty.Handler;
32  import org.mortbay.jetty.HttpConnection;
33  import org.mortbay.jetty.Request;
34  import org.mortbay.jetty.handler.ContextHandler;
35  import org.mortbay.util.Attributes;
36  import org.mortbay.util.LazyList;
37  import org.mortbay.util.MultiMap;
38  import org.mortbay.util.UrlEncoded;
39  
40  /* ------------------------------------------------------------ */
41  /** Servlet RequestDispatcher.
42   * 
43   * @author Greg Wilkins (gregw)
44   */
45  public class Dispatcher implements RequestDispatcher
46  {
47      /** Dispatch include attribute names */
48      public final static String __INCLUDE_JETTY="org.mortbay.jetty.included";
49      public final static String __INCLUDE_PREFIX="javax.servlet.include.";
50      public final static String __INCLUDE_REQUEST_URI= "javax.servlet.include.request_uri";
51      public final static String __INCLUDE_CONTEXT_PATH= "javax.servlet.include.context_path";
52      public final static String __INCLUDE_SERVLET_PATH= "javax.servlet.include.servlet_path";
53      public final static String __INCLUDE_PATH_INFO= "javax.servlet.include.path_info";
54      public final static String __INCLUDE_QUERY_STRING= "javax.servlet.include.query_string";
55  
56      /** Dispatch include attribute names */
57      public final static String __FORWARD_JETTY="org.mortbay.jetty.forwarded";
58      public final static String __FORWARD_PREFIX="javax.servlet.forward.";
59      public final static String __FORWARD_REQUEST_URI= "javax.servlet.forward.request_uri";
60      public final static String __FORWARD_CONTEXT_PATH= "javax.servlet.forward.context_path";
61      public final static String __FORWARD_SERVLET_PATH= "javax.servlet.forward.servlet_path";
62      public final static String __FORWARD_PATH_INFO= "javax.servlet.forward.path_info";
63      public final static String __FORWARD_QUERY_STRING= "javax.servlet.forward.query_string";
64  
65      /** JSP attributes */
66      public final static String __JSP_FILE="org.apache.catalina.jsp_file";
67  
68      /* ------------------------------------------------------------ */
69      /** Dispatch type from name
70       */
71      public static int type(String type)
72      {
73          if ("request".equalsIgnoreCase(type))
74              return Handler.REQUEST;
75          if ("forward".equalsIgnoreCase(type))
76              return Handler.FORWARD;
77          if ("include".equalsIgnoreCase(type))
78              return Handler.INCLUDE;
79          if ("error".equalsIgnoreCase(type))
80              return Handler.ERROR;
81          throw new IllegalArgumentException(type);
82      }
83  
84  
85      /* ------------------------------------------------------------ */
86      private ContextHandler _contextHandler;
87      private String _uri;
88      private String _path;
89      private String _dQuery;
90      private String _named;
91      
92      /* ------------------------------------------------------------ */
93      /**
94       * @param contextHandler
95       * @param uriInContext
96       * @param pathInContext
97       * @param query
98       */
99      public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
100     {
101         _contextHandler=contextHandler;
102         _uri=uri;
103         _path=pathInContext;
104         _dQuery=query;
105     }
106 
107 
108     /* ------------------------------------------------------------ */
109     /** Constructor. 
110      * @param servletHandler
111      * @param name
112      */
113     public Dispatcher(ContextHandler contextHandler,String name)
114         throws IllegalStateException
115     {
116         _contextHandler=contextHandler;
117         _named=name;
118     }
119     
120     /* ------------------------------------------------------------ */
121     /* 
122      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
123      */
124     public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
125     {
126         forward(request, response, Handler.FORWARD);
127     }
128     
129     /* ------------------------------------------------------------ */
130     /* 
131      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
132      */
133     public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
134     {
135         forward(request, response, Handler.ERROR);
136     }
137     
138     /* ------------------------------------------------------------ */
139     /* 
140      * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
141      */
142     public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
143     {
144         Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
145         request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed
146         
147         // TODO - allow stream or writer????
148         
149         Attributes old_attr=base_request.getAttributes();
150         MultiMap old_params=base_request.getParameters();
151         try
152         {
153             base_request.getConnection().include();
154             if (_named!=null)
155                 _contextHandler.handle(_named, (HttpServletRequest)request, (HttpServletResponse)response, Handler.INCLUDE);
156             else 
157             {
158                 String query=_dQuery;
159                 
160                 if (query!=null)
161                 {
162                     MultiMap parameters=new MultiMap();
163                     UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding());
164                     
165                     if (old_params!=null && old_params.size()>0)
166                     {
167                         // Merge parameters.
168                         Iterator iter = old_params.entrySet().iterator();
169                         while (iter.hasNext())
170                         {
171                             Map.Entry entry = (Map.Entry)iter.next();
172                             String name=(String)entry.getKey();
173                             Object values=entry.getValue();
174                             for (int i=0;i<LazyList.size(values);i++)
175                                 parameters.add(name, LazyList.get(values, i));
176                         }
177                         
178                     }
179                     base_request.setParameters(parameters);
180                 }
181                 
182                 IncludeAttributes attr = new IncludeAttributes(old_attr); 
183                 
184                 attr._requestURI=_uri;
185                 attr._contextPath=_contextHandler.getContextPath();
186                 attr._servletPath=null; // set by ServletHandler
187                 attr._pathInfo=_path;
188                 attr._query=query;
189                 
190                 base_request.setAttributes(attr);
191                 
192                 _contextHandler.handle(_named==null?_path:_named, (HttpServletRequest)request, (HttpServletResponse)response, Handler.INCLUDE);
193             }
194         }
195         finally
196         {
197             base_request.setAttributes(old_attr);
198             base_request.getConnection().included();
199             base_request.setParameters(old_params);
200         }
201     }
202 
203     
204     /* ------------------------------------------------------------ */
205     /* 
206      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
207      */
208     protected void forward(ServletRequest request, ServletResponse response, int dispatch) throws ServletException, IOException
209     {
210         Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
211         response.resetBuffer(); 
212         request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed
213         
214         String old_uri=base_request.getRequestURI();
215         String old_context_path=base_request.getContextPath();
216         String old_servlet_path=base_request.getServletPath();
217         String old_path_info=base_request.getPathInfo();
218         String old_query=base_request.getQueryString();
219         Attributes old_attr=base_request.getAttributes();
220         MultiMap old_params=base_request.getParameters();
221         try
222         {
223             if (_named!=null)
224                 _contextHandler.handle(_named, (HttpServletRequest)request, (HttpServletResponse)response, dispatch);
225             else 
226             {
227                 String query=_dQuery;
228                 
229                 if (query!=null)
230                 {
231                     MultiMap parameters=new MultiMap();
232                     UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding());
233                  
234                     boolean rewrite_old_query = false;
235 
236                     if( old_params == null )
237                     {
238                         base_request.getParameterNames();    // force parameters to be evaluated
239                         old_params = base_request.getParameters();
240                     }
241                     
242                     if (old_params!=null && old_params.size()>0)
243                     {
244                         // Merge parameters; new parameters of the same name take precedence.
245                         Iterator iter = old_params.entrySet().iterator();
246                         while (iter.hasNext())
247                         {
248                             Map.Entry entry = (Map.Entry)iter.next();
249                             String name=(String)entry.getKey();
250                             
251                             if (parameters.containsKey(name))
252                             {
253                                 rewrite_old_query = true;
254                             }
255                             else
256                             {
257                                 Object values=entry.getValue();
258                                 for (int i=0;i<LazyList.size(values);i++)
259                                 {
260                                     parameters.add(name, LazyList.get(values, i));
261                                 }
262                             }
263                         }
264                     }
265                     
266                     if (old_query != null && old_query.length()>0)
267                     {
268                         if ( rewrite_old_query )
269                         {
270                             StringBuffer overridden_query_string = new StringBuffer();
271                             MultiMap overridden_old_query = new MultiMap();
272                             UrlEncoded.decodeTo(old_query,overridden_old_query,request.getCharacterEncoding());
273     
274                             MultiMap overridden_new_query = new MultiMap(); 
275                             UrlEncoded.decodeTo(query,overridden_new_query,request.getCharacterEncoding());
276 
277                             Iterator iter = overridden_old_query.entrySet().iterator();
278                             while (iter.hasNext())
279                             {
280                                 Map.Entry entry = (Map.Entry)iter.next();
281                                 String name=(String)entry.getKey();
282                                 if(!overridden_new_query.containsKey(name))
283                                 {
284                                     Object values=entry.getValue();
285                                     for (int i=0;i<LazyList.size(values);i++)
286                                     {
287                                         overridden_query_string.append("&"+name+"="+LazyList.get(values, i));
288                                     }
289                                 }
290                             }
291                             
292                             query = query + overridden_query_string;
293                         }
294                         else 
295                         {
296                             query=query+"&"+old_query;
297                         }
298                    }
299 
300                     base_request.setParameters(parameters);
301                     base_request.setQueryString(query);
302                 }
303                 
304                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
305                 
306                 //If we have already been forwarded previously, then keep using the established 
307                 //original value. Otherwise, this is the first forward and we need to establish the values.
308                 //Note: the established value on the original request for pathInfo and
309                 //for queryString is allowed to be null, but cannot be null for the other values.
310                 if ((String)old_attr.getAttribute(__FORWARD_REQUEST_URI) != null)
311                 {
312                     attr._pathInfo=(String)old_attr.getAttribute(__FORWARD_PATH_INFO);
313                     attr._query=(String)old_attr.getAttribute(__FORWARD_QUERY_STRING);
314                     attr._requestURI=(String)old_attr.getAttribute(__FORWARD_REQUEST_URI);
315                     attr._contextPath=(String)old_attr.getAttribute(__FORWARD_CONTEXT_PATH);
316                     attr._servletPath=(String)old_attr.getAttribute(__FORWARD_SERVLET_PATH);
317                 }
318                 else
319                 {
320                     attr._pathInfo=old_path_info;
321                     attr._query=old_query;
322                     attr._requestURI=old_uri;
323                     attr._contextPath=old_context_path;
324                     attr._servletPath=old_servlet_path;
325                 }                
326    
327               
328                 
329                 base_request.setRequestURI(_uri);
330                 base_request.setContextPath(_contextHandler.getContextPath());
331                 base_request.setAttributes(attr);
332                 base_request.setQueryString(query);
333                 
334                 _contextHandler.handle(_path, (HttpServletRequest)request, (HttpServletResponse)response, dispatch);
335                 
336                 if (base_request.getConnection().getResponse().isWriting())
337                 {
338                     try {response.getWriter().close();}
339                     catch(IllegalStateException e) { response.getOutputStream().close(); }
340                 }
341                 else
342                 {
343                     try {response.getOutputStream().close();}
344                     catch(IllegalStateException e) { response.getWriter().close(); }
345                 }
346             }
347         }
348         finally
349         {
350             base_request.setRequestURI(old_uri);
351             base_request.setContextPath(old_context_path);
352             base_request.setServletPath(old_servlet_path);
353             base_request.setPathInfo(old_path_info);
354             base_request.setAttributes(old_attr);
355             base_request.setParameters(old_params);
356             base_request.setQueryString(old_query);
357         }
358     }
359 
360 
361     /* ------------------------------------------------------------ */
362     /* ------------------------------------------------------------ */
363     /* ------------------------------------------------------------ */
364     private class ForwardAttributes implements Attributes
365     {
366         Attributes _attr;
367         
368         String _requestURI;
369         String _contextPath;
370         String _servletPath;
371         String _pathInfo;
372         String _query;
373         
374         ForwardAttributes(Attributes attributes)
375         {
376             _attr=attributes;
377         }
378         
379         /* ------------------------------------------------------------ */
380         public Object getAttribute(String key)
381         {
382             if (Dispatcher.this._named==null)
383             {
384                 if (key.equals(__FORWARD_PATH_INFO))    return _pathInfo;
385                 if (key.equals(__FORWARD_REQUEST_URI))  return _requestURI;
386                 if (key.equals(__FORWARD_SERVLET_PATH)) return _servletPath;
387                 if (key.equals(__FORWARD_CONTEXT_PATH)) return _contextPath;
388                 if (key.equals(__FORWARD_QUERY_STRING)) return _query;
389             }
390             
391             if (key.startsWith(__INCLUDE_PREFIX) || key.equals(__INCLUDE_JETTY) )
392                 return null;
393 
394             if (key.equals(__FORWARD_JETTY)) 
395                 return Boolean.TRUE;
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))         _pathInfo=(String)value;
437                 else if (key.equals(__FORWARD_REQUEST_URI))  _requestURI=(String)value;
438                 else if (key.equals(__FORWARD_SERVLET_PATH)) _servletPath=(String)value;
439                 else if (key.equals(__FORWARD_CONTEXT_PATH)) _contextPath=(String)value;
440                 else if (key.equals(__FORWARD_QUERY_STRING)) _query=(String)value;
441                 
442                 else if (value==null)
443                     _attr.removeAttribute(key);
444                 else
445                     _attr.setAttribute(key,value); 
446             }
447             else if (value==null)
448                 _attr.removeAttribute(key);
449             else
450                 _attr.setAttribute(key,value);
451         }
452         
453         /* ------------------------------------------------------------ */
454         public String toString() 
455         {
456             return "FORWARD+"+_attr.toString();
457         }
458 
459         /* ------------------------------------------------------------ */
460         public void clearAttributes()
461         {
462             throw new IllegalStateException();
463         }
464 
465         /* ------------------------------------------------------------ */
466         public void removeAttribute(String name)
467         {
468             setAttribute(name,null);
469         }
470     }
471 
472     /* ------------------------------------------------------------ */
473     private class IncludeAttributes implements Attributes
474     {
475         Attributes _attr;
476         
477         String _requestURI;
478         String _contextPath;
479         String _servletPath;
480         String _pathInfo;
481         String _query;
482         
483         IncludeAttributes(Attributes attributes)
484         {
485             _attr=attributes;
486         }
487         
488         /* ------------------------------------------------------------ */
489         /* ------------------------------------------------------------ */
490         /* ------------------------------------------------------------ */
491         public Object getAttribute(String key)
492         {
493             if (Dispatcher.this._named==null)
494             {
495                 if (key.equals(__INCLUDE_PATH_INFO))    return _pathInfo;
496                 if (key.equals(__INCLUDE_SERVLET_PATH)) return _servletPath;
497                 if (key.equals(__INCLUDE_CONTEXT_PATH)) return _contextPath;
498                 if (key.equals(__INCLUDE_QUERY_STRING)) return _query;
499                 if (key.equals(__INCLUDE_REQUEST_URI))  return _requestURI;
500             }
501             else if (key.startsWith(__INCLUDE_PREFIX)) 
502                     return null;
503             
504             if (key.equals(__INCLUDE_JETTY)) 
505                 return Boolean.TRUE;
506             
507             return _attr.getAttribute(key);
508         }
509         
510         /* ------------------------------------------------------------ */
511         public Enumeration getAttributeNames()
512         {
513             HashSet set=new HashSet();
514             Enumeration e=_attr.getAttributeNames();
515             while(e.hasMoreElements())
516             {
517                 String name=(String)e.nextElement();
518                 if (!name.startsWith(__INCLUDE_PREFIX))
519                     set.add(name);
520             }
521             
522             if (_named==null)
523             {
524                 if (_pathInfo!=null)
525                     set.add(__INCLUDE_PATH_INFO);
526                 else
527                     set.remove(__INCLUDE_PATH_INFO);
528                 set.add(__INCLUDE_REQUEST_URI);
529                 set.add(__INCLUDE_SERVLET_PATH);
530                 set.add(__INCLUDE_CONTEXT_PATH);
531                 if (_query!=null)
532                     set.add(__INCLUDE_QUERY_STRING);
533                 else
534                     set.remove(__INCLUDE_QUERY_STRING);
535             }
536             
537             return Collections.enumeration(set);
538         }
539         
540         /* ------------------------------------------------------------ */
541         public void setAttribute(String key, Object value)
542         {
543             if (_named==null && key.startsWith("javax.servlet."))
544             {
545                 if (key.equals(__INCLUDE_PATH_INFO))         _pathInfo=(String)value;
546                 else if (key.equals(__INCLUDE_REQUEST_URI))  _requestURI=(String)value;
547                 else if (key.equals(__INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
548                 else if (key.equals(__INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
549                 else if (key.equals(__INCLUDE_QUERY_STRING)) _query=(String)value;
550                 else if (value==null)
551                     _attr.removeAttribute(key);
552                 else
553                     _attr.setAttribute(key,value); 
554             }
555             else if (value==null)
556                 _attr.removeAttribute(key);
557             else
558                 _attr.setAttribute(key,value);
559         }
560         
561         /* ------------------------------------------------------------ */
562         public String toString() 
563         {
564             return "INCLUDE+"+_attr.toString();
565         }
566 
567         /* ------------------------------------------------------------ */
568         public void clearAttributes()
569         {
570             throw new IllegalStateException();
571         }
572 
573         /* ------------------------------------------------------------ */
574         public void removeAttribute(String name)
575         {
576             setAttribute(name,null);
577         }
578     }
579 };