Source code: org/mortbay/jetty/servlet/Invoker.java
1 // ===========================================================================
2 // Copyright (c) 1996-2002 Mort Bay Consulting Pty. Ltd. All rights reserved.
3 // $Id: Invoker.java,v 1.10 2003/09/18 13:29:24 gregwilkins Exp $
4 // ---------------------------------------------------------------------------
5
6 package org.mortbay.jetty.servlet;
7
8
9 import java.io.IOException;
10 import java.util.Enumeration;
11 import java.util.HashMap;
12 import java.util.Map;
13
14 import javax.servlet.ServletContext;
15 import javax.servlet.ServletException;
16 import javax.servlet.UnavailableException;
17 import javax.servlet.http.HttpServlet;
18 import javax.servlet.http.HttpServletRequest;
19 import javax.servlet.http.HttpServletRequestWrapper;
20 import javax.servlet.http.HttpServletResponse;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.mortbay.util.LogSupport;
25 import org.mortbay.util.URI;
26
27 /* ------------------------------------------------------------ */
28 /** Dynamic Servlet Invoker.
29 * This servlet invokes anonymous servlets that have not been defined
30 * in the web.xml or by other means. The first element of the pathInfo
31 * of a request passed to the envoker is treated as a servlet name for
32 * an existing servlet, or as a class name of a new servlet.
33 * This servlet is normally mapped to /servlet/*
34 * This servlet support the following initParams:
35 * <PRE>
36 * nonContextServlets If false, the invoker can only load
37 * servlets from the contexts classloader.
38 * This is false by default and setting this
39 * to true may have security implications.
40 *
41 * verbose If true, log dynamic loads
42 *
43 * * All other parameters are copied to the
44 * each dynamic servlet as init parameters
45 * </PRE>
46 * @version $Id: Invoker.java,v 1.10 2003/09/18 13:29:24 gregwilkins Exp $
47 * @author Greg Wilkins (gregw)
48 */
49 public class Invoker extends HttpServlet
50 {
51 private static Log log = LogFactory.getLog(Invoker.class);
52
53 private ServletHandler _servletHandler;
54 private Map.Entry _invokerEntry;
55 private Map _parameters;
56 private boolean _nonContextServlets;
57 private boolean _verbose;
58
59 /* ------------------------------------------------------------ */
60 public void init()
61 {
62 ServletContext config=getServletContext();
63 _servletHandler=((ServletHandler.Context)config).getServletHandler();
64
65 Enumeration e = getInitParameterNames();
66 while(e.hasMoreElements())
67 {
68 String param=(String)e.nextElement();
69 String value=getInitParameter(param);
70 String lvalue=value.toLowerCase();
71 if ("nonContextServlets".equals(param))
72 {
73 _nonContextServlets=value.length()>0 && lvalue.startsWith("t");
74 }
75 if ("verbose".equals(param))
76 {
77 _verbose=value.length()>0 && lvalue.startsWith("t");
78 }
79 else
80 {
81 if (_parameters==null)
82 _parameters=new HashMap();
83 _parameters.put(param,value);
84 }
85 }
86 }
87
88 /* ------------------------------------------------------------ */
89 protected void service(HttpServletRequest request, HttpServletResponse response)
90 throws ServletException, IOException
91 {
92 // Get the requested path and info
93 boolean included=false;
94 String servlet_path=(String)request.getAttribute(Dispatcher.__INCLUDE_SERVLET_PATH);
95 if (servlet_path==null)
96 servlet_path=request.getServletPath();
97 else
98 included=true;
99 String path_info = (String)request.getAttribute(Dispatcher.__INCLUDE_PATH_INFO);
100 if (path_info==null)
101 path_info=request.getPathInfo();
102
103 // Get the servlet class
104 String servlet = path_info;
105 if (servlet==null || servlet.length()<=1 )
106 {
107 response.sendError(404);
108 return;
109 }
110
111 int i0=servlet.charAt(0)=='/'?1:0;
112 int i1=servlet.indexOf('/',i0);
113 servlet=i1<0?servlet.substring(i0):servlet.substring(i0,i1);
114
115 // look for a named holder
116 ServletHolder holder=_servletHandler.getServletHolder(servlet);
117 if (holder!=null)
118 {
119 // Add named servlet mapping
120 _servletHandler.addServletHolder(URI.addPaths(servlet_path,servlet)+"/*",holder);
121 }
122 else
123 {
124 // look for a class mapping
125 if (servlet.endsWith(".class"))
126 servlet=servlet.substring(0,servlet.length()-6);
127 if (servlet==null || servlet.length()==0)
128 {
129 response.sendError(404);
130 return;
131 }
132
133 synchronized(_servletHandler)
134 {
135 // find the entry for the invoker
136 if (_invokerEntry==null)
137 _invokerEntry=_servletHandler.getHolderEntry(servlet_path);
138
139 // Check for existing mapping (avoid threaded race).
140 String path=URI.addPaths(servlet_path,servlet);
141 Map.Entry entry = _servletHandler.getHolderEntry(path);
142
143 if (entry!=null && entry!=_invokerEntry)
144 {
145 // Use the holder
146 holder=(ServletHolder)entry.getValue();
147 }
148 else
149 {
150 // Make a holder
151 holder=new ServletHolder(_servletHandler,servlet,servlet);
152
153 if (_parameters!=null)
154 holder.putAll(_parameters);
155
156 try {holder.start();}
157 catch (Exception e)
158 {
159 log.debug(LogSupport.EXCEPTION,e);
160 throw new UnavailableException(e.toString());
161 }
162
163 // Check it is from an allowable classloader
164 if (!_nonContextServlets)
165 {
166 Object s=holder.getServlet();
167
168 if (_servletHandler.getClassLoader()!=
169 s.getClass().getClassLoader())
170 {
171 holder.stop();
172 log.warn("Dynamic servlet "+s+
173 " not loaded from context "+
174 request.getContextPath());
175 throw new UnavailableException("Not in context");
176 }
177 }
178
179 // Add the holder for all the possible paths
180 if (_verbose)
181 log("Dynamic load '"+servlet+"' at "+path);
182 _servletHandler.addServletHolder(path+"/*",holder);
183 _servletHandler.addServletHolder(path+".class/*",holder);
184 }
185 }
186
187 }
188
189 if (holder!=null)
190 holder.handle(new Request(request,included,servlet,servlet_path,path_info),
191 response);
192 else
193 response.sendError(404);
194
195 }
196
197 /* ------------------------------------------------------------ */
198 class Request extends HttpServletRequestWrapper
199 {
200 String _servletPath;
201 String _pathInfo;
202 boolean _included;
203
204 /* ------------------------------------------------------------ */
205 Request(HttpServletRequest request,
206 boolean included,
207 String name,
208 String servletPath,
209 String pathInfo)
210 {
211 super(request);
212 _included=included;
213 _servletPath=URI.addPaths(servletPath,name);
214 _pathInfo=pathInfo.substring(name.length()+1);
215 if (_pathInfo.length()==0)
216 _pathInfo=null;
217 }
218
219 /* ------------------------------------------------------------ */
220 public String getServletPath()
221 {
222 if (_included)
223 return super.getServletPath();
224 return _servletPath;
225 }
226
227 /* ------------------------------------------------------------ */
228 public String getPathInfo()
229 {
230 if (_included)
231 return super.getPathInfo();
232 return _pathInfo;
233 }
234
235 /* ------------------------------------------------------------ */
236 public Object getAttribute(String name)
237 {
238 if (_included)
239 {
240 if (name.equals(Dispatcher.__INCLUDE_REQUEST_URI))
241 return URI.addPaths(URI.addPaths(getContextPath(),_servletPath),_pathInfo);
242 if (name.equals(Dispatcher.__INCLUDE_PATH_INFO))
243 return _pathInfo;
244 if (name.equals(Dispatcher.__INCLUDE_SERVLET_PATH))
245 return _servletPath;
246 }
247 return super.getAttribute(name);
248 }
249 }
250 }