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