View Javadoc

1   // ========================================================================
2   // $Id: Invoker.java 3539 2008-08-21 04:46:59Z dyu $
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 3539 2008-08-21 04:46:59Z dyu $
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.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             holder.handle(new Request(request,included,servlet,servlet_path,path_info),
219                           response);
220         else
221         {
222             Log.info("Can't find holder for servlet: "+servlet);
223             response.sendError(404);
224         }
225             
226         
227     }
228 
229     /* ------------------------------------------------------------ */
230     class Request extends HttpServletRequestWrapper
231     {
232         String _servletPath;
233         String _pathInfo;
234         boolean _included;
235         
236         /* ------------------------------------------------------------ */
237         Request(HttpServletRequest request,
238                 boolean included,
239                 String name,
240                 String servletPath,
241                 String pathInfo)
242         {
243             super(request);
244             _included=included;
245             _servletPath=URIUtil.addPaths(servletPath,name);
246             _pathInfo=pathInfo.substring(name.length()+1);
247             if (_pathInfo.length()==0)
248                 _pathInfo=null;
249         }
250         
251         /* ------------------------------------------------------------ */
252         public String getServletPath()
253         {
254             if (_included)
255                 return super.getServletPath();
256             return _servletPath;
257         }
258         
259         /* ------------------------------------------------------------ */
260         public String getPathInfo()
261         {
262             if (_included)
263                 return super.getPathInfo();
264             return _pathInfo;
265         }
266         
267         /* ------------------------------------------------------------ */
268         public Object getAttribute(String name)
269         {
270             if (_included)
271             {
272                 if (name.equals(Dispatcher.__INCLUDE_REQUEST_URI))
273                     return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
274                 if (name.equals(Dispatcher.__INCLUDE_PATH_INFO))
275                     return _pathInfo;
276                 if (name.equals(Dispatcher.__INCLUDE_SERVLET_PATH))
277                     return _servletPath;
278             }
279             return super.getAttribute(name);
280         }
281     }
282     
283     
284     private ServletHolder getHolder(ServletHolder[] holders, String servlet)
285     {
286         if (holders == null)
287             return null;
288        
289         ServletHolder holder = null;
290         for (int i=0; holder==null && i<holders.length; i++)
291         {
292             if (holders[i].getName().equals(servlet))
293             {
294                 holder = holders[i];
295             }
296         }
297         return holder;
298     }
299 }