1   // ========================================================================
2   // $Id: Invoker.java 1344 2006-12-01 22:31:56Z gregw $
3   // Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
4   // ------------------------------------------------------------------------
5   // Licensed under the Apache License, Version 2.0 (the "License");
6   // you may not use this file except in compliance with the License.
7   // You may obtain a copy of the License at 
8   // http://www.apache.org/licenses/LICENSE-2.0
9   // Unless required by applicable law or agreed to in writing, software
10  // distributed under the License is distributed on an "AS IS" BASIS,
11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  // See the License for the specific language governing permissions and
13  // limitations under the License.
14  // ========================================================================
15  
16  package org.mortbay.jetty.servlet;
17  
18  import java.io.IOException;
19  import java.util.Enumeration;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import javax.servlet.ServletContext;
24  import javax.servlet.ServletException;
25  import javax.servlet.UnavailableException;
26  import javax.servlet.http.HttpServlet;
27  import javax.servlet.http.HttpServletRequest;
28  import javax.servlet.http.HttpServletRequestWrapper;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import org.mortbay.jetty.Handler;
32  import org.mortbay.jetty.handler.ContextHandler;
33  import org.mortbay.jetty.handler.HandlerWrapper;
34  import org.mortbay.log.Log;
35  import org.mortbay.util.LazyList;
36  import org.mortbay.util.URIUtil;
37  ;
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 1344 2006-12-01 22:31:56Z gregw $
59   * @author Greg Wilkins (gregw)
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.SContext)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.addServlet(servlet, path+"/*");
176                     Map.Entry eee = _servletHandler.getHolderEntry(path);
177                     ServletMapping mapping = new ServletMapping();
178                     mapping.setServletName(servlet);
179                     mapping.setPathSpec(path+".class/*");
180                     _servletHandler.setServletMappings((ServletMapping[])LazyList.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
181                     
182                     
183                     
184                     
185                     if (_parameters!=null)
186                         holder.setInitParameters(_parameters);
187                     
188                     try {holder.start();}
189                     catch (Exception e)
190                     {
191                         Log.debug(e);
192                         throw new UnavailableException(e.toString());
193                     }
194                     
195                     // Check it is from an allowable classloader
196                     if (!_nonContextServlets)
197                     {
198                         Object s=holder.getServlet();
199                         
200                         if (_contextHandler.getClassLoader()!=
201                             s.getClass().getClassLoader())
202                         {
203                             try 
204                             {
205                                 holder.stop();
206                             } 
207                             catch (Exception e) 
208                             {
209                                 Log.ignore(e);
210                             }
211                             
212                             Log.warn("Dynamic servlet "+s+
213                                          " not loaded from context "+
214                                          request.getContextPath());
215                             throw new UnavailableException("Not in context");
216                         }
217                     }
218 
219                     if (_verbose)
220                         Log.debug("Dynamic load '"+servlet+"' at "+path);
221                 }
222             }
223         }
224         
225         if (holder!=null)
226             holder.handle(new Request(request,included,servlet,servlet_path,path_info),
227                           response);
228         else
229         {
230             Log.info("Can't find holder for servlet: "+servlet);
231             response.sendError(404);
232         }
233             
234         
235     }
236 
237     /* ------------------------------------------------------------ */
238     class Request extends HttpServletRequestWrapper
239     {
240         String _servletPath;
241         String _pathInfo;
242         boolean _included;
243         
244         /* ------------------------------------------------------------ */
245         Request(HttpServletRequest request,
246                 boolean included,
247                 String name,
248                 String servletPath,
249                 String pathInfo)
250         {
251             super(request);
252             _included=included;
253             _servletPath=URIUtil.addPaths(servletPath,name);
254             _pathInfo=pathInfo.substring(name.length()+1);
255             if (_pathInfo.length()==0)
256                 _pathInfo=null;
257         }
258         
259         /* ------------------------------------------------------------ */
260         public String getServletPath()
261         {
262             if (_included)
263                 return super.getServletPath();
264             return _servletPath;
265         }
266         
267         /* ------------------------------------------------------------ */
268         public String getPathInfo()
269         {
270             if (_included)
271                 return super.getPathInfo();
272             return _pathInfo;
273         }
274         
275         /* ------------------------------------------------------------ */
276         public Object getAttribute(String name)
277         {
278             if (_included)
279             {
280                 if (name.equals(Dispatcher.__INCLUDE_REQUEST_URI))
281                     return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
282                 if (name.equals(Dispatcher.__INCLUDE_PATH_INFO))
283                     return _pathInfo;
284                 if (name.equals(Dispatcher.__INCLUDE_SERVLET_PATH))
285                     return _servletPath;
286             }
287             return super.getAttribute(name);
288         }
289     }
290     
291     
292     private ServletHolder getHolder(ServletHolder[] holders, String servlet)
293     {
294         if (holders == null)
295             return null;
296        
297         ServletHolder holder = null;
298         for (int i=0; holder==null && i<holders.length; i++)
299         {
300             if (holders[i].getName().equals(servlet))
301             {
302                 holder = holders[i];
303             }
304         }
305         return holder;
306     }
307 }