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