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                     if (old_params!=null && old_params.size()>0)
235                     {
236                         // Merge parameters.
237                         Iterator iter = old_params.entrySet().iterator();
238                         while (iter.hasNext())
239                         {
240                             Map.Entry entry = (Map.Entry)iter.next();
241                             String name=(String)entry.getKey();
242                             Object values=entry.getValue();
243                             for (int i=0;i<LazyList.size(values);i++)
244                                 parameters.add(name, LazyList.get(values, i));
245                         }
246                     }
247                     
248                     if (old_query!=null && old_query.length()>0)
249                         query=query+"&"+old_query;
250                     
251                     base_request.setParameters(parameters);
252                     base_request.setQueryString(query);
253                 }
254                 
255                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
256                 
257                 //If we have already been forwarded previously, then keep using the established 
258                 //original value. Otherwise, this is the first forward and we need to establish the values.
259                 //Note: the established value on the original request for pathInfo and
260                 //for queryString is allowed to be null, but cannot be null for the other values.
261                 if ((String)old_attr.getAttribute(__FORWARD_REQUEST_URI) != null)
262                 {
263                     attr._pathInfo=(String)old_attr.getAttribute(__FORWARD_PATH_INFO);
264                     attr._query=(String)old_attr.getAttribute(__FORWARD_QUERY_STRING);
265                     attr._requestURI=(String)old_attr.getAttribute(__FORWARD_REQUEST_URI);
266                     attr._contextPath=(String)old_attr.getAttribute(__FORWARD_CONTEXT_PATH);
267                     attr._servletPath=(String)old_attr.getAttribute(__FORWARD_SERVLET_PATH);
268                 }
269                 else
270                 {
271                     attr._pathInfo=old_path_info;
272                     attr._query=old_query;
273                     attr._requestURI=old_uri;
274                     attr._contextPath=old_context_path;
275                     attr._servletPath=old_servlet_path;
276                 }                
277    
278               
279                 
280                 base_request.setRequestURI(_uri);
281                 base_request.setContextPath(_contextHandler.getContextPath());
282                 base_request.setAttributes(attr);
283                 base_request.setQueryString(query);
284                 
285                 _contextHandler.handle(_path, (HttpServletRequest)request, (HttpServletResponse)response, dispatch);
286                 
287                 if (base_request.getConnection().getResponse().isWriting())
288                 {
289                     try {response.getWriter().close();}
290                     catch(IllegalStateException e) { response.getOutputStream().close(); }
291                 }
292                 else
293                 {
294                     try {response.getOutputStream().close();}
295                     catch(IllegalStateException e) { response.getWriter().close(); }
296                 }
297             }
298         }
299         finally
300         {
301             base_request.setRequestURI(old_uri);
302             base_request.setContextPath(old_context_path);
303             base_request.setServletPath(old_servlet_path);
304             base_request.setPathInfo(old_path_info);
305             base_request.setAttributes(old_attr);
306             base_request.setParameters(old_params);
307             base_request.setQueryString(old_query);
308         }
309     }
310 
311 
312     /* ------------------------------------------------------------ */
313     /* ------------------------------------------------------------ */
314     /* ------------------------------------------------------------ */
315     private class ForwardAttributes implements Attributes
316     {
317         Attributes _attr;
318         
319         String _requestURI;
320         String _contextPath;
321         String _servletPath;
322         String _pathInfo;
323         String _query;
324         
325         ForwardAttributes(Attributes attributes)
326         {
327             _attr=attributes;
328         }
329         
330         /* ------------------------------------------------------------ */
331         public Object getAttribute(String key)
332         {
333             if (Dispatcher.this._named==null)
334             {
335                 if (key.equals(__FORWARD_PATH_INFO))    return _pathInfo;
336                 if (key.equals(__FORWARD_REQUEST_URI))  return _requestURI;
337                 if (key.equals(__FORWARD_SERVLET_PATH)) return _servletPath;
338                 if (key.equals(__FORWARD_CONTEXT_PATH)) return _contextPath;
339                 if (key.equals(__FORWARD_QUERY_STRING)) return _query;
340             }
341 
342             if (key.equals(__FORWARD_JETTY)) 
343                 return Boolean.TRUE;
344             
345             return _attr.getAttribute(key);
346         }
347         
348         /* ------------------------------------------------------------ */
349         public Enumeration getAttributeNames()
350         {
351             HashSet set=new HashSet();
352             Enumeration e=_attr.getAttributeNames();
353             while(e.hasMoreElements())
354             {
355                 String name=(String)e.nextElement();
356                 if (!name.startsWith(__INCLUDE_PREFIX) &&
357                     !name.startsWith(__FORWARD_PREFIX))
358                     set.add(name);
359             }
360             
361             if (_named==null)
362             {
363                 if (_pathInfo!=null)
364                     set.add(__FORWARD_PATH_INFO);
365                 else
366                     set.remove(__FORWARD_PATH_INFO);
367                 set.add(__FORWARD_REQUEST_URI);
368                 set.add(__FORWARD_SERVLET_PATH);
369                 set.add(__FORWARD_CONTEXT_PATH);
370                 if (_query!=null)
371                     set.add(__FORWARD_QUERY_STRING);
372                 else
373                     set.remove(__FORWARD_QUERY_STRING);
374             }
375 
376             return Collections.enumeration(set);
377         }
378         
379         /* ------------------------------------------------------------ */
380         public void setAttribute(String key, Object value)
381         {
382             if (_named==null && key.startsWith("javax.servlet."))
383             {
384                 if (key.equals(__FORWARD_PATH_INFO))         _pathInfo=(String)value;
385                 else if (key.equals(__FORWARD_REQUEST_URI))  _requestURI=(String)value;
386                 else if (key.equals(__FORWARD_SERVLET_PATH)) _servletPath=(String)value;
387                 else if (key.equals(__FORWARD_CONTEXT_PATH)) _contextPath=(String)value;
388                 else if (key.equals(__FORWARD_QUERY_STRING)) _query=(String)value;
389                 
390                 else if (value==null)
391                     _attr.removeAttribute(key);
392                 else
393                     _attr.setAttribute(key,value); 
394             }
395             else if (value==null)
396                 _attr.removeAttribute(key);
397             else
398                 _attr.setAttribute(key,value);
399         }
400         
401         /* ------------------------------------------------------------ */
402         public String toString() 
403         {
404             return "FORWARD+"+_attr.toString();
405         }
406 
407         /* ------------------------------------------------------------ */
408         public void clearAttributes()
409         {
410             throw new IllegalStateException();
411         }
412 
413         /* ------------------------------------------------------------ */
414         public void removeAttribute(String name)
415         {
416             setAttribute(name,null);
417         }
418     }
419 
420     /* ------------------------------------------------------------ */
421     private class IncludeAttributes implements Attributes
422     {
423         Attributes _attr;
424         
425         String _requestURI;
426         String _contextPath;
427         String _servletPath;
428         String _pathInfo;
429         String _query;
430         
431         IncludeAttributes(Attributes attributes)
432         {
433             _attr=attributes;
434         }
435         
436         /* ------------------------------------------------------------ */
437         /* ------------------------------------------------------------ */
438         /* ------------------------------------------------------------ */
439         public Object getAttribute(String key)
440         {
441             if (Dispatcher.this._named==null)
442             {
443                 if (key.equals(__INCLUDE_PATH_INFO))    return _pathInfo;
444                 if (key.equals(__INCLUDE_SERVLET_PATH)) return _servletPath;
445                 if (key.equals(__INCLUDE_CONTEXT_PATH)) return _contextPath;
446                 if (key.equals(__INCLUDE_QUERY_STRING)) return _query;
447                 if (key.equals(__INCLUDE_REQUEST_URI))  return _requestURI;
448             }
449             else if (key.startsWith(__INCLUDE_PREFIX)) 
450                     return null;
451             
452             if (key.equals(__INCLUDE_JETTY)) 
453                 return Boolean.TRUE;
454             
455             return _attr.getAttribute(key);
456         }
457         
458         /* ------------------------------------------------------------ */
459         public Enumeration getAttributeNames()
460         {
461             HashSet set=new HashSet();
462             Enumeration e=_attr.getAttributeNames();
463             while(e.hasMoreElements())
464             {
465                 String name=(String)e.nextElement();
466                 if (!name.startsWith(__INCLUDE_PREFIX) &&
467                     !name.startsWith(__FORWARD_PREFIX))
468                     set.add(name);
469             }
470             
471             if (_named==null)
472             {
473                 if (_pathInfo!=null)
474                     set.add(__INCLUDE_PATH_INFO);
475                 else
476                     set.remove(__INCLUDE_PATH_INFO);
477                 set.add(__INCLUDE_REQUEST_URI);
478                 set.add(__INCLUDE_SERVLET_PATH);
479                 set.add(__INCLUDE_CONTEXT_PATH);
480                 if (_query!=null)
481                     set.add(__INCLUDE_QUERY_STRING);
482                 else
483                     set.remove(__INCLUDE_QUERY_STRING);
484             }
485             
486             return Collections.enumeration(set);
487         }
488         
489         /* ------------------------------------------------------------ */
490         public void setAttribute(String key, Object value)
491         {
492             if (_named==null && key.startsWith("javax.servlet."))
493             {
494                 if (key.equals(__INCLUDE_PATH_INFO))         _pathInfo=(String)value;
495                 else if (key.equals(__INCLUDE_REQUEST_URI))  _requestURI=(String)value;
496                 else if (key.equals(__INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
497                 else if (key.equals(__INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
498                 else if (key.equals(__INCLUDE_QUERY_STRING)) _query=(String)value;
499                 else if (value==null)
500                     _attr.removeAttribute(key);
501                 else
502                     _attr.setAttribute(key,value); 
503             }
504             else if (value==null)
505                 _attr.removeAttribute(key);
506             else
507                 _attr.setAttribute(key,value);
508         }
509         
510         /* ------------------------------------------------------------ */
511         public String toString() 
512         {
513             return "INCLUDE+"+_attr.toString();
514         }
515 
516         /* ------------------------------------------------------------ */
517         public void clearAttributes()
518         {
519             throw new IllegalStateException();
520         }
521 
522         /* ------------------------------------------------------------ */
523         public void removeAttribute(String name)
524         {
525             setAttribute(name,null);
526         }
527     }
528 };