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                                 rewrite_old_query = true;
253                             Object values=entry.getValue();
254                             for (int i=0;i<LazyList.size(values);i++)
255                                 parameters.add(name, LazyList.get(values, i));
256                         }
257                     }
258                     
259                     if (old_query != null && old_query.length()>0)
260                     {
261                         if ( rewrite_old_query )
262                         {
263                             StringBuffer overridden_query_string = new StringBuffer();
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                     base_request.setParameters(parameters);
294                     base_request.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                 base_request.setRequestURI(_uri);
323                 base_request.setContextPath(_contextHandler.getContextPath());
324                 base_request.setAttributes(attr);
325                 base_request.setQueryString(query);
326                 
327                 _contextHandler.handle(_path, (HttpServletRequest)request, (HttpServletResponse)response, dispatch);
328                 
329                 if (base_request.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             base_request.setRequestURI(old_uri);
344             base_request.setContextPath(old_context_path);
345             base_request.setServletPath(old_servlet_path);
346             base_request.setPathInfo(old_path_info);
347             base_request.setAttributes(old_attr);
348             base_request.setParameters(old_params);
349             base_request.setQueryString(old_query);
350         }
351     }
352 
353 
354     /* ------------------------------------------------------------ */
355     /* ------------------------------------------------------------ */
356     /* ------------------------------------------------------------ */
357     private class ForwardAttributes implements Attributes
358     {
359         Attributes _attr;
360         
361         String _requestURI;
362         String _contextPath;
363         String _servletPath;
364         String _pathInfo;
365         String _query;
366         
367         ForwardAttributes(Attributes attributes)
368         {
369             _attr=attributes;
370         }
371         
372         /* ------------------------------------------------------------ */
373         public Object getAttribute(String key)
374         {
375             if (Dispatcher.this._named==null)
376             {
377                 if (key.equals(__FORWARD_PATH_INFO))    return _pathInfo;
378                 if (key.equals(__FORWARD_REQUEST_URI))  return _requestURI;
379                 if (key.equals(__FORWARD_SERVLET_PATH)) return _servletPath;
380                 if (key.equals(__FORWARD_CONTEXT_PATH)) return _contextPath;
381                 if (key.equals(__FORWARD_QUERY_STRING)) return _query;
382             }
383             
384             if (key.startsWith(__INCLUDE_PREFIX) || key.equals(__INCLUDE_JETTY) )
385                 return null;
386 
387             if (key.equals(__FORWARD_JETTY)) 
388                 return Boolean.TRUE;
389             
390             return _attr.getAttribute(key);
391         }
392         
393         /* ------------------------------------------------------------ */
394         public Enumeration getAttributeNames()
395         {
396             HashSet set=new HashSet();
397             Enumeration e=_attr.getAttributeNames();
398             while(e.hasMoreElements())
399             {
400                 String name=(String)e.nextElement();
401                 if (!name.startsWith(__INCLUDE_PREFIX) &&
402                     !name.startsWith(__FORWARD_PREFIX))
403                     set.add(name);
404             }
405             
406             if (_named==null)
407             {
408                 if (_pathInfo!=null)
409                     set.add(__FORWARD_PATH_INFO);
410                 else
411                     set.remove(__FORWARD_PATH_INFO);
412                 set.add(__FORWARD_REQUEST_URI);
413                 set.add(__FORWARD_SERVLET_PATH);
414                 set.add(__FORWARD_CONTEXT_PATH);
415                 if (_query!=null)
416                     set.add(__FORWARD_QUERY_STRING);
417                 else
418                     set.remove(__FORWARD_QUERY_STRING);
419             }
420 
421             return Collections.enumeration(set);
422         }
423         
424         /* ------------------------------------------------------------ */
425         public void setAttribute(String key, Object value)
426         {
427             if (_named==null && key.startsWith("javax.servlet."))
428             {
429                 if (key.equals(__FORWARD_PATH_INFO))         _pathInfo=(String)value;
430                 else if (key.equals(__FORWARD_REQUEST_URI))  _requestURI=(String)value;
431                 else if (key.equals(__FORWARD_SERVLET_PATH)) _servletPath=(String)value;
432                 else if (key.equals(__FORWARD_CONTEXT_PATH)) _contextPath=(String)value;
433                 else if (key.equals(__FORWARD_QUERY_STRING)) _query=(String)value;
434                 
435                 else if (value==null)
436                     _attr.removeAttribute(key);
437                 else
438                     _attr.setAttribute(key,value); 
439             }
440             else if (value==null)
441                 _attr.removeAttribute(key);
442             else
443                 _attr.setAttribute(key,value);
444         }
445         
446         /* ------------------------------------------------------------ */
447         public String toString() 
448         {
449             return "FORWARD+"+_attr.toString();
450         }
451 
452         /* ------------------------------------------------------------ */
453         public void clearAttributes()
454         {
455             throw new IllegalStateException();
456         }
457 
458         /* ------------------------------------------------------------ */
459         public void removeAttribute(String name)
460         {
461             setAttribute(name,null);
462         }
463     }
464 
465     /* ------------------------------------------------------------ */
466     private class IncludeAttributes implements Attributes
467     {
468         Attributes _attr;
469         
470         String _requestURI;
471         String _contextPath;
472         String _servletPath;
473         String _pathInfo;
474         String _query;
475         
476         IncludeAttributes(Attributes attributes)
477         {
478             _attr=attributes;
479         }
480         
481         /* ------------------------------------------------------------ */
482         /* ------------------------------------------------------------ */
483         /* ------------------------------------------------------------ */
484         public Object getAttribute(String key)
485         {
486             if (Dispatcher.this._named==null)
487             {
488                 if (key.equals(__INCLUDE_PATH_INFO))    return _pathInfo;
489                 if (key.equals(__INCLUDE_SERVLET_PATH)) return _servletPath;
490                 if (key.equals(__INCLUDE_CONTEXT_PATH)) return _contextPath;
491                 if (key.equals(__INCLUDE_QUERY_STRING)) return _query;
492                 if (key.equals(__INCLUDE_REQUEST_URI))  return _requestURI;
493             }
494             else if (key.startsWith(__INCLUDE_PREFIX)) 
495                     return null;
496             
497             if (key.equals(__INCLUDE_JETTY)) 
498                 return Boolean.TRUE;
499             
500             return _attr.getAttribute(key);
501         }
502         
503         /* ------------------------------------------------------------ */
504         public Enumeration getAttributeNames()
505         {
506             HashSet set=new HashSet();
507             Enumeration e=_attr.getAttributeNames();
508             while(e.hasMoreElements())
509             {
510                 String name=(String)e.nextElement();
511                 if (!name.startsWith(__INCLUDE_PREFIX))
512                     set.add(name);
513             }
514             
515             if (_named==null)
516             {
517                 if (_pathInfo!=null)
518                     set.add(__INCLUDE_PATH_INFO);
519                 else
520                     set.remove(__INCLUDE_PATH_INFO);
521                 set.add(__INCLUDE_REQUEST_URI);
522                 set.add(__INCLUDE_SERVLET_PATH);
523                 set.add(__INCLUDE_CONTEXT_PATH);
524                 if (_query!=null)
525                     set.add(__INCLUDE_QUERY_STRING);
526                 else
527                     set.remove(__INCLUDE_QUERY_STRING);
528             }
529             
530             return Collections.enumeration(set);
531         }
532         
533         /* ------------------------------------------------------------ */
534         public void setAttribute(String key, Object value)
535         {
536             if (_named==null && key.startsWith("javax.servlet."))
537             {
538                 if (key.equals(__INCLUDE_PATH_INFO))         _pathInfo=(String)value;
539                 else if (key.equals(__INCLUDE_REQUEST_URI))  _requestURI=(String)value;
540                 else if (key.equals(__INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
541                 else if (key.equals(__INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
542                 else if (key.equals(__INCLUDE_QUERY_STRING)) _query=(String)value;
543                 else if (value==null)
544                     _attr.removeAttribute(key);
545                 else
546                     _attr.setAttribute(key,value); 
547             }
548             else if (value==null)
549                 _attr.removeAttribute(key);
550             else
551                 _attr.setAttribute(key,value);
552         }
553         
554         /* ------------------------------------------------------------ */
555         public String toString() 
556         {
557             return "INCLUDE+"+_attr.toString();
558         }
559 
560         /* ------------------------------------------------------------ */
561         public void clearAttributes()
562         {
563             throw new IllegalStateException();
564         }
565 
566         /* ------------------------------------------------------------ */
567         public void removeAttribute(String name)
568         {
569             setAttribute(name,null);
570         }
571     }
572 };