--- /dev/null
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlet;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Dispatcher;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.HandlerWrapper;
+import org.eclipse.jetty.util.ArrayUtil;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Dynamic Servlet Invoker.
+ * This servlet invokes anonymous servlets that have not been defined
+ * in the web.xml or by other means. The first element of the pathInfo
+ * of a request passed to the envoker is treated as a servlet name for
+ * an existing servlet, or as a class name of a new servlet.
+ * This servlet is normally mapped to /servlet/*
+ * This servlet support the following initParams:
+ * <PRE>
+ * nonContextServlets If false, the invoker can only load
+ * servlets from the contexts classloader.
+ * This is false by default and setting this
+ * to true may have security implications.
+ *
+ * verbose If true, log dynamic loads
+ *
+ * * All other parameters are copied to the
+ * each dynamic servlet as init parameters
+ * </PRE>
+ * @version $Id: Invoker.java 4780 2009-03-17 15:36:08Z jesse $
+ *
+ */
+public class Invoker extends HttpServlet
+{
+ private static final Logger LOG = Log.getLogger(Invoker.class);
+
+
+ private ContextHandler _contextHandler;
+ private ServletHandler _servletHandler;
+ private Map.Entry _invokerEntry;
+ private Map _parameters;
+ private boolean _nonContextServlets;
+ private boolean _verbose;
+
+ /* ------------------------------------------------------------ */
+ public void init()
+ {
+ ServletContext config=getServletContext();
+ _contextHandler=((ContextHandler.Context)config).getContextHandler();
+
+ Handler handler=_contextHandler.getHandler();
+ while (handler!=null && !(handler instanceof ServletHandler) && (handler instanceof HandlerWrapper))
+ handler=((HandlerWrapper)handler).getHandler();
+ _servletHandler = (ServletHandler)handler;
+ Enumeration e = getInitParameterNames();
+ while(e.hasMoreElements())
+ {
+ String param=(String)e.nextElement();
+ String value=getInitParameter(param);
+ String lvalue=value.toLowerCase(Locale.ENGLISH);
+ if ("nonContextServlets".equals(param))
+ {
+ _nonContextServlets=value.length()>0 && lvalue.startsWith("t");
+ }
+ if ("verbose".equals(param))
+ {
+ _verbose=value.length()>0 && lvalue.startsWith("t");
+ }
+ else
+ {
+ if (_parameters==null)
+ _parameters=new HashMap();
+ _parameters.put(param,value);
+ }
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ protected void service(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException
+ {
+ // Get the requested path and info
+ boolean included=false;
+ String servlet_path=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
+ if (servlet_path==null)
+ servlet_path=request.getServletPath();
+ else
+ included=true;
+ String path_info = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
+ if (path_info==null)
+ path_info=request.getPathInfo();
+
+ // Get the servlet class
+ String servlet = path_info;
+ if (servlet==null || servlet.length()<=1 )
+ {
+ response.sendError(404);
+ return;
+ }
+
+
+ int i0=servlet.charAt(0)=='/'?1:0;
+ int i1=servlet.indexOf('/',i0);
+ servlet=i1<0?servlet.substring(i0):servlet.substring(i0,i1);
+
+ // look for a named holder
+ ServletHolder[] holders = _servletHandler.getServlets();
+ ServletHolder holder = getHolder (holders, servlet);
+
+ if (holder!=null)
+ {
+ // Found a named servlet (from a user's web.xml file) so
+ // now we add a mapping for it
+ if (LOG.isDebugEnabled())
+ LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*");
+ ServletMapping mapping = new ServletMapping();
+ mapping.setServletName(servlet);
+ mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*");
+ _servletHandler.setServletMappings((ServletMapping[])ArrayUtil.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
+ }
+ else
+ {
+ // look for a class mapping
+ if (servlet.endsWith(".class"))
+ servlet=servlet.substring(0,servlet.length()-6);
+ if (servlet==null || servlet.length()==0)
+ {
+ response.sendError(404);
+ return;
+ }
+
+ synchronized(_servletHandler)
+ {
+ // find the entry for the invoker (me)
+ _invokerEntry=_servletHandler.getHolderEntry(servlet_path);
+
+ // Check for existing mapping (avoid threaded race).
+ String path=URIUtil.addPaths(servlet_path,servlet);
+ Map.Entry entry = _servletHandler.getHolderEntry(path);
+
+ if (entry!=null && !entry.equals(_invokerEntry))
+ {
+ // Use the holder
+ holder=(ServletHolder)entry.getValue();
+ }
+ else
+ {
+ // Make a holder
+ if (LOG.isDebugEnabled())
+ LOG.debug("Making new servlet="+servlet+" with path="+path+"/*");
+ holder=_servletHandler.addServletWithMapping(servlet, path+"/*");
+
+ if (_parameters!=null)
+ holder.setInitParameters(_parameters);
+
+ try {holder.start();}
+ catch (Exception e)
+ {
+ LOG.debug(e);
+ throw new UnavailableException(e.toString());
+ }
+
+ // Check it is from an allowable classloader
+ if (!_nonContextServlets)
+ {
+ Object s=holder.getServlet();
+
+ if (_contextHandler.getClassLoader()!=
+ s.getClass().getClassLoader())
+ {
+ try
+ {
+ holder.stop();
+ }
+ catch (Exception e)
+ {
+ LOG.ignore(e);
+ }
+
+ LOG.warn("Dynamic servlet "+s+
+ " not loaded from context "+
+ request.getContextPath());
+ throw new UnavailableException("Not in context");
+ }
+ }
+
+ if (_verbose && LOG.isDebugEnabled())
+ LOG.debug("Dynamic load '"+servlet+"' at "+path);
+ }
+ }
+ }
+
+ if (holder!=null)
+ {
+ final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
+ holder.handle(baseRequest,
+ new InvokedRequest(request,included,servlet,servlet_path,path_info),
+ response);
+ }
+ else
+ {
+ LOG.info("Can't find holder for servlet: "+servlet);
+ response.sendError(404);
+ }
+
+
+ }
+
+ /* ------------------------------------------------------------ */
+ class InvokedRequest extends HttpServletRequestWrapper
+ {
+ String _servletPath;
+ String _pathInfo;
+ boolean _included;
+
+ /* ------------------------------------------------------------ */
+ InvokedRequest(HttpServletRequest request,
+ boolean included,
+ String name,
+ String servletPath,
+ String pathInfo)
+ {
+ super(request);
+ _included=included;
+ _servletPath=URIUtil.addPaths(servletPath,name);
+ _pathInfo=pathInfo.substring(name.length()+1);
+ if (_pathInfo.length()==0)
+ _pathInfo=null;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String getServletPath()
+ {
+ if (_included)
+ return super.getServletPath();
+ return _servletPath;
+ }
+
+ /* ------------------------------------------------------------ */
+ public String getPathInfo()
+ {
+ if (_included)
+ return super.getPathInfo();
+ return _pathInfo;
+ }
+
+ /* ------------------------------------------------------------ */
+ public Object getAttribute(String name)
+ {
+ if (_included)
+ {
+ if (name.equals(Dispatcher.INCLUDE_REQUEST_URI))
+ return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
+ if (name.equals(Dispatcher.INCLUDE_PATH_INFO))
+ return _pathInfo;
+ if (name.equals(Dispatcher.INCLUDE_SERVLET_PATH))
+ return _servletPath;
+ }
+ return super.getAttribute(name);
+ }
+ }
+
+
+ private ServletHolder getHolder(ServletHolder[] holders, String servlet)
+ {
+ if (holders == null)
+ return null;
+
+ ServletHolder holder = null;
+ for (int i=0; holder==null && i<holders.length; i++)
+ {
+ if (holders[i].getName().equals(servlet))
+ {
+ holder = holders[i];
+ }
+ }
+ return holder;
+ }
+}