001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.servlets;
019:
020: import java.io.IOException;
021:
022: import javax.servlet.Servlet;
023: import javax.servlet.ServletException;
024: import javax.servlet.UnavailableException;
025: import javax.servlet.http.HttpServlet;
026: import javax.servlet.http.HttpServletRequest;
027: import javax.servlet.http.HttpServletResponse;
028:
029: import org.apache.catalina.ContainerServlet;
030: import org.apache.catalina.Context;
031: import org.apache.catalina.Globals;
032: import org.apache.catalina.Wrapper;
033: import org.apache.catalina.util.StringManager;
034:
035: /**
036: * The default servlet-invoking servlet for most web applications,
037: * used to serve requests to servlets that have not been registered
038: * in the web application deployment descriptor.
039: *
040: * @author Craig R. McClanahan
041: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
042: */
043:
044: public final class InvokerServlet extends HttpServlet implements
045: ContainerServlet {
046:
047: // ----------------------------------------------------- Instance Variables
048:
049: /**
050: * The Context container associated with our web application.
051: */
052: private Context context = null;
053:
054: /**
055: * The debugging detail level for this servlet.
056: */
057: private int debug = 0;
058:
059: /**
060: * The string manager for this package.
061: */
062: private static StringManager sm = StringManager
063: .getManager(Constants.Package);
064:
065: /**
066: * The Wrapper container associated with this servlet.
067: */
068: private Wrapper wrapper = null;
069:
070: // ----------------------------------------------- ContainerServlet Methods
071:
072: /**
073: * Return the Wrapper with which we are associated.
074: */
075: public Wrapper getWrapper() {
076:
077: return (this .wrapper);
078:
079: }
080:
081: /**
082: * Set the Wrapper with which we are associated.
083: *
084: * @param wrapper The new wrapper
085: */
086: public void setWrapper(Wrapper wrapper) {
087:
088: this .wrapper = wrapper;
089: if (wrapper == null)
090: context = null;
091: else
092: context = (Context) wrapper.getParent();
093:
094: }
095:
096: // --------------------------------------------------------- Public Methods
097:
098: /**
099: * Finalize this servlet.
100: */
101: public void destroy() {
102:
103: ; // No actions necessary
104:
105: }
106:
107: /**
108: * Process a GET request for the specified resource.
109: *
110: * @param request The servlet request we are processing
111: * @param response The servlet response we are creating
112: *
113: * @exception IOException if an input/output error occurs
114: * @exception ServletException if a servlet-specified error occurs
115: */
116: public void doGet(HttpServletRequest request,
117: HttpServletResponse response) throws IOException,
118: ServletException {
119:
120: serveRequest(request, response);
121:
122: }
123:
124: /**
125: * Process a HEAD request for the specified resource.
126: *
127: * @param request The servlet request we are processing
128: * @param response The servlet response we are creating
129: *
130: * @exception IOException if an input/output error occurs
131: * @exception ServletException if a servlet-specified error occurs
132: */
133: public void doHead(HttpServletRequest request,
134: HttpServletResponse response) throws IOException,
135: ServletException {
136:
137: serveRequest(request, response);
138:
139: }
140:
141: /**
142: * Process a POST request for the specified resource.
143: *
144: * @param request The servlet request we are processing
145: * @param response The servlet response we are creating
146: *
147: * @exception IOException if an input/output error occurs
148: * @exception ServletException if a servlet-specified error occurs
149: */
150: public void doPost(HttpServletRequest request,
151: HttpServletResponse response) throws IOException,
152: ServletException {
153:
154: serveRequest(request, response);
155:
156: }
157:
158: /**
159: * Initialize this servlet.
160: */
161: public void init() throws ServletException {
162:
163: // Ensure that our ContainerServlet properties have been set
164: if ((wrapper == null) || (context == null))
165: throw new UnavailableException(sm
166: .getString("invokerServlet.noWrapper"));
167:
168: // Set our properties from the initialization parameters
169: if (getServletConfig().getInitParameter("debug") != null)
170: debug = Integer.parseInt(getServletConfig()
171: .getInitParameter("debug"));
172:
173: if (debug >= 1)
174: log("init: Associated with Context '" + context.getPath()
175: + "'");
176:
177: }
178:
179: // -------------------------------------------------------- Private Methods
180:
181: /**
182: * Serve the specified request, creating the corresponding response.
183: * After the first time a particular servlet class is requested, it will
184: * be served directly (like any registered servlet) because it will have
185: * been registered and mapped in our associated Context.
186: *
187: * @param request The servlet request we are processing
188: * @param response The servlet response we are creating
189: *
190: * @exception IOException if an input/output error occurs
191: * @exception ServletException if a servlet-specified error occurs
192: */
193: public void serveRequest(HttpServletRequest request,
194: HttpServletResponse response) throws IOException,
195: ServletException {
196:
197: // Disallow calling this servlet via a named dispatcher
198: if (request.getAttribute(Globals.NAMED_DISPATCHER_ATTR) != null)
199: throw new ServletException(sm
200: .getString("invokerServlet.notNamed"));
201:
202: // Identify the input parameters and our "included" state
203: String inRequestURI = null;
204: String inServletPath = null;
205: String inPathInfo = null;
206: boolean included = (request
207: .getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null);
208:
209: if (included) {
210: inRequestURI = (String) request
211: .getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR);
212: inServletPath = (String) request
213: .getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR);
214: inPathInfo = (String) request
215: .getAttribute(Globals.INCLUDE_PATH_INFO_ATTR);
216: } else {
217: inRequestURI = request.getRequestURI();
218: inServletPath = request.getServletPath();
219: inPathInfo = request.getPathInfo();
220: }
221: if (debug >= 1) {
222: log("included='" + included + "', requestURI='"
223: + inRequestURI + "'");
224: log(" servletPath='" + inServletPath + "', pathInfo='"
225: + inPathInfo + "'");
226: }
227:
228: // Make sure a servlet name or class name was specified
229: if (inPathInfo == null) {
230: if (debug >= 1)
231: log("Invalid pathInfo '" + inPathInfo + "'");
232: if (included)
233: throw new ServletException(sm.getString(
234: "invokerServlet.invalidPath", inRequestURI));
235: else {
236: response.sendError(HttpServletResponse.SC_NOT_FOUND,
237: inRequestURI);
238: return;
239: }
240: }
241:
242: // Identify the outgoing servlet name or class, and outgoing path info
243: String pathInfo = inPathInfo;
244: String servletClass = pathInfo.substring(1);
245: int slash = servletClass.indexOf('/');
246: if (slash >= 0) {
247: pathInfo = servletClass.substring(slash);
248: servletClass = servletClass.substring(0, slash);
249: } else {
250: pathInfo = "";
251: }
252:
253: if (servletClass.startsWith("org.apache.catalina")) {
254: response.sendError(HttpServletResponse.SC_NOT_FOUND,
255: inRequestURI);
256: return;
257: }
258:
259: if (debug >= 1)
260: log("Processing servlet '" + servletClass
261: + "' with path info '" + pathInfo + "'");
262: String name = "org.apache.catalina.INVOKER." + servletClass;
263: String pattern = inServletPath + "/" + servletClass + "/*";
264: Wrapper wrapper = null;
265:
266: // Synchronize to avoid race conditions when multiple requests
267: // try to initialize the same servlet at the same time
268: synchronized (this ) {
269:
270: // Are we referencing an existing servlet class or name?
271: wrapper = (Wrapper) context.findChild(servletClass);
272: if (wrapper == null)
273: wrapper = (Wrapper) context.findChild(name);
274: if (wrapper != null) {
275: String actualServletClass = wrapper.getServletClass();
276: if ((actualServletClass != null)
277: && (actualServletClass
278: .startsWith("org.apache.catalina"))) {
279: response.sendError(
280: HttpServletResponse.SC_NOT_FOUND,
281: inRequestURI);
282: return;
283: }
284: if (debug >= 1)
285: log("Using wrapper for servlet '"
286: + wrapper.getName() + "' with mapping '"
287: + pattern + "'");
288: context.addServletMapping(pattern, wrapper.getName());
289: }
290:
291: // No, create a new wrapper for the specified servlet class
292: else {
293:
294: if (debug >= 1)
295: log("Creating wrapper for '" + servletClass
296: + "' with mapping '" + pattern + "'");
297:
298: try {
299: wrapper = context.createWrapper();
300: wrapper.setName(name);
301: wrapper.setLoadOnStartup(1);
302: wrapper.setServletClass(servletClass);
303: context.addChild(wrapper);
304: context.addServletMapping(pattern, name);
305: } catch (Exception e) {
306: log(sm.getString("invokerServlet.cannotCreate",
307: inRequestURI), e);
308: context.removeServletMapping(pattern);
309: context.removeChild(wrapper);
310: if (included)
311: throw new ServletException(sm.getString(
312: "invokerServlet.cannotCreate",
313: inRequestURI), e);
314: else {
315: response.sendError(
316: HttpServletResponse.SC_NOT_FOUND,
317: inRequestURI);
318: return;
319: }
320: }
321: }
322:
323: }
324:
325: // Create a request wrapper to pass on to the invoked servlet
326: InvokerHttpRequest wrequest = new InvokerHttpRequest(request);
327: wrequest.setRequestURI(inRequestURI);
328: StringBuffer sb = new StringBuffer(inServletPath);
329: sb.append("/");
330: sb.append(servletClass);
331: wrequest.setServletPath(sb.toString());
332: if ((pathInfo == null) || (pathInfo.length() < 1)) {
333: wrequest.setPathInfo(null);
334: wrequest.setPathTranslated(null);
335: } else {
336: wrequest.setPathInfo(pathInfo);
337: wrequest.setPathTranslated(getServletContext().getRealPath(
338: pathInfo));
339: }
340:
341: // Allocate a servlet instance to perform this request
342: Servlet instance = null;
343: try {
344: instance = wrapper.allocate();
345: } catch (ServletException e) {
346: log(sm.getString("invokerServlet.allocate", inRequestURI),
347: e);
348: context.removeServletMapping(pattern);
349: context.removeChild(wrapper);
350: Throwable rootCause = e.getRootCause();
351: if (rootCause == null)
352: rootCause = e;
353: if (rootCause instanceof ClassNotFoundException) {
354: response.sendError(HttpServletResponse.SC_NOT_FOUND,
355: inRequestURI);
356: return;
357: } else if (rootCause instanceof IOException) {
358: throw (IOException) rootCause;
359: } else if (rootCause instanceof RuntimeException) {
360: throw (RuntimeException) rootCause;
361: } else if (rootCause instanceof ServletException) {
362: throw (ServletException) rootCause;
363: } else {
364: throw new ServletException(sm.getString(
365: "invokerServlet.allocate", inRequestURI),
366: rootCause);
367: }
368: }
369:
370: // After loading the wrapper, restore some of the fields when including
371: if (included) {
372: wrequest.setRequestURI(request.getRequestURI());
373: wrequest.setPathInfo(request.getPathInfo());
374: wrequest.setServletPath(request.getServletPath());
375: }
376:
377: // Invoke the service() method of the allocated servlet
378: try {
379: String jspFile = wrapper.getJspFile();
380: if (jspFile != null)
381: request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
382: else
383: request.removeAttribute(Globals.JSP_FILE_ATTR);
384: request.setAttribute(Globals.INVOKED_ATTR, request
385: .getServletPath());
386: instance.service(wrequest, response);
387: } catch (UnavailableException e) {
388: context.removeServletMapping(pattern);
389: throw e;
390: } finally {
391: request.removeAttribute(Globals.INVOKED_ATTR);
392: request.removeAttribute(Globals.JSP_FILE_ATTR);
393: // Deallocate the allocated servlet instance
394: try {
395: wrapper.deallocate(instance);
396: } catch (ServletException e) {
397: log(sm.getString("invokerServlet.deallocate",
398: inRequestURI), e);
399: throw e;
400: }
401: }
402:
403: }
404:
405: }
|