Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » core » [javadoc | source]
    1   /*
    2    * $Header: /u/cvs/Projects/EnhydraOrg/enhydra3x/Enhydra/modules/Tomcat/src/share/org/apache/tomcat/core/ServletWrapper.java,v 1.2.2.3 2000/04/07 08:37:20 peterj Exp $
    3    * $Revision: 1.2.2.3 $
    4    * $Date: 2000/04/07 08:37:20 $
    5    *
    6    * ====================================================================
    7    *
    8    * The Apache Software License, Version 1.1
    9    *
   10    * Copyright (c) 1999 The Apache Software Foundation.  All rights
   11    * reserved.
   12    *
   13    * Redistribution and use in source and binary forms, with or without
   14    * modification, are permitted provided that the following conditions
   15    * are met:
   16    *
   17    * 1. Redistributions of source code must retain the above copyright
   18    *    notice, this list of conditions and the following disclaimer.
   19    *
   20    * 2. Redistributions in binary form must reproduce the above copyright
   21    *    notice, this list of conditions and the following disclaimer in
   22    *    the documentation and/or other materials provided with the
   23    *    distribution.
   24    *
   25    * 3. The end-user documentation included with the redistribution, if
   26    *    any, must include the following acknowlegement:
   27    *       "This product includes software developed by the
   28    *        Apache Software Foundation (http://www.apache.org/)."
   29    *    Alternately, this acknowlegement may appear in the software itself,
   30    *    if and wherever such third-party acknowlegements normally appear.
   31    *
   32    * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   33    *    Foundation" must not be used to endorse or promote products derived
   34    *    from this software without prior written permission. For written
   35    *    permission, please contact apache@apache.org.
   36    *
   37    * 5. Products derived from this software may not be called "Apache"
   38    *    nor may "Apache" appear in their names without prior written
   39    *    permission of the Apache Group.
   40    *
   41    * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   42    * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   43    * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   44    * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   45    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   46    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   47    * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   48    * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   49    * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   50    * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   51    * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   52    * SUCH DAMAGE.
   53    * ====================================================================
   54    *
   55    * This software consists of voluntary contributions made by many
   56    * individuals on behalf of the Apache Software Foundation.  For more
   57    * information on the Apache Software Foundation, please see
   58    * <http://www.apache.org/>.
   59    *
   60    * [Additional notices, if required by prior licensing conditions]
   61    *
   62    */
   63   
   64   
   65   package org.apache.tomcat.core;
   66   
   67   import org.apache.tomcat.util;
   68   import java.io;
   69   import java.net;
   70   import java.util;
   71   import javax.servlet;
   72   import javax.servlet.http;
   73   
   74   /**
   75    *
   76    * @author James Duncan Davidson [duncan@eng.sun.com]
   77    * @author Jason Hunter [jch@eng.sun.com]
   78    * @author James Todd [gonzo@eng.sun.com]
   79    * @author Harish Prabandham
   80    */
   81   
   82   //
   83   // WARNING: Some of the APIs in this class are used by J2EE.
   84   // Please talk to harishp@eng.sun.com before making any changes.
   85   //
   86   class ServletWrapper {
   87   
   88       private StringManager sm =
   89           StringManager.getManager(Constants.Package);
   90       private Container container;
   91       private String description = null;
   92       private String servletClassName;
   93       private Class servletClass;
   94       private File servletClassFile;
   95       private String path = null;
   96       private Servlet servlet;
   97       private long lastAccessed;
   98       private ServletConfigImpl config;
   99       private boolean isReloadable = false;
  100       private long classFileLastMod = 0;
  101       private int serviceCount = 0;
  102   
  103       ServletWrapper(Container container) {
  104           this.container = container;
  105   
  106           config = new ServletConfigImpl(container.getContext());
  107       }
  108   
  109       void setReloadable(boolean reloadable) {
  110   	isReloadable = reloadable;
  111       }
  112   
  113       String getServletName() {
  114           return config.getServletName();
  115       }
  116   
  117       void setServletName(String servletName) {
  118           config.setServletName(servletName);
  119       }
  120   
  121       String getServletDescription() {
  122           return this.description;
  123       }
  124   
  125       void setServletDescription(String description) {
  126           this.description = description;
  127       }
  128   
  129       String getPath() {
  130           return this.path;
  131       }
  132   
  133       void setPath(String path) {
  134           this.path = path;
  135       }
  136   
  137       void setServletClassFile(File servletClassFile) {
  138   	this.servletClassFile = servletClassFile;
  139   	classFileLastMod = servletClassFile.lastModified();
  140   
  141   	config.setServletClassName(this.servletClassFile.getName());
  142       }
  143   
  144       String getServletClass() {
  145           return this.servletClassName;
  146       }
  147   
  148       void setServletClass(String servletClassName) {
  149           this.servletClassName = servletClassName;
  150   
  151   	config.setServletClassName(servletClassName);
  152       }
  153   
  154       void setServletClass(Class servletClass) {
  155   	this.servletClass = servletClass;
  156   
  157   	config.setServletClassName(this.servletClass.getName());
  158       }
  159   
  160       void setInitArgs(Hashtable initArgs) {
  161           config.setInitArgs(initArgs);
  162       }
  163   
  164       /**
  165        *
  166        */
  167   
  168       Servlet getServlet() {
  169           return servlet;
  170       }
  171   
  172   
  173       void destroy() {
  174   	// Fancy sync logic is to make sure that no threads are in the
  175   	// handlerequest when this is called and, furthermore, that
  176   	// no threads go through handle request after this method starts!
  177   
  178   	if (servlet != null) {
  179   	    synchronized (this) {
  180   		// Wait until there are no outstanding service calls,
  181   		// or until 30 seconds have passed (to avoid a hang)
  182   
  183   		while (serviceCount > 0) {
  184   		    try {
  185   		        wait(30000);
  186   
  187   			break;
  188   		    } catch (InterruptedException e) { }
  189   		}
  190   
  191   		try {
  192   		    final Servlet sinstance = servlet;
  193   		    Context context = getContext();
  194   
  195   		    handleInvocation(
  196   		        context.getDestroyInterceptors().elements(),
  197   			new LifecycleInvocationHandler(context, servlet) {
  198   		            void method() throws ServletException {
  199   			        sinstance.destroy();
  200   			    }
  201   		        });
  202   		} catch(IOException ioe) {
  203   		    // Should never come here...
  204   		} catch(ServletException se) {
  205   		    // Should never come here...
  206   		}
  207   	    }
  208   	}
  209       }
  210   
  211       void loadServlet()
  212       throws ClassNotFoundException, InstantiationException,
  213           IllegalAccessException, ServletException {
  214           // Check if this is a JSP, they get special treatment
  215   
  216           if (path != null &&
  217               servletClass == null &&
  218               servletClassName == null) {
  219   	    // XXX XXX XXX
  220   	    // core shouldn't depend on a particular connector!
  221   	    // need to find out what this code does!
  222   	    RequestAdapterImpl reqA=new RequestAdapterImpl();
  223   	    ResponseAdapterImpl resA=new ResponseAdapterImpl();
  224   
  225   	    Request request = new Request();
  226               Response response = new Response();
  227               request.recycle();
  228               response.recycle();
  229   
  230   	    request.setRequestAdapter( reqA );
  231   	    response.setResponseAdapter( resA );
  232   
  233               request.setResponse(response);
  234               response.setRequest(request);
  235   
  236               String requestURI = path + "?" +
  237                   Constants.JSP.Directive.Compile.Name + "=" +
  238                   Constants.JSP.Directive.Compile.Value;
  239   
  240               reqA.setRequestURI(getContext().getPath() + path);
  241   	    reqA.setQueryString( Constants.JSP.Directive.Compile.Name + "=" +
  242   				 Constants.JSP.Directive.Compile.Value );
  243   
  244               request.setContext(getContext());
  245               request.getSession(true);
  246   
  247               RequestDispatcher rd =
  248                   config.getServletContext().getRequestDispatcher(requestURI);
  249   
  250               try {
  251                   rd.forward(request.getFacade(), response.getFacade());
  252               } catch (ServletException se) {
  253               } catch (IOException ioe) {
  254               }
  255           } else {
  256   	    if (servletClass == null) {
  257   	        if (servletClassName == null) {
  258   		    String msg = sm.getString("wrapper.load.noclassname");
  259   
  260   		    throw new IllegalStateException(msg);
  261   	        }
  262   	        servletClass = container.getLoader().loadServlet(this,
  263                       servletClassName);
  264   	    }
  265   
  266               // make sure we have a classname or class def
  267               //if (servletClassName == null || servletClass == null) {
  268               //    String msg = sm.getString("wrapper.load.noclassname");
  269               //    throw new IllegalStateException(msg);
  270   	    // }
  271   	    //Class c = container.getLoader().loadServlet(this,
  272   	    //servletClassName);
  273   
  274   	    servlet = (Servlet)servletClass.newInstance();
  275   
  276   	    config.setServletClassName(servlet.getClass().getName());
  277   
  278   	    try {
  279   	        final Servlet sinstance = servlet;
  280   	        final ServletConfigImpl servletConfig = config;
  281   	        Context context = getContext();
  282   
  283   	        handleInvocation(context.getInitInterceptors().elements(),
  284   	            new LifecycleInvocationHandler(context, servlet) {
  285   	                void method() throws ServletException {
  286   		            sinstance.init(servletConfig);
  287   		        }
  288   	            });
  289   	    } catch(IOException ioe) {
  290   	    // Should never come here...
  291   	    }
  292           }
  293       }
  294   
  295       private Context getContext() {
  296   	return this.container.getContext();
  297       }
  298   
  299       void handleRequest(final HttpServletRequestFacade request,
  300           final HttpServletResponseFacade response)
  301       throws IOException {
  302   	//  if (isReloadable) {
  303   //  	    long lm = servletClassFile.lastModified();
  304   //  	    if (lm > classFileLastMod) {
  305   //  		//container.recycle();
  306   //  	    }
  307   //  	}
  308   	// make sure that only one thread goes through
  309   	// this block at a time!
  310   
  311           synchronized (this) {
  312   	    // XXX
  313   	    // rather klunky - this method needs a once over
  314   
  315   	    if (path != null &&
  316                   servletClass == null &&
  317                   servletClassName == null) {
  318                   String requestURI = path + request.getPathInfo();
  319   	        RequestDispatcher rd =
  320                       request.getRequestDispatcher(requestURI);
  321   
  322   		try {
  323   		    // Watch out, ugly code ahead...
  324   		    // We need to do a forward or include here, but we can't
  325   		    // easily determine which.  So we try a forward, and if
  326   		    // there's an IllegalStateException thrown, then we know
  327   		    // we should have tried an include, so we do the include.
  328   		    // It's so ugly I have to giggle.
  329   		    // All this to support dispatching to named JSPs!
  330   		    try {
  331   		        rd.forward(request, response);
  332   		    } catch (IllegalStateException e) {
  333   			rd.include(request, response);
  334   		    }
  335   		} catch (ServletException se) {
  336                       //FIXME: markd@lutris.com: Need to log here.
  337   		    response.sendError(404);
  338   		} catch (IOException ioe) {
  339                       //FIXME: markd@lutris.com: Need to log here.
  340   		    response.sendError(404);
  341   		}
  342   
  343   		return;
  344   	    } else {
  345   	        if (servlet == null) {
  346   		    try {
  347   		        loadServlet();
  348   		    } catch (ClassNotFoundException e) {
  349                           //FIXME: markd@lutris.com: Need to log here.
  350                           container.getContext().getLogChannel().write(com.lutris.logging.Logger.ERROR,
  351                                       "Class not found " + servletClassName);
  352   		        response.sendError(404);
  353   
  354   			return;
  355   		    } catch (Exception e) {
  356   		        // Make sure the servlet will never
  357   		        // service a request
  358   
  359   		        servlet = null;
  360   
  361   			// XXX
  362   			// check to see what kind of exception it was --
  363   			// maybe it should be reported to the user
  364   			// differently or at least logged differently
  365   
  366   			// XXX
  367   			// we really need to pick up an error file on
  368   			// a per context basis or, failing that from the
  369   			// classpath
  370   
  371                           //FIXME: markd@lutris.com: Need to log here.
  372   			sendInternalServletError(e, response);
  373   
  374   			return;
  375   		    }
  376   		}
  377   	    }
  378   	}
  379   
  380           try {
  381   	    synchronized(this) {
  382   		serviceCount++;
  383   	    }
  384   
  385   	    Context context = getContext();
  386               Enumeration serviceInterceptors =
  387                   context.getServiceInterceptors().elements();
  388               ServiceInvocationHandler serviceHandler =
  389                   new ServiceInvocationHandler(context, servlet,
  390                       request, response);
  391   
  392   	    handleInvocation(serviceInterceptors, serviceHandler);
  393   	} catch (ServletException e) {
  394               // XXX
  395   	    // check to see if it's unavailable and set internal status
  396   	    // appropriately
  397   
  398   	    // XXX
  399   	    // if it's an unvailable exception, we probably want
  400   	    // to paint a different screen
  401   
  402               handleException(request, response, e);
  403   
  404   	    return;
  405           } catch (SocketException e) {
  406   	    // XXX
  407   	    // Catch and eat all SocketExceptions
  408   	    // *Should* only eat client disconnected socket exceptions
  409   
  410   	    return;
  411           } catch (Throwable e) {
  412   	    // if we are in an include, then we should rethrow the
  413   	    // exception
  414   
  415   	    // XXX
  416   	    // we need a better way of dealing with figuring out
  417   	    // if we are in an include -- this particular gem
  418   	    // will pass IllegalStateException when we are in
  419   	    // an include 'cause users will like to know when
  420   	    // that happens in their included servlet
  421   
  422   	    //if (e instanceof IllegalStateException) {
  423   	    //    String str = (String)request.getAttribute
  424   	    //    (Constants.Attribute.RequestURI);
  425   	    //    if (str != null) {
  426   	    //        throw (IllegalStateException)e;
  427   	    //    }
  428   	    //}
  429   
  430   	    // XXX
  431   	    // decide which exceptions we should not eat at this point
  432               handleException(request, response, e);
  433   
  434   	    return;
  435   	} finally {
  436   	    synchronized(this) {
  437   		serviceCount--;
  438   		notifyAll();
  439   	    }
  440   	}
  441       }
  442   
  443       //FIXME: markd@lutris.com: Need to log here.
  444       public void handleException(HttpServletRequestFacade request,
  445           HttpServletResponseFacade response,
  446       Throwable t) {
  447           Context context = request.getRealRequest().getContext();
  448           ServletContextFacade contextFacade = context.getFacade();
  449   
  450           // Scan the exception's inheritance tree looking for a rule
  451           // that this type of exception should be forwarded
  452   
  453           String path = null;
  454           Class clazz = t.getClass();
  455   
  456           while (path == null && clazz != null) {
  457               String name = clazz.getName();
  458               path = context.getErrorPage(name);
  459               clazz = clazz.getSuperclass();
  460           }
  461   
  462           // If path is non-null, we should do a forward
  463           // Don't do a forward if exception_type is already defined though to
  464           // avoid an infinite loop.
  465   
  466           if (path != null &&
  467   	    request.getAttribute(
  468                   Constants.Attribute.ERROR_EXCEPTION_TYPE) == null) {
  469               RequestDispatcher rd = contextFacade.getRequestDispatcher(path);
  470   
  471               // XXX
  472               // The spec should really be changed to allow us to include
  473               // the full exception object.  Oh well.
  474   
  475               request.setAttribute(Constants.Attribute.ERROR_EXCEPTION_TYPE,
  476   	        t.getClass().getName());
  477               request.setAttribute(Constants.Attribute.ERROR_MESSAGE,
  478                   t.getMessage());
  479   
  480               try {
  481                   try {
  482                       // A forward would be ideal, so reset and try it
  483   
  484                       response.getRealResponse().reset();
  485                       rd.forward(request, response);
  486                   } catch (IllegalStateException ise) {
  487                       // Oops, too late for a forward; settle for an include
  488   
  489                       rd.include(request, response);
  490                   }
  491               } catch (IOException e) {
  492                   // Shouldn't get here
  493               } catch (ServletException e) {
  494                   // Shouldn't get here
  495               }
  496           } else {
  497               try {
  498                   sendInternalServletError(t, response);
  499               } catch (IOException e) {
  500                   // ???
  501               }
  502           }
  503       }
  504   
  505       void sendInternalServletError(Throwable e,
  506           HttpServletResponseFacade response)
  507       throws IOException {
  508   	StringWriter sw = new StringWriter();
  509   	PrintWriter pw = new PrintWriter(sw);
  510   
  511   	pw.println("<b>Internal Servlet Error:</b><br>");
  512           pw.println("<pre>");
  513   	e.printStackTrace(pw);
  514   	pw.println("</pre>");
  515   
  516           if (e instanceof ServletException) {
  517   	    printRootCause((ServletException) e, pw);
  518   	}
  519   
  520   	response.sendError(500, sw.toString());
  521       }
  522   
  523       void printRootCause(ServletException e, PrintWriter out) {
  524           Throwable cause = e.getRootCause();
  525   
  526   	if (cause != null) {
  527   	    out.println("<b>Root cause:</b>");
  528   	    out.println("<pre>");
  529   	    cause.printStackTrace(out);
  530   	    out.println("</pre>");
  531   
  532   	    if (cause instanceof ServletException) {
  533   		printRootCause((ServletException)cause, out);  // recurse
  534   	    }
  535   	}
  536       }
  537   
  538       private void handleInvocation(Enumeration interceptors,
  539           InvocationHandler inv)
  540       throws ServletException, IOException {
  541   	Stack iStack = new Stack();
  542   
  543   	try {
  544   	    for (Enumeration e = interceptors; e.hasMoreElements(); ) {
  545   		iStack.push(e.nextElement());
  546   		inv.preInvoke(iStack.peek());
  547   	    }
  548   
  549   	    inv.method();
  550   	} catch(InterceptorException ie) {
  551   	} finally {
  552   	    // in any case, we should make sure we call the
  553   	    // postInvoke before leaving.
  554   
  555   	    while (! iStack.empty()) {
  556   		try {
  557   		    inv.postInvoke(iStack.pop());
  558   		} catch(InterceptorException ie) {
  559   		    // can't do much ....
  560   		}
  561   	    }
  562   	}
  563       }
  564   }
  565   
  566   
  567   //
  568   // WARNING: Some of the APIs in this class are used by J2EE.
  569   // Please talk to harishp@eng.sun.com before making any changes.
  570   //
  571   abstract class InvocationHandler {
  572       protected Servlet servlet;
  573       protected Context context;
  574   
  575       InvocationHandler(Context context, Servlet servlet) {
  576   	this.context = context;
  577   	this.servlet = servlet;
  578       }
  579   
  580       abstract void preInvoke(Object interceptor)
  581       throws InterceptorException;
  582   
  583       abstract void method()
  584       throws ServletException, IOException;
  585   
  586       abstract void postInvoke(Object interceptor)
  587       throws InterceptorException;
  588   }
  589   
  590   
  591   //
  592   // WARNING: Some of the APIs in this class are used by J2EE.
  593   // Please talk to harishp@eng.sun.com before making any changes.
  594   //
  595   abstract class LifecycleInvocationHandler extends InvocationHandler {
  596       LifecycleInvocationHandler(Context context, Servlet servlet) {
  597   	super(context, servlet);
  598       }
  599   
  600       void preInvoke(Object interceptor)
  601       throws InterceptorException {
  602   	((LifecycleInterceptor)interceptor).preInvoke(context, servlet);
  603       }
  604   
  605       void postInvoke(Object interceptor)
  606       throws InterceptorException {
  607   	((LifecycleInterceptor)interceptor).postInvoke(context, servlet);
  608       }
  609   }
  610   
  611   //
  612   // WARNING: Some of the APIs in this class are used by J2EE.
  613   // Please talk to harishp@eng.sun.com before making any changes.
  614   //
  615   class ServiceInvocationHandler extends InvocationHandler {
  616       private HttpServletRequestFacade request;
  617       private HttpServletResponseFacade response;
  618   
  619       ServiceInvocationHandler(Context context, Servlet servlet,
  620           HttpServletRequestFacade request,
  621   	HttpServletResponseFacade response) {
  622   	super(context, servlet);
  623   
  624   	this.request = request;
  625   	this.response = response;
  626       }
  627   
  628       void preInvoke(Object interceptor)
  629       throws InterceptorException {
  630   	((ServiceInterceptor)interceptor).preInvoke(context, servlet,
  631               request, response);
  632       }
  633   
  634       void method()
  635       throws ServletException, IOException {
  636   	if (servlet instanceof SingleThreadModel) {
  637   	    synchronized(servlet) {
  638   		servlet.service(request, response);
  639   	    }
  640   	} else {
  641   	    servlet.service(request, response);
  642   	}
  643       }
  644   
  645       void postInvoke(Object interceptor)
  646       throws InterceptorException {
  647   	((ServiceInterceptor)interceptor).postInvoke(context, servlet,
  648               request, response);
  649       }
  650   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » core » [javadoc | source]