View Javadoc

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