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      */
type(String type)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      */
Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)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      */
Dispatcher(ContextHandler contextHandler,String name)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      */
forward(ServletRequest request, ServletResponse response)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      */
error(ServletRequest request, ServletResponse response)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      */
include(ServletRequest request, ServletResponse response)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      */
forward(ServletRequest request, ServletResponse response, int dispatch)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 
ForwardAttributes(Attributes attributes)374         ForwardAttributes(Attributes attributes)
375         {
376             _attr=attributes;
377         }
378 
379         /* ------------------------------------------------------------ */
getAttribute(String key)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         /* ------------------------------------------------------------ */
getAttributeNames()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         /* ------------------------------------------------------------ */
setAttribute(String key, Object value)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         /* ------------------------------------------------------------ */
toString()454         public String toString()
455         {
456             return "FORWARD+"+_attr.toString();
457         }
458 
459         /* ------------------------------------------------------------ */
clearAttributes()460         public void clearAttributes()
461         {
462             throw new IllegalStateException();
463         }
464 
465         /* ------------------------------------------------------------ */
removeAttribute(String name)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 
IncludeAttributes(Attributes attributes)483         IncludeAttributes(Attributes attributes)
484         {
485             _attr=attributes;
486         }
487 
488         /* ------------------------------------------------------------ */
489         /* ------------------------------------------------------------ */
490         /* ------------------------------------------------------------ */
getAttribute(String key)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         /* ------------------------------------------------------------ */
getAttributeNames()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         /* ------------------------------------------------------------ */
setAttribute(String key, Object value)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         /* ------------------------------------------------------------ */
toString()562         public String toString()
563         {
564             return "INCLUDE+"+_attr.toString();
565         }
566 
567         /* ------------------------------------------------------------ */
clearAttributes()568         public void clearAttributes()
569         {
570             throw new IllegalStateException();
571         }
572 
573         /* ------------------------------------------------------------ */
removeAttribute(String name)574         public void removeAttribute(String name)
575         {
576             setAttribute(name,null);
577         }
578     }
579 };
580