View Javadoc

1   // ========================================================================
2   // Copyright (c) 1999-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.servlet;
15  
16  import java.io.IOException;
17  import java.util.Enumeration;
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import javax.servlet.ServletContext;
22  import javax.servlet.ServletException;
23  import javax.servlet.UnavailableException;
24  import javax.servlet.http.HttpServlet;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletRequestWrapper;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.eclipse.jetty.server.Dispatcher;
30  import org.eclipse.jetty.server.Handler;
31  import org.eclipse.jetty.server.HttpConnection;
32  import org.eclipse.jetty.server.Request;
33  import org.eclipse.jetty.server.handler.ContextHandler;
34  import org.eclipse.jetty.server.handler.HandlerWrapper;
35  import org.eclipse.jetty.util.LazyList;
36  import org.eclipse.jetty.util.URIUtil;
37  import org.eclipse.jetty.util.log.Log;
38  import org.eclipse.jetty.util.log.Logger;
39  
40  /* ------------------------------------------------------------ */
41  /**  Dynamic Servlet Invoker.  
42   * This servlet invokes anonymous servlets that have not been defined   
43   * in the web.xml or by other means. The first element of the pathInfo  
44   * of a request passed to the envoker is treated as a servlet name for  
45   * an existing servlet, or as a class name of a new servlet.            
46   * This servlet is normally mapped to /servlet/*                        
47   * This servlet support the following initParams:                       
48   * <PRE>                                                                     
49   *  nonContextServlets       If false, the invoker can only load        
50   *                           servlets from the contexts classloader.    
51   *                           This is false by default and setting this  
52   *                           to true may have security implications.    
53   *                                                                      
54   *  verbose                  If true, log dynamic loads                 
55   *                                                                      
56   *  *                        All other parameters are copied to the     
57   *                           each dynamic servlet as init parameters    
58   * </PRE>
59   * @version $Id: Invoker.java 4780 2009-03-17 15:36:08Z jesse $
60   * 
61   */
62  public class Invoker extends HttpServlet
63  {
64      private static final Logger LOG = Log.getLogger(Invoker.class);
65  
66  
67      private ContextHandler _contextHandler;
68      private ServletHandler _servletHandler;
69      private Map.Entry _invokerEntry;
70      private Map _parameters;
71      private boolean _nonContextServlets;
72      private boolean _verbose;
73          
74      /* ------------------------------------------------------------ */
75      public void init()
76      {
77          ServletContext config=getServletContext();
78          _contextHandler=((ContextHandler.Context)config).getContextHandler();
79  
80          Handler handler=_contextHandler.getHandler();
81          while (handler!=null && !(handler instanceof ServletHandler) && (handler instanceof HandlerWrapper))
82              handler=((HandlerWrapper)handler).getHandler();
83          _servletHandler = (ServletHandler)handler;
84          Enumeration e = getInitParameterNames();
85          while(e.hasMoreElements())
86          {
87              String param=(String)e.nextElement();
88              String value=getInitParameter(param);
89              String lvalue=value.toLowerCase();
90              if ("nonContextServlets".equals(param))
91              {
92                  _nonContextServlets=value.length()>0 && lvalue.startsWith("t");
93              }
94              if ("verbose".equals(param))
95              {
96                  _verbose=value.length()>0 && lvalue.startsWith("t");
97              }
98              else
99              {
100                 if (_parameters==null)
101                     _parameters=new HashMap();
102                 _parameters.put(param,value);
103             }
104         }
105     }
106     
107     /* ------------------------------------------------------------ */
108     protected void service(HttpServletRequest request, HttpServletResponse response)
109 	throws ServletException, IOException
110     {
111         // Get the requested path and info
112         boolean included=false;
113         String servlet_path=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
114         if (servlet_path==null)
115             servlet_path=request.getServletPath();
116         else
117             included=true;
118         String path_info = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
119         if (path_info==null)
120             path_info=request.getPathInfo();
121         
122         // Get the servlet class
123         String servlet = path_info;
124         if (servlet==null || servlet.length()<=1 )
125         {
126             response.sendError(404);
127             return;
128         }
129         
130         
131         int i0=servlet.charAt(0)=='/'?1:0;
132         int i1=servlet.indexOf('/',i0);
133         servlet=i1<0?servlet.substring(i0):servlet.substring(i0,i1);
134 
135         // look for a named holder
136         ServletHolder[] holders = _servletHandler.getServlets();
137         ServletHolder holder = getHolder (holders, servlet);
138        
139         if (holder!=null)
140         {
141             // Found a named servlet (from a user's web.xml file) so
142             // now we add a mapping for it
143             if (LOG.isDebugEnabled())
144                 LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*");
145             ServletMapping mapping = new ServletMapping();
146             mapping.setServletName(servlet);
147             mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*");
148             _servletHandler.setServletMappings((ServletMapping[])LazyList.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
149         }
150         else
151         {
152             // look for a class mapping
153             if (servlet.endsWith(".class"))
154                 servlet=servlet.substring(0,servlet.length()-6);
155             if (servlet==null || servlet.length()==0)
156             {
157                 response.sendError(404);
158                 return;
159             }   
160         
161             synchronized(_servletHandler)
162             {
163                 // find the entry for the invoker (me)
164                  _invokerEntry=_servletHandler.getHolderEntry(servlet_path);
165             
166                 // Check for existing mapping (avoid threaded race).
167                 String path=URIUtil.addPaths(servlet_path,servlet);
168                 Map.Entry entry = _servletHandler.getHolderEntry(path);
169                
170                 if (entry!=null && !entry.equals(_invokerEntry))
171                 {
172                     // Use the holder
173                     holder=(ServletHolder)entry.getValue();
174                 }
175                 else
176                 {
177                     // Make a holder
178                     if (LOG.isDebugEnabled())
179                         LOG.debug("Making new servlet="+servlet+" with path="+path+"/*");
180                     holder=_servletHandler.addServletWithMapping(servlet, path+"/*");
181                     
182                     if (_parameters!=null)
183                         holder.setInitParameters(_parameters);
184                     
185                     try {holder.start();}
186                     catch (Exception e)
187                     {
188                         LOG.debug(e);
189                         throw new UnavailableException(e.toString());
190                     }
191                     
192                     // Check it is from an allowable classloader
193                     if (!_nonContextServlets)
194                     {
195                         Object s=holder.getServlet();
196                         
197                         if (_contextHandler.getClassLoader()!=
198                             s.getClass().getClassLoader())
199                         {
200                             try 
201                             {
202                                 holder.stop();
203                             } 
204                             catch (Exception e) 
205                             {
206                                 LOG.ignore(e);
207                             }
208                             
209                             LOG.warn("Dynamic servlet "+s+
210                                          " not loaded from context "+
211                                          request.getContextPath());
212                             throw new UnavailableException("Not in context");
213                         }
214                     }
215 
216                     if (_verbose && LOG.isDebugEnabled())
217                         LOG.debug("Dynamic load '"+servlet+"' at "+path);
218                 }
219             }
220         }
221         
222         if (holder!=null)
223         {
224             final Request baseRequest=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest();
225             holder.handle(baseRequest,
226                     new InvokedRequest(request,included,servlet,servlet_path,path_info),
227                           response);
228         }
229         else
230         {
231             LOG.info("Can't find holder for servlet: "+servlet);
232             response.sendError(404);
233         }
234             
235         
236     }
237 
238     /* ------------------------------------------------------------ */
239     class InvokedRequest extends HttpServletRequestWrapper
240     {
241         String _servletPath;
242         String _pathInfo;
243         boolean _included;
244         
245         /* ------------------------------------------------------------ */
246         InvokedRequest(HttpServletRequest request,
247                 boolean included,
248                 String name,
249                 String servletPath,
250                 String pathInfo)
251         {
252             super(request);
253             _included=included;
254             _servletPath=URIUtil.addPaths(servletPath,name);
255             _pathInfo=pathInfo.substring(name.length()+1);
256             if (_pathInfo.length()==0)
257                 _pathInfo=null;
258         }
259         
260         /* ------------------------------------------------------------ */
261         public String getServletPath()
262         {
263             if (_included)
264                 return super.getServletPath();
265             return _servletPath;
266         }
267         
268         /* ------------------------------------------------------------ */
269         public String getPathInfo()
270         {
271             if (_included)
272                 return super.getPathInfo();
273             return _pathInfo;
274         }
275         
276         /* ------------------------------------------------------------ */
277         public Object getAttribute(String name)
278         {
279             if (_included)
280             {
281                 if (name.equals(Dispatcher.INCLUDE_REQUEST_URI))
282                     return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
283                 if (name.equals(Dispatcher.INCLUDE_PATH_INFO))
284                     return _pathInfo;
285                 if (name.equals(Dispatcher.INCLUDE_SERVLET_PATH))
286                     return _servletPath;
287             }
288             return super.getAttribute(name);
289         }
290     }
291     
292     
293     private ServletHolder getHolder(ServletHolder[] holders, String servlet)
294     {
295         if (holders == null)
296             return null;
297        
298         ServletHolder holder = null;
299         for (int i=0; holder==null && i<holders.length; i++)
300         {
301             if (holders[i].getName().equals(servlet))
302             {
303                 holder = holders[i];
304             }
305         }
306         return holder;
307     }
308 }