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.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.servlet;
21 import java.io.IOException;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.Locale;
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;
35 import org.eclipse.jetty.server.Dispatcher;
36 import org.eclipse.jetty.server.Handler;
37 import org.eclipse.jetty.server.HttpChannel;
38 import org.eclipse.jetty.server.Request;
39 import org.eclipse.jetty.server.handler.ContextHandler;
40 import org.eclipse.jetty.server.handler.HandlerWrapper;
41 import org.eclipse.jetty.util.ArrayUtil;
42 import org.eclipse.jetty.util.URIUtil;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
46 /* ------------------------------------------------------------ */
47 /** Dynamic Servlet Invoker.
48 * This servlet invokes anonymous servlets that have not been defined
49 * in the web.xml or by other means. The first element of the pathInfo
50 * of a request passed to the envoker is treated as a servlet name for
51 * an existing servlet, or as a class name of a new servlet.
52 * This servlet is normally mapped to /servlet/*
53 * This servlet support the following initParams:
55 * nonContextServlets If false, the invoker can only load
56 * servlets from the contexts classloader.
57 * This is false by default and setting this
58 * to true may have security implications.
60 * verbose If true, log dynamic loads
62 * * All other parameters are copied to the
63 * each dynamic servlet as init parameters
65 * @version $Id: Invoker.java 4780 2009-03-17 15:36:08Z jesse $
68 public class Invoker extends HttpServlet
70 private static final Logger LOG = Log.getLogger(Invoker.class);
73 private ContextHandler _contextHandler;
74 private ServletHandler _servletHandler;
75 private Map.Entry<String, ServletHolder> _invokerEntry;
76 private Map<String, String> _parameters;
77 private boolean _nonContextServlets;
78 private boolean _verbose;
80 /* ------------------------------------------------------------ */
83 ServletContext config=getServletContext();
84 _contextHandler=((ContextHandler.Context)config).getContextHandler();
86 Handler handler=_contextHandler.getHandler();
87 while (handler!=null && !(handler instanceof ServletHandler) && (handler instanceof HandlerWrapper))
88 handler=((HandlerWrapper)handler).getHandler();
89 _servletHandler = (ServletHandler)handler;
90 Enumeration<String> e = getInitParameterNames();
91 while(e.hasMoreElements())
93 String param=e.nextElement();
94 String value=getInitParameter(param);
95 String lvalue=value.toLowerCase(Locale.ENGLISH);
96 if ("nonContextServlets".equals(param))
98 _nonContextServlets=value.length()>0 && lvalue.startsWith("t");
100 if ("verbose".equals(param))
102 _verbose=value.length()>0 && lvalue.startsWith("t");
106 if (_parameters==null)
107 _parameters=new HashMap<String, String>();
108 _parameters.put(param,value);
113 /* ------------------------------------------------------------ */
114 protected void service(HttpServletRequest request, HttpServletResponse response)
115 throws ServletException, IOException
117 // Get the requested path and info
118 boolean included=false;
119 String servlet_path=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
120 if (servlet_path==null)
121 servlet_path=request.getServletPath();
124 String path_info = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
126 path_info=request.getPathInfo();
128 // Get the servlet class
129 String servlet = path_info;
130 if (servlet==null || servlet.length()<=1 )
132 response.sendError(404);
137 int i0=servlet.charAt(0)=='/'?1:0;
138 int i1=servlet.indexOf('/',i0);
139 servlet=i1<0?servlet.substring(i0):servlet.substring(i0,i1);
141 // look for a named holder
142 ServletHolder[] holders = _servletHandler.getServlets();
143 ServletHolder holder = getHolder (holders, servlet);
147 // Found a named servlet (from a user's web.xml file) so
148 // now we add a mapping for it
149 if (LOG.isDebugEnabled())
150 LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*");
151 ServletMapping mapping = new ServletMapping();
152 mapping.setServletName(servlet);
153 mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*");
154 _servletHandler.setServletMappings(ArrayUtil.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
158 // look for a class mapping
159 if (servlet.endsWith(".class"))
160 servlet=servlet.substring(0,servlet.length()-6);
161 if (servlet==null || servlet.length()==0)
163 response.sendError(404);
167 synchronized(_servletHandler)
169 // find the entry for the invoker (me)
170 _invokerEntry=_servletHandler.getHolderEntry(servlet_path);
172 // Check for existing mapping (avoid threaded race).
173 String path=URIUtil.addPaths(servlet_path,servlet);
174 Map.Entry<String, ServletHolder> entry = _servletHandler.getHolderEntry(path);
176 if (entry!=null && !entry.equals(_invokerEntry))
179 holder=(ServletHolder)entry.getValue();
184 if (LOG.isDebugEnabled())
185 LOG.debug("Making new servlet="+servlet+" with path="+path+"/*");
186 holder=_servletHandler.addServletWithMapping(servlet, path+"/*");
188 if (_parameters!=null)
189 holder.setInitParameters(_parameters);
191 try {holder.start();}
195 throw new UnavailableException(e.toString());
198 // Check it is from an allowable classloader
199 if (!_nonContextServlets)
201 Object s=holder.getServlet();
203 if (_contextHandler.getClassLoader()!=
204 s.getClass().getClassLoader())
215 LOG.warn("Dynamic servlet "+s+
216 " not loaded from context "+
217 request.getContextPath());
218 throw new UnavailableException("Not in context");
222 if (_verbose && LOG.isDebugEnabled())
223 LOG.debug("Dynamic load '"+servlet+"' at "+path);
230 final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
231 holder.handle(baseRequest,
232 new InvokedRequest(request,included,servlet,servlet_path,path_info),
237 LOG.info("Can't find holder for servlet: "+servlet);
238 response.sendError(404);
244 /* ------------------------------------------------------------ */
245 class InvokedRequest extends HttpServletRequestWrapper
251 /* ------------------------------------------------------------ */
252 InvokedRequest(HttpServletRequest request,
260 _servletPath=URIUtil.addPaths(servletPath,name);
261 _pathInfo=pathInfo.substring(name.length()+1);
262 if (_pathInfo.length()==0)
266 /* ------------------------------------------------------------ */
267 public String getServletPath()
270 return super.getServletPath();
274 /* ------------------------------------------------------------ */
275 public String getPathInfo()
278 return super.getPathInfo();
282 /* ------------------------------------------------------------ */
283 public Object getAttribute(String name)
287 if (name.equals(Dispatcher.INCLUDE_REQUEST_URI))
288 return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
289 if (name.equals(Dispatcher.INCLUDE_PATH_INFO))
291 if (name.equals(Dispatcher.INCLUDE_SERVLET_PATH))
294 return super.getAttribute(name);
299 private ServletHolder getHolder(ServletHolder[] holders, String servlet)
304 ServletHolder holder = null;
305 for (int i=0; holder==null && i<holders.length; i++)
307 if (holders[i].getName().equals(servlet))