1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19 package org.apache.catalina.servlets;
20
21
22 import java.io.IOException;
23
24 import javax.servlet.Servlet;
25 import javax.servlet.ServletException;
26 import javax.servlet.UnavailableException;
27 import javax.servlet.http.HttpServlet;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.apache.catalina.ContainerServlet;
32 import org.apache.catalina.Context;
33 import org.apache.catalina.Globals;
34 import org.apache.catalina.Wrapper;
35 import org.apache.catalina.util.StringManager;
36
37
38 /**
39 * The default servlet-invoking servlet for most web applications,
40 * used to serve requests to servlets that have not been registered
41 * in the web application deployment descriptor.
42 *
43 * @author Craig R. McClanahan
44 * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
45 */
46
47 public final class InvokerServlet
48 extends HttpServlet implements ContainerServlet {
49
50
51 // ----------------------------------------------------- Instance Variables
52
53
54 /**
55 * The Context container associated with our web application.
56 */
57 private Context context = null;
58
59
60 /**
61 * The debugging detail level for this servlet.
62 */
63 private int debug = 0;
64
65
66 /**
67 * The string manager for this package.
68 */
69 private static StringManager sm =
70 StringManager.getManager(Constants.Package);
71
72
73 /**
74 * The Wrapper container associated with this servlet.
75 */
76 private Wrapper wrapper = null;
77
78
79 // ----------------------------------------------- ContainerServlet Methods
80
81
82 /**
83 * Return the Wrapper with which we are associated.
84 */
85 public Wrapper getWrapper() {
86
87 return (this.wrapper);
88
89 }
90
91
92 /**
93 * Set the Wrapper with which we are associated.
94 *
95 * @param wrapper The new wrapper
96 */
97 public void setWrapper(Wrapper wrapper) {
98
99 this.wrapper = wrapper;
100 if (wrapper == null)
101 context = null;
102 else
103 context = (Context) wrapper.getParent();
104
105 }
106
107
108 // --------------------------------------------------------- Public Methods
109
110
111 /**
112 * Finalize this servlet.
113 */
114 public void destroy() {
115
116 ; // No actions necessary
117
118 }
119
120
121 /**
122 * Process a GET request for the specified resource.
123 *
124 * @param request The servlet request we are processing
125 * @param response The servlet response we are creating
126 *
127 * @exception IOException if an input/output error occurs
128 * @exception ServletException if a servlet-specified error occurs
129 */
130 public void doGet(HttpServletRequest request,
131 HttpServletResponse response)
132 throws IOException, ServletException {
133
134 serveRequest(request, response);
135
136 }
137
138
139 /**
140 * Process a HEAD request for the specified resource.
141 *
142 * @param request The servlet request we are processing
143 * @param response The servlet response we are creating
144 *
145 * @exception IOException if an input/output error occurs
146 * @exception ServletException if a servlet-specified error occurs
147 */
148 public void doHead(HttpServletRequest request,
149 HttpServletResponse response)
150 throws IOException, ServletException {
151
152 serveRequest(request, response);
153
154 }
155
156
157 /**
158 * Process a POST request for the specified resource.
159 *
160 * @param request The servlet request we are processing
161 * @param response The servlet response we are creating
162 *
163 * @exception IOException if an input/output error occurs
164 * @exception ServletException if a servlet-specified error occurs
165 */
166 public void doPost(HttpServletRequest request,
167 HttpServletResponse response)
168 throws IOException, ServletException {
169
170 serveRequest(request, response);
171
172 }
173
174
175 /**
176 * Initialize this servlet.
177 */
178 public void init() throws ServletException {
179
180 // Ensure that our ContainerServlet properties have been set
181 if ((wrapper == null) || (context == null))
182 throw new UnavailableException
183 (sm.getString("invokerServlet.noWrapper"));
184
185 // Set our properties from the initialization parameters
186 if (getServletConfig().getInitParameter("debug") != null)
187 debug = Integer.parseInt(getServletConfig().getInitParameter("debug"));
188
189 if (debug >= 1)
190 log("init: Associated with Context '" + context.getPath() + "'");
191
192 }
193
194
195
196 // -------------------------------------------------------- Private Methods
197
198
199 /**
200 * Serve the specified request, creating the corresponding response.
201 * After the first time a particular servlet class is requested, it will
202 * be served directly (like any registered servlet) because it will have
203 * been registered and mapped in our associated Context.
204 *
205 * @param request The servlet request we are processing
206 * @param response The servlet response we are creating
207 *
208 * @exception IOException if an input/output error occurs
209 * @exception ServletException if a servlet-specified error occurs
210 */
211 public void serveRequest(HttpServletRequest request,
212 HttpServletResponse response)
213 throws IOException, ServletException {
214
215 // Disallow calling this servlet via a named dispatcher
216 if (request.getAttribute(Globals.NAMED_DISPATCHER_ATTR) != null)
217 throw new ServletException
218 (sm.getString("invokerServlet.notNamed"));
219
220 // Identify the input parameters and our "included" state
221 String inRequestURI = null;
222 String inServletPath = null;
223 String inPathInfo = null;
224 boolean included =
225 (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null);
226
227 if (included) {
228 inRequestURI =
229 (String) request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR);
230 inServletPath =
231 (String) request.getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
232 inPathInfo =
233 (String) request.getAttribute(Globals.INCLUDE_PATH_INFO_ATTR);
234 } else {
235 inRequestURI = request.getRequestURI();
236 inServletPath = request.getServletPath();
237 inPathInfo = request.getPathInfo();
238 }
239 if (debug >= 1) {
240 log("included='" + included + "', requestURI='" +
241 inRequestURI + "'");
242 log(" servletPath='" + inServletPath + "', pathInfo='" +
243 inPathInfo + "'");
244 }
245
246 // Make sure a servlet name or class name was specified
247 if (inPathInfo == null) {
248 if (debug >= 1)
249 log("Invalid pathInfo '" + inPathInfo + "'");
250 if (included)
251 throw new ServletException
252 (sm.getString("invokerServlet.invalidPath", inRequestURI));
253 else {
254 response.sendError(HttpServletResponse.SC_NOT_FOUND,
255 inRequestURI);
256 return;
257 }
258 }
259
260 // Identify the outgoing servlet name or class, and outgoing path info
261 String pathInfo = inPathInfo;
262 String servletClass = pathInfo.substring(1);
263 int slash = servletClass.indexOf('/');
264 if (slash >= 0) {
265 pathInfo = servletClass.substring(slash);
266 servletClass = servletClass.substring(0, slash);
267 } else {
268 pathInfo = "";
269 }
270
271 if (servletClass.startsWith("org.apache.catalina")) {
272 response.sendError(HttpServletResponse.SC_NOT_FOUND,
273 inRequestURI);
274 return;
275 }
276
277 if (debug >= 1)
278 log("Processing servlet '" + servletClass +
279 "' with path info '" + pathInfo + "'");
280 String name = "org.apache.catalina.INVOKER." + servletClass;
281 String pattern = inServletPath + "/" + servletClass + "/*";
282 Wrapper wrapper = null;
283
284 // Synchronize to avoid race conditions when multiple requests
285 // try to initialize the same servlet at the same time
286 synchronized (this) {
287
288 // Are we referencing an existing servlet class or name?
289 wrapper = (Wrapper) context.findChild(servletClass);
290 if (wrapper == null)
291 wrapper = (Wrapper) context.findChild(name);
292 if (wrapper != null) {
293 String actualServletClass = wrapper.getServletClass();
294 if ((actualServletClass != null)
295 && (actualServletClass.startsWith
296 ("org.apache.catalina"))) {
297 response.sendError(HttpServletResponse.SC_NOT_FOUND,
298 inRequestURI);
299 return;
300 }
301 if (debug >= 1)
302 log("Using wrapper for servlet '" +
303 wrapper.getName() + "' with mapping '" +
304 pattern + "'");
305 context.addServletMapping(pattern, wrapper.getName());
306 }
307
308 // No, create a new wrapper for the specified servlet class
309 else {
310
311 if (debug >= 1)
312 log("Creating wrapper for '" + servletClass +
313 "' with mapping '" + pattern + "'");
314
315 try {
316 wrapper = context.createWrapper();
317 wrapper.setName(name);
318 wrapper.setLoadOnStartup(1);
319 wrapper.setServletClass(servletClass);
320 context.addChild(wrapper);
321 context.addServletMapping(pattern, name);
322 } catch (Exception e) {
323 log(sm.getString("invokerServlet.cannotCreate",
324 inRequestURI), e);
325 context.removeServletMapping(pattern);
326 context.removeChild(wrapper);
327 if (included)
328 throw new ServletException
329 (sm.getString("invokerServlet.cannotCreate",
330 inRequestURI), e);
331 else {
332 response.sendError(HttpServletResponse.SC_NOT_FOUND,
333 inRequestURI);
334 return;
335 }
336 }
337 }
338
339 }
340
341 // Create a request wrapper to pass on to the invoked servlet
342 InvokerHttpRequest wrequest =
343 new InvokerHttpRequest(request);
344 wrequest.setRequestURI(inRequestURI);
345 StringBuffer sb = new StringBuffer(inServletPath);
346 sb.append("/");
347 sb.append(servletClass);
348 wrequest.setServletPath(sb.toString());
349 if ((pathInfo == null) || (pathInfo.length() < 1)) {
350 wrequest.setPathInfo(null);
351 wrequest.setPathTranslated(null);
352 } else {
353 wrequest.setPathInfo(pathInfo);
354 wrequest.setPathTranslated
355 (getServletContext().getRealPath(pathInfo));
356 }
357
358 // Allocate a servlet instance to perform this request
359 Servlet instance = null;
360 try {
361 instance = wrapper.allocate();
362 } catch (ServletException e) {
363 log(sm.getString("invokerServlet.allocate", inRequestURI), e);
364 context.removeServletMapping(pattern);
365 context.removeChild(wrapper);
366 Throwable rootCause = e.getRootCause();
367 if (rootCause == null)
368 rootCause = e;
369 if (rootCause instanceof ClassNotFoundException) {
370 response.sendError(HttpServletResponse.SC_NOT_FOUND,
371 inRequestURI);
372 return;
373 } else if (rootCause instanceof IOException) {
374 throw (IOException) rootCause;
375 } else if (rootCause instanceof RuntimeException) {
376 throw (RuntimeException) rootCause;
377 } else if (rootCause instanceof ServletException) {
378 throw (ServletException) rootCause;
379 } else {
380 throw new ServletException
381 (sm.getString("invokerServlet.allocate", inRequestURI),
382 rootCause);
383 }
384 }
385
386 // After loading the wrapper, restore some of the fields when including
387 if (included) {
388 wrequest.setRequestURI(request.getRequestURI());
389 wrequest.setPathInfo(request.getPathInfo());
390 wrequest.setServletPath(request.getServletPath());
391 }
392
393 // Invoke the service() method of the allocated servlet
394 try {
395 String jspFile = wrapper.getJspFile();
396 if (jspFile != null)
397 request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
398 else
399 request.removeAttribute(Globals.JSP_FILE_ATTR);
400 request.setAttribute(Globals.INVOKED_ATTR,
401 request.getServletPath());
402 instance.service(wrequest, response);
403 } catch (UnavailableException e) {
404 context.removeServletMapping(pattern);
405 throw e;
406 } finally {
407 request.removeAttribute(Globals.INVOKED_ATTR);
408 request.removeAttribute(Globals.JSP_FILE_ATTR);
409 // Deallocate the allocated servlet instance
410 try {
411 wrapper.deallocate(instance);
412 } catch (ServletException e) {
413 log(sm.getString("invokerServlet.deallocate", inRequestURI), e);
414 throw e;
415 }
416 }
417
418 }
419
420
421 }