| /* |
| * Copyright 1999,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| package org.apache.catalina.servlets; |
| |
| |
| import java.io.IOException; |
| |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletException; |
| import javax.servlet.UnavailableException; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.catalina.ContainerServlet; |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Globals; |
| import org.apache.catalina.Wrapper; |
| import org.apache.catalina.util.StringManager; |
| |
| |
| /** |
| * The default servlet-invoking servlet for most web applications, |
| * used to serve requests to servlets that have not been registered |
| * in the web application deployment descriptor. |
| * |
| * @author Craig R. McClanahan |
| * @version $Revision$ $Date$ |
| */ |
| |
| public final class InvokerServlet |
| extends HttpServlet implements ContainerServlet { |
| |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * The Context container associated with our web application. |
| */ |
| private Context context = null; |
| |
| |
| /** |
| * The debugging detail level for this servlet. |
| */ |
| private int debug = 0; |
| |
| |
| /** |
| * The string manager for this package. |
| */ |
| private static StringManager sm = |
| StringManager.getManager(Constants.Package); |
| |
| |
| /** |
| * The Wrapper container associated with this servlet. |
| */ |
| private Wrapper wrapper = null; |
| |
| |
| // ----------------------------------------------- ContainerServlet Methods |
| |
| |
| /** |
| * Return the Wrapper with which we are associated. |
| */ |
| public Wrapper getWrapper() { |
| |
| return (this.wrapper); |
| |
| } |
| |
| |
| /** |
| * Set the Wrapper with which we are associated. |
| * |
| * @param wrapper The new wrapper |
| */ |
| public void setWrapper(Wrapper wrapper) { |
| |
| this.wrapper = wrapper; |
| if (wrapper == null) |
| context = null; |
| else |
| context = (Context) wrapper.getParent(); |
| |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Finalize this servlet. |
| */ |
| public void destroy() { |
| |
| ; // No actions necessary |
| |
| } |
| |
| |
| /** |
| * Process a GET request for the specified resource. |
| * |
| * @param request The servlet request we are processing |
| * @param response The servlet response we are creating |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet-specified error occurs |
| */ |
| public void doGet(HttpServletRequest request, |
| HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| serveRequest(request, response); |
| |
| } |
| |
| |
| /** |
| * Process a HEAD request for the specified resource. |
| * |
| * @param request The servlet request we are processing |
| * @param response The servlet response we are creating |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet-specified error occurs |
| */ |
| public void doHead(HttpServletRequest request, |
| HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| serveRequest(request, response); |
| |
| } |
| |
| |
| /** |
| * Process a POST request for the specified resource. |
| * |
| * @param request The servlet request we are processing |
| * @param response The servlet response we are creating |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet-specified error occurs |
| */ |
| public void doPost(HttpServletRequest request, |
| HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| serveRequest(request, response); |
| |
| } |
| |
| |
| /** |
| * Initialize this servlet. |
| */ |
| public void init() throws ServletException { |
| |
| // Ensure that our ContainerServlet properties have been set |
| if ((wrapper == null) || (context == null)) |
| throw new UnavailableException |
| (sm.getString("invokerServlet.noWrapper")); |
| |
| // Set our properties from the initialization parameters |
| String value = null; |
| try { |
| value = getServletConfig().getInitParameter("debug"); |
| debug = Integer.parseInt(value); |
| } catch (Throwable t) { |
| ; |
| } |
| if (debug >= 1) |
| log("init: Associated with Context '" + context.getPath() + "'"); |
| |
| } |
| |
| |
| |
| // -------------------------------------------------------- Private Methods |
| |
| |
| /** |
| * Serve the specified request, creating the corresponding response. |
| * After the first time a particular servlet class is requested, it will |
| * be served directly (like any registered servlet) because it will have |
| * been registered and mapped in our associated Context. |
| * |
| * @param request The servlet request we are processing |
| * @param response The servlet response we are creating |
| * |
| * @exception IOException if an input/output error occurs |
| * @exception ServletException if a servlet-specified error occurs |
| */ |
| public void serveRequest(HttpServletRequest request, |
| HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| // Disallow calling this servlet via a named dispatcher |
| if (request.getAttribute(Globals.NAMED_DISPATCHER_ATTR) != null) |
| throw new ServletException |
| (sm.getString("invokerServlet.notNamed")); |
| |
| // Identify the input parameters and our "included" state |
| String inRequestURI = null; |
| String inServletPath = null; |
| String inPathInfo = null; |
| boolean included = |
| (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null); |
| |
| if (included) { |
| inRequestURI = |
| (String) request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR); |
| inServletPath = |
| (String) request.getAttribute(Globals.INCLUDE_SERVLET_PATH_ATTR); |
| inPathInfo = |
| (String) request.getAttribute(Globals.INCLUDE_PATH_INFO_ATTR); |
| } else { |
| inRequestURI = request.getRequestURI(); |
| inServletPath = request.getServletPath(); |
| inPathInfo = request.getPathInfo(); |
| } |
| if (debug >= 1) { |
| log("included='" + included + "', requestURI='" + |
| inRequestURI + "'"); |
| log(" servletPath='" + inServletPath + "', pathInfo='" + |
| inPathInfo + "'"); |
| } |
| |
| // Make sure a servlet name or class name was specified |
| if (inPathInfo == null) { |
| if (debug >= 1) |
| log("Invalid pathInfo '" + inPathInfo + "'"); |
| if (included) |
| throw new ServletException |
| (sm.getString("invokerServlet.invalidPath", inRequestURI)); |
| else { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, |
| inRequestURI); |
| return; |
| } |
| } |
| |
| // Identify the outgoing servlet name or class, and outgoing path info |
| String pathInfo = inPathInfo; |
| String servletClass = pathInfo.substring(1); |
| int slash = servletClass.indexOf('/'); |
| // if (debug >= 2) |
| // log(" Calculating with servletClass='" + servletClass + |
| // "', pathInfo='" + pathInfo + "', slash=" + slash); |
| if (slash >= 0) { |
| pathInfo = servletClass.substring(slash); |
| servletClass = servletClass.substring(0, slash); |
| } else { |
| pathInfo = ""; |
| } |
| |
| if (servletClass.startsWith("org.apache.catalina")) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, |
| inRequestURI); |
| return; |
| } |
| |
| if (debug >= 1) |
| log("Processing servlet '" + servletClass + |
| "' with path info '" + pathInfo + "'"); |
| String name = "org.apache.catalina.INVOKER." + servletClass; |
| String pattern = inServletPath + "/" + servletClass + "/*"; |
| Wrapper wrapper = null; |
| |
| // Synchronize to avoid race conditions when multiple requests |
| // try to initialize the same servlet at the same time |
| synchronized (this) { |
| |
| // Are we referencing an existing servlet class or name? |
| wrapper = (Wrapper) context.findChild(servletClass); |
| if (wrapper == null) |
| wrapper = (Wrapper) context.findChild(name); |
| if (wrapper != null) { |
| String actualServletClass = wrapper.getServletClass(); |
| if ((actualServletClass != null) |
| && (actualServletClass.startsWith |
| ("org.apache.catalina"))) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, |
| inRequestURI); |
| return; |
| } |
| if (debug >= 1) |
| log("Using wrapper for servlet '" + |
| wrapper.getName() + "' with mapping '" + |
| pattern + "'"); |
| context.addServletMapping(pattern, wrapper.getName()); |
| } |
| |
| // No, create a new wrapper for the specified servlet class |
| else { |
| |
| if (debug >= 1) |
| log("Creating wrapper for '" + servletClass + |
| "' with mapping '" + pattern + "'"); |
| |
| try { |
| wrapper = context.createWrapper(); |
| wrapper.setName(name); |
| wrapper.setLoadOnStartup(1); |
| wrapper.setServletClass(servletClass); |
| context.addChild(wrapper); |
| context.addServletMapping(pattern, name); |
| } catch (Throwable t) { |
| log(sm.getString("invokerServlet.cannotCreate", |
| inRequestURI), t); |
| context.removeServletMapping(pattern); |
| context.removeChild(wrapper); |
| if (included) |
| throw new ServletException |
| (sm.getString("invokerServlet.cannotCreate", |
| inRequestURI), t); |
| else { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, |
| inRequestURI); |
| return; |
| } |
| } |
| } |
| |
| } |
| |
| // Create a request wrapper to pass on to the invoked servlet |
| InvokerHttpRequest wrequest = |
| new InvokerHttpRequest(request); |
| wrequest.setRequestURI(inRequestURI); |
| StringBuffer sb = new StringBuffer(inServletPath); |
| sb.append("/"); |
| sb.append(servletClass); |
| wrequest.setServletPath(sb.toString()); |
| if ((pathInfo == null) || (pathInfo.length() < 1)) { |
| wrequest.setPathInfo(null); |
| wrequest.setPathTranslated(null); |
| } else { |
| wrequest.setPathInfo(pathInfo); |
| wrequest.setPathTranslated |
| (getServletContext().getRealPath(pathInfo)); |
| } |
| |
| // Allocate a servlet instance to perform this request |
| Servlet instance = null; |
| try { |
| // if (debug >= 2) |
| // log(" Allocating servlet instance"); |
| instance = wrapper.allocate(); |
| } catch (ServletException e) { |
| log(sm.getString("invokerServlet.allocate", inRequestURI), e); |
| context.removeServletMapping(pattern); |
| context.removeChild(wrapper); |
| Throwable rootCause = e.getRootCause(); |
| if (rootCause == null) |
| rootCause = e; |
| if (rootCause instanceof ClassNotFoundException) { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND, |
| inRequestURI); |
| return; |
| } else if (rootCause instanceof IOException) { |
| throw (IOException) rootCause; |
| } else if (rootCause instanceof RuntimeException) { |
| throw (RuntimeException) rootCause; |
| } else if (rootCause instanceof ServletException) { |
| throw (ServletException) rootCause; |
| } else { |
| throw new ServletException |
| (sm.getString("invokerServlet.allocate", inRequestURI), |
| rootCause); |
| } |
| } catch (Throwable e) { |
| log(sm.getString("invokerServlet.allocate", inRequestURI), e); |
| context.removeServletMapping(pattern); |
| context.removeChild(wrapper); |
| throw new ServletException |
| (sm.getString("invokerServlet.allocate", inRequestURI), e); |
| } |
| |
| // After loading the wrapper, restore some of the fields when including |
| if (included) { |
| wrequest.setRequestURI(request.getRequestURI()); |
| wrequest.setPathInfo(request.getPathInfo()); |
| wrequest.setServletPath(request.getServletPath()); |
| } |
| |
| // Invoke the service() method of the allocated servlet |
| try { |
| String jspFile = wrapper.getJspFile(); |
| if (jspFile != null) |
| request.setAttribute(Globals.JSP_FILE_ATTR, jspFile); |
| else |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| request.setAttribute(Globals.INVOKED_ATTR, |
| request.getServletPath()); |
| // if (debug >= 2) |
| // log(" Calling service() method, jspFile=" + |
| // jspFile); |
| instance.service(wrequest, response); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| } catch (IOException e) { |
| // if (debug >= 2) |
| // log(" service() method IOException", e); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| try { |
| wrapper.deallocate(instance); |
| } catch (Throwable f) { |
| ; |
| } |
| throw e; |
| } catch (UnavailableException e) { |
| // if (debug >= 2) |
| // log(" service() method UnavailableException", e); |
| context.removeServletMapping(pattern); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| try { |
| wrapper.deallocate(instance); |
| } catch (Throwable f) { |
| ; |
| } |
| throw e; |
| } catch (ServletException e) { |
| // if (debug >= 2) |
| // log(" service() method ServletException", e); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| try { |
| wrapper.deallocate(instance); |
| } catch (Throwable f) { |
| ; |
| } |
| throw e; |
| } catch (RuntimeException e) { |
| // if (debug >= 2) |
| // log(" service() method RuntimeException", e); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| try { |
| wrapper.deallocate(instance); |
| } catch (Throwable f) { |
| ; |
| } |
| throw e; |
| } catch (Throwable e) { |
| // if (debug >= 2) |
| // log(" service() method Throwable", e); |
| request.removeAttribute(Globals.INVOKED_ATTR); |
| request.removeAttribute(Globals.JSP_FILE_ATTR); |
| try { |
| wrapper.deallocate(instance); |
| } catch (Throwable f) { |
| ; |
| } |
| throw new ServletException("Invoker service() exception", e); |
| } |
| |
| // Deallocate the allocated servlet instance |
| try { |
| // if (debug >= 2) |
| // log(" deallocate servlet instance"); |
| wrapper.deallocate(instance); |
| } catch (ServletException e) { |
| log(sm.getString("invokerServlet.deallocate", inRequestURI), e); |
| throw e; |
| } catch (Throwable e) { |
| log(sm.getString("invokerServlet.deallocate", inRequestURI), e); |
| throw new ServletException |
| (sm.getString("invokerServlet.deallocate", inRequestURI), e); |
| } |
| |
| } |
| |
| |
| } |