View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.util.Collections;
23  import java.util.Enumeration;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import javax.servlet.DispatcherType;
29  import javax.servlet.RequestDispatcher;
30  import javax.servlet.ServletException;
31  import javax.servlet.ServletRequest;
32  import javax.servlet.ServletResponse;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  
36  import org.eclipse.jetty.server.handler.ContextHandler;
37  import org.eclipse.jetty.util.Attributes;
38  import org.eclipse.jetty.util.LazyList;
39  import org.eclipse.jetty.util.MultiMap;
40  import org.eclipse.jetty.util.UrlEncoded;
41  
42  /* ------------------------------------------------------------ */
43  /** Servlet RequestDispatcher.
44   * 
45   * 
46   */
47  public class Dispatcher implements RequestDispatcher
48  {
49      /** Dispatch include attribute names */
50      public final static String __INCLUDE_PREFIX="javax.servlet.include.";
51  
52      /** Dispatch include attribute names */
53      public final static String __FORWARD_PREFIX="javax.servlet.forward.";
54  
55      /** JSP attributes */
56      public final static String __JSP_FILE="org.apache.catalina.jsp_file";
57  
58      /* ------------------------------------------------------------ */
59      private final ContextHandler _contextHandler;
60      private final String _uri;
61      private final String _path;
62      private final String _dQuery;
63      private final String _named;
64      
65      /* ------------------------------------------------------------ */
66      /**
67       * @param contextHandler
68       * @param uri
69       * @param pathInContext
70       * @param query
71       */
72      public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
73      {
74          _contextHandler=contextHandler;
75          _uri=uri;
76          _path=pathInContext;
77          _dQuery=query;
78          _named=null;
79      }
80  
81  
82      /* ------------------------------------------------------------ */
83      /** Constructor. 
84       * @param contextHandler
85       * @param name
86       */
87      public Dispatcher(ContextHandler contextHandler,String name)
88          throws IllegalStateException
89      {
90          _contextHandler=contextHandler;
91          _named=name;
92          _uri=null;
93          _path=null;
94          _dQuery=null;
95      }
96      
97      /* ------------------------------------------------------------ */
98      /* 
99       * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
100      */
101     public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
102     {
103         forward(request, response, DispatcherType.FORWARD);
104     }
105     
106     /* ------------------------------------------------------------ */
107     /* 
108      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
109      */
110     public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
111     {
112         forward(request, response, DispatcherType.ERROR);
113     }
114     
115     /* ------------------------------------------------------------ */
116     /* 
117      * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
118      */
119     public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
120     {
121         Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
122       
123         
124         if (!(request instanceof HttpServletRequest))
125             request = new ServletRequestHttpWrapper(request);
126         if (!(response instanceof HttpServletResponse))
127             response = new ServletResponseHttpWrapper(response);
128             
129         
130         // TODO - allow stream or writer????
131         
132         final DispatcherType old_type = baseRequest.getDispatcherType();
133         final Attributes old_attr=baseRequest.getAttributes();
134         MultiMap old_params=baseRequest.getParameters();
135         try
136         {
137             baseRequest.setDispatcherType(DispatcherType.INCLUDE);
138             baseRequest.getConnection().include();
139             if (_named!=null)
140                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
141             else 
142             {
143                 String query=_dQuery;
144                 
145                 if (query!=null)
146                 {
147                     // force parameter extraction
148                     if (old_params==null)
149                     {
150                         baseRequest.extractParameters();
151                         old_params=baseRequest.getParameters();
152                     }
153                     
154                     MultiMap parameters=new MultiMap();
155                     UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
156                     
157                     if (old_params!=null && old_params.size()>0)
158                     {
159                         // Merge parameters.
160                         Iterator iter = old_params.entrySet().iterator();
161                         while (iter.hasNext())
162                         {
163                             Map.Entry entry = (Map.Entry)iter.next();
164                             String name=(String)entry.getKey();
165                             Object values=entry.getValue();
166                             for (int i=0;i<LazyList.size(values);i++)
167                                 parameters.add(name, LazyList.get(values, i));
168                         }
169                     }
170                     baseRequest.setParameters(parameters);
171                 }
172                 
173                 IncludeAttributes attr = new IncludeAttributes(old_attr); 
174                 
175                 attr._requestURI=_uri;
176                 attr._contextPath=_contextHandler.getContextPath();
177                 attr._servletPath=null; // set by ServletHandler
178                 attr._pathInfo=_path;
179                 attr._query=query;
180                 
181                 baseRequest.setAttributes(attr);
182                 
183                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
184             }
185         }
186         finally
187         {
188             baseRequest.setAttributes(old_attr);
189             baseRequest.getConnection().included();
190             baseRequest.setParameters(old_params);
191             baseRequest.setDispatcherType(old_type);
192         }
193     }
194 
195     
196     /* ------------------------------------------------------------ */
197     /* 
198      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
199      */
200     protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
201     {
202         Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
203         Response base_response=baseRequest.getResponse();
204         response.resetBuffer();
205         base_response.fwdReset();
206        
207 
208         if (!(request instanceof HttpServletRequest))
209             request = new ServletRequestHttpWrapper(request);
210         if (!(response instanceof HttpServletResponse))
211             response = new ServletResponseHttpWrapper(response);
212         
213         final boolean old_handled=baseRequest.isHandled();
214         final String old_uri=baseRequest.getRequestURI();
215         final String old_context_path=baseRequest.getContextPath();
216         final String old_servlet_path=baseRequest.getServletPath();
217         final String old_path_info=baseRequest.getPathInfo();
218         final String old_query=baseRequest.getQueryString();
219         final Attributes old_attr=baseRequest.getAttributes();
220         final DispatcherType old_type=baseRequest.getDispatcherType();
221         MultiMap<String> old_params=baseRequest.getParameters();
222         
223         try
224         {
225             baseRequest.setHandled(false);
226             baseRequest.setDispatcherType(dispatch);
227             
228             if (_named!=null)
229                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
230             else 
231             {
232                 
233                 // process any query string from the dispatch URL
234                 String query=_dQuery;
235                 if (query!=null)
236                 {
237                     // force parameter extraction
238                     if (old_params==null)
239                     {
240                         baseRequest.extractParameters();
241                         old_params=baseRequest.getParameters();
242                     }
243                     
244                     baseRequest.mergeQueryString(query);
245                 }
246                 
247                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
248                 
249                 //If we have already been forwarded previously, then keep using the established 
250                 //original value. Otherwise, this is the first forward and we need to establish the values.
251                 //Note: the established value on the original request for pathInfo and
252                 //for queryString is allowed to be null, but cannot be null for the other values.
253                 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
254                 {
255                     attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
256                     attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
257                     attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
258                     attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
259                     attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
260                 }
261                 else
262                 {
263                     attr._pathInfo=old_path_info;
264                     attr._query=old_query;
265                     attr._requestURI=old_uri;
266                     attr._contextPath=old_context_path;
267                     attr._servletPath=old_servlet_path;
268                 }     
269                 
270                 baseRequest.setRequestURI(_uri);
271                 baseRequest.setContextPath(_contextHandler.getContextPath());
272                 baseRequest.setServletPath(null);
273                 baseRequest.setPathInfo(_uri);
274                 baseRequest.setAttributes(attr);
275                 
276                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
277                 
278                 if (!baseRequest.getAsyncContinuation().isAsyncStarted())
279                     commitResponse(response,baseRequest);
280             }
281         }
282         finally
283         {
284             baseRequest.setHandled(old_handled);
285             baseRequest.setRequestURI(old_uri);
286             baseRequest.setContextPath(old_context_path);
287             baseRequest.setServletPath(old_servlet_path);
288             baseRequest.setPathInfo(old_path_info);
289             baseRequest.setAttributes(old_attr);
290             baseRequest.setParameters(old_params);
291             baseRequest.setQueryString(old_query);
292             baseRequest.setDispatcherType(old_type);
293         }
294     }
295 
296 
297     /* ------------------------------------------------------------ */
298     private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
299     {
300         if (baseRequest.getResponse().isWriting())
301         {
302             try
303             {
304                 response.getWriter().close();
305             }
306             catch (IllegalStateException e)
307             {
308                 response.getOutputStream().close();
309             }
310         }
311         else
312         {
313             try
314             {
315                 response.getOutputStream().close();
316             }
317             catch (IllegalStateException e)
318             {
319                 response.getWriter().close();
320             }
321         }
322     }
323 
324 
325     /* ------------------------------------------------------------ */
326     /* ------------------------------------------------------------ */
327     /* ------------------------------------------------------------ */
328     private class ForwardAttributes implements Attributes
329     {
330         final Attributes _attr;
331         
332         String _requestURI;
333         String _contextPath;
334         String _servletPath;
335         String _pathInfo;
336         String _query;
337         
338         ForwardAttributes(Attributes attributes)
339         {
340             _attr=attributes;
341         }
342         
343         /* ------------------------------------------------------------ */
344         public Object getAttribute(String key)
345         {
346             if (Dispatcher.this._named==null)
347             {
348                 if (key.equals(FORWARD_PATH_INFO))    
349                     return _pathInfo;
350                 if (key.equals(FORWARD_REQUEST_URI))  
351                     return _requestURI;
352                 if (key.equals(FORWARD_SERVLET_PATH)) 
353                     return _servletPath;
354                 if (key.equals(FORWARD_CONTEXT_PATH)) 
355                     return _contextPath;
356                 if (key.equals(FORWARD_QUERY_STRING)) 
357                     return _query;
358             }
359             
360             if (key.startsWith(__INCLUDE_PREFIX))
361                 return null;
362             
363             return _attr.getAttribute(key);
364         }
365         
366         /* ------------------------------------------------------------ */
367         public Enumeration getAttributeNames()
368         {
369             HashSet set=new HashSet();
370             Enumeration e=_attr.getAttributeNames();
371             while(e.hasMoreElements())
372             {
373                 String name=(String)e.nextElement();
374                 if (!name.startsWith(__INCLUDE_PREFIX) &&
375                     !name.startsWith(__FORWARD_PREFIX))
376                     set.add(name);
377             }
378             
379             if (_named==null)
380             {
381                 if (_pathInfo!=null)
382                     set.add(FORWARD_PATH_INFO);
383                 else
384                     set.remove(FORWARD_PATH_INFO);
385                 set.add(FORWARD_REQUEST_URI);
386                 set.add(FORWARD_SERVLET_PATH);
387                 set.add(FORWARD_CONTEXT_PATH);
388                 if (_query!=null)
389                     set.add(FORWARD_QUERY_STRING);
390                 else
391                     set.remove(FORWARD_QUERY_STRING);
392             }
393 
394             return Collections.enumeration(set);
395         }
396         
397         /* ------------------------------------------------------------ */
398         public void setAttribute(String key, Object value)
399         {
400             if (_named==null && key.startsWith("javax.servlet."))
401             {
402                 if (key.equals(FORWARD_PATH_INFO))         
403                     _pathInfo=(String)value;
404                 else if (key.equals(FORWARD_REQUEST_URI))  
405                     _requestURI=(String)value;
406                 else if (key.equals(FORWARD_SERVLET_PATH)) 
407                     _servletPath=(String)value;
408                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
409                     _contextPath=(String)value;
410                 else if (key.equals(FORWARD_QUERY_STRING)) 
411                     _query=(String)value;
412                 
413                 else if (value==null)
414                     _attr.removeAttribute(key);
415                 else
416                     _attr.setAttribute(key,value); 
417             }
418             else if (value==null)
419                 _attr.removeAttribute(key);
420             else
421                 _attr.setAttribute(key,value);
422         }
423         
424         /* ------------------------------------------------------------ */
425         @Override
426         public String toString() 
427         {
428             return "FORWARD+"+_attr.toString();
429         }
430 
431         /* ------------------------------------------------------------ */
432         public void clearAttributes()
433         {
434             throw new IllegalStateException();
435         }
436 
437         /* ------------------------------------------------------------ */
438         public void removeAttribute(String name)
439         {
440             setAttribute(name,null);
441         }
442     }
443 
444     /* ------------------------------------------------------------ */
445     private class IncludeAttributes implements Attributes
446     {
447         final Attributes _attr;
448         
449         String _requestURI;
450         String _contextPath;
451         String _servletPath;
452         String _pathInfo;
453         String _query;
454         
455         IncludeAttributes(Attributes attributes)
456         {
457             _attr=attributes;
458         }
459         
460         /* ------------------------------------------------------------ */
461         /* ------------------------------------------------------------ */
462         /* ------------------------------------------------------------ */
463         public Object getAttribute(String key)
464         {
465             if (Dispatcher.this._named==null)
466             {
467                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
468                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
469                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
470                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
471                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
472             }
473             else if (key.startsWith(__INCLUDE_PREFIX)) 
474                     return null;
475             
476             
477             return _attr.getAttribute(key);
478         }
479         
480         /* ------------------------------------------------------------ */
481         public Enumeration getAttributeNames()
482         {
483             HashSet set=new HashSet();
484             Enumeration e=_attr.getAttributeNames();
485             while(e.hasMoreElements())
486             {
487                 String name=(String)e.nextElement();
488                 if (!name.startsWith(__INCLUDE_PREFIX))
489                     set.add(name);
490             }
491             
492             if (_named==null)
493             {
494                 if (_pathInfo!=null)
495                     set.add(INCLUDE_PATH_INFO);
496                 else
497                     set.remove(INCLUDE_PATH_INFO);
498                 set.add(INCLUDE_REQUEST_URI);
499                 set.add(INCLUDE_SERVLET_PATH);
500                 set.add(INCLUDE_CONTEXT_PATH);
501                 if (_query!=null)
502                     set.add(INCLUDE_QUERY_STRING);
503                 else
504                     set.remove(INCLUDE_QUERY_STRING);
505             }
506             
507             return Collections.enumeration(set);
508         }
509         
510         /* ------------------------------------------------------------ */
511         public void setAttribute(String key, Object value)
512         {
513             if (_named==null && key.startsWith("javax.servlet."))
514             {
515                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
516                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
517                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
518                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
519                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
520                 else if (value==null)
521                     _attr.removeAttribute(key);
522                 else
523                     _attr.setAttribute(key,value); 
524             }
525             else if (value==null)
526                 _attr.removeAttribute(key);
527             else
528                 _attr.setAttribute(key,value);
529         }
530         
531         /* ------------------------------------------------------------ */
532         @Override
533         public String toString() 
534         {
535             return "INCLUDE+"+_attr.toString();
536         }
537 
538         /* ------------------------------------------------------------ */
539         public void clearAttributes()
540         {
541             throw new IllegalStateException();
542         }
543 
544         /* ------------------------------------------------------------ */
545         public void removeAttribute(String name)
546         {
547             setAttribute(name,null);
548         }
549     }
550 }