0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v 1.39 2002/06/26 13:41:12 glenn Exp $
0003: * $Revision: 1.39 $
0004: * $Date: 2002/06/26 13:41:12 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999-2002 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.core;
0065:
0066: import java.io.File;
0067: import java.io.IOException;
0068: import java.io.PrintStream;
0069: import java.net.URL;
0070: import java.util.Enumeration;
0071: import java.util.HashMap;
0072: import java.util.Stack;
0073: import javax.servlet.Servlet;
0074: import javax.servlet.ServletConfig;
0075: import javax.servlet.ServletContext;
0076: import javax.servlet.ServletException;
0077: import javax.servlet.SingleThreadModel;
0078: import javax.servlet.UnavailableException;
0079: import javax.servlet.http.HttpServletResponse;
0080: import org.apache.catalina.Container;
0081: import org.apache.catalina.ContainerServlet;
0082: import org.apache.catalina.Context;
0083: import org.apache.catalina.InstanceEvent;
0084: import org.apache.catalina.InstanceListener;
0085: import org.apache.catalina.Lifecycle;
0086: import org.apache.catalina.LifecycleException;
0087: import org.apache.catalina.Loader;
0088: import org.apache.catalina.Logger;
0089: import org.apache.catalina.Request;
0090: import org.apache.catalina.Response;
0091: import org.apache.catalina.Wrapper;
0092: import org.apache.catalina.connector.HttpRequestBase;
0093: import org.apache.catalina.connector.HttpResponseBase;
0094: import org.apache.catalina.loader.StandardClassLoader;
0095: import org.apache.catalina.util.Enumerator;
0096: import org.apache.catalina.util.InstanceSupport;
0097: import org.apache.tomcat.util.log.SystemLogHandler;
0098:
0099: /**
0100: * Standard implementation of the <b>Wrapper</b> interface that represents
0101: * an individual servlet definition. No child Containers are allowed, and
0102: * the parent Container must be a Context.
0103: *
0104: * @author Craig R. McClanahan
0105: * @author Remy Maucherat
0106: * @version $Revision: 1.39 $ $Date: 2002/06/26 13:41:12 $
0107: */
0108:
0109: public final class StandardWrapper extends ContainerBase implements
0110: ServletConfig, Wrapper {
0111:
0112: // ----------------------------------------------------------- Constructors
0113:
0114: /**
0115: * Create a new StandardWrapper component with the default basic Valve.
0116: */
0117: public StandardWrapper() {
0118:
0119: super ();
0120: pipeline.setBasic(new StandardWrapperValve());
0121:
0122: }
0123:
0124: // ----------------------------------------------------- Instance Variables
0125:
0126: /**
0127: * The date and time at which this servlet will become available (in
0128: * milliseconds since the epoch), or zero if the servlet is available.
0129: * If this value equals Long.MAX_VALUE, the unavailability of this
0130: * servlet is considered permanent.
0131: */
0132: private long available = 0L;
0133:
0134: /**
0135: * The count of allocations that are currently active (even if they
0136: * are for the same instance, as will be true on a non-STM servlet).
0137: */
0138: private int countAllocated = 0;
0139:
0140: /**
0141: * The debugging detail level for this component.
0142: */
0143: private int debug = 0;
0144:
0145: /**
0146: * The facade associated with this wrapper.
0147: */
0148: private StandardWrapperFacade facade = new StandardWrapperFacade(
0149: this );
0150:
0151: /**
0152: * The descriptive information string for this implementation.
0153: */
0154: private static final String info = "org.apache.catalina.core.StandardWrapper/1.0";
0155:
0156: /**
0157: * The (single) initialized instance of this servlet.
0158: */
0159: private Servlet instance = null;
0160:
0161: /**
0162: * The support object for our instance listeners.
0163: */
0164: private InstanceSupport instanceSupport = new InstanceSupport(this );
0165:
0166: /**
0167: * The context-relative URI of the JSP file for this servlet.
0168: */
0169: private String jspFile = null;
0170:
0171: /**
0172: * The load-on-startup order value (negative value means load on
0173: * first call) for this servlet.
0174: */
0175: private int loadOnStartup = -1;
0176:
0177: /**
0178: * The initialization parameters for this servlet, keyed by
0179: * parameter name.
0180: */
0181: private HashMap parameters = new HashMap();
0182:
0183: /**
0184: * The security role references for this servlet, keyed by role name
0185: * used in the servlet. The corresponding value is the role name of
0186: * the web application itself.
0187: */
0188: private HashMap references = new HashMap();
0189:
0190: /**
0191: * The run-as identity for this servlet.
0192: */
0193: private String runAs = null;
0194:
0195: /**
0196: * The fully qualified servlet class name for this servlet.
0197: */
0198: private String servletClass = null;
0199:
0200: /**
0201: * Does this servlet implement the SingleThreadModel interface?
0202: */
0203: private boolean singleThreadModel = false;
0204:
0205: /**
0206: * Are we unloading our servlet instance at the moment?
0207: */
0208: private boolean unloading = false;
0209:
0210: /**
0211: * Maximum number of STM instances.
0212: */
0213: private int maxInstances = 20;
0214:
0215: /**
0216: * Number of instances currently loaded for a STM servlet.
0217: */
0218: private int nInstances = 0;
0219:
0220: /**
0221: * Stack containing the STM instances.
0222: */
0223: private Stack instancePool = null;
0224:
0225: // ------------------------------------------------------------- Properties
0226:
0227: /**
0228: * Return the available date/time for this servlet, in milliseconds since
0229: * the epoch. If this date/time is in the future, any request for this
0230: * servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero,
0231: * the servlet is currently available. A value equal to Long.MAX_VALUE
0232: * is considered to mean that unavailability is permanent.
0233: */
0234: public long getAvailable() {
0235:
0236: return (this .available);
0237:
0238: }
0239:
0240: /**
0241: * Set the available date/time for this servlet, in milliseconds since the
0242: * epoch. If this date/time is in the future, any request for this servlet
0243: * will return an SC_SERVICE_UNAVAILABLE error.
0244: *
0245: * @param available The new available date/time
0246: */
0247: public void setAvailable(long available) {
0248:
0249: long oldAvailable = this .available;
0250: if (available > System.currentTimeMillis())
0251: this .available = available;
0252: else
0253: this .available = 0L;
0254: support.firePropertyChange("available", new Long(oldAvailable),
0255: new Long(this .available));
0256:
0257: }
0258:
0259: /**
0260: * Return the number of active allocations of this servlet, even if they
0261: * are all for the same instance (as will be true for servlets that do
0262: * not implement <code>SingleThreadModel</code>.
0263: */
0264: public int getCountAllocated() {
0265:
0266: return (this .countAllocated);
0267:
0268: }
0269:
0270: /**
0271: * Return the debugging detail level for this component.
0272: */
0273: public int getDebug() {
0274:
0275: return (this .debug);
0276:
0277: }
0278:
0279: /**
0280: * Set the debugging detail level for this component.
0281: *
0282: * @param debug The new debugging detail level
0283: */
0284: public void setDebug(int debug) {
0285:
0286: int oldDebug = this .debug;
0287: this .debug = debug;
0288: support.firePropertyChange("debug", new Integer(oldDebug),
0289: new Long(this .debug));
0290:
0291: }
0292:
0293: /**
0294: * Return descriptive information about this Container implementation and
0295: * the corresponding version number, in the format
0296: * <code><description>/<version></code>.
0297: */
0298: public String getInfo() {
0299:
0300: return (info);
0301:
0302: }
0303:
0304: /**
0305: * Return the InstanceSupport object for this Wrapper instance.
0306: */
0307: public InstanceSupport getInstanceSupport() {
0308:
0309: return (this .instanceSupport);
0310:
0311: }
0312:
0313: /**
0314: * Return the context-relative URI of the JSP file for this servlet.
0315: */
0316: public String getJspFile() {
0317:
0318: return (this .jspFile);
0319:
0320: }
0321:
0322: /**
0323: * Set the context-relative URI of the JSP file for this servlet.
0324: *
0325: * @param jspFile JSP file URI
0326: */
0327: public void setJspFile(String jspFile) {
0328:
0329: // if ((jspFile != null) && !jspFile.startsWith("/"))
0330: // throw new IllegalArgumentException
0331: // (sm.getString("standardWrapper.jspFile.format", jspFile));
0332:
0333: String oldJspFile = this .jspFile;
0334: this .jspFile = jspFile;
0335: support.firePropertyChange("jspFile", oldJspFile, this .jspFile);
0336:
0337: }
0338:
0339: /**
0340: * Return the load-on-startup order value (negative value means
0341: * load on first call).
0342: */
0343: public int getLoadOnStartup() {
0344:
0345: return (this .loadOnStartup);
0346:
0347: }
0348:
0349: /**
0350: * Set the load-on-startup order value (negative value means
0351: * load on first call).
0352: *
0353: * @param value New load-on-startup value
0354: */
0355: public void setLoadOnStartup(int value) {
0356:
0357: int oldLoadOnStartup = this .loadOnStartup;
0358: this .loadOnStartup = value;
0359: support.firePropertyChange("loadOnStartup", new Integer(
0360: oldLoadOnStartup), new Integer(this .loadOnStartup));
0361:
0362: }
0363:
0364: /**
0365: * Set the load-on-startup order value from a (possibly null) string.
0366: * Per the specification, any missing or non-numeric value is converted
0367: * to a zero, so that this servlet will still be loaded at startup
0368: * time, but in an arbitrary order.
0369: *
0370: * @param value New load-on-startup value
0371: */
0372: public void setLoadOnStartupString(String value) {
0373:
0374: try {
0375: setLoadOnStartup(Integer.parseInt(value));
0376: } catch (NumberFormatException e) {
0377: setLoadOnStartup(0);
0378: }
0379:
0380: }
0381:
0382: /**
0383: * Return maximum number of instances that will be allocated when a single
0384: * thread model servlet is used.
0385: */
0386: public int getMaxInstances() {
0387:
0388: return (this .maxInstances);
0389:
0390: }
0391:
0392: /**
0393: * Set the maximum number of instances that will be allocated when a single
0394: * thread model servlet is used.
0395: *
0396: * @param maxInstnces New value of maxInstances
0397: */
0398: public void setMaxInstances(int maxInstances) {
0399:
0400: int oldMaxInstances = this .maxInstances;
0401: this .maxInstances = maxInstances;
0402: support.firePropertyChange("maxInstances", oldMaxInstances,
0403: this .maxInstances);
0404:
0405: }
0406:
0407: /**
0408: * Set the parent Container of this Wrapper, but only if it is a Context.
0409: *
0410: * @param container Proposed parent Container
0411: */
0412: public void setParent(Container container) {
0413:
0414: if ((container != null) && !(container instanceof Context))
0415: throw new IllegalArgumentException(sm
0416: .getString("standardWrapper.notContext"));
0417: super .setParent(container);
0418:
0419: }
0420:
0421: /**
0422: * Return the run-as identity for this servlet.
0423: */
0424: public String getRunAs() {
0425:
0426: return (this .runAs);
0427:
0428: }
0429:
0430: /**
0431: * Set the run-as identity for this servlet.
0432: *
0433: * @param value New run-as identity value
0434: */
0435: public void setRunAs(String runAs) {
0436:
0437: String oldRunAs = this .runAs;
0438: this .runAs = runAs;
0439: support.firePropertyChange("runAs", oldRunAs, this .runAs);
0440:
0441: }
0442:
0443: /**
0444: * Return the fully qualified servlet class name for this servlet.
0445: */
0446: public String getServletClass() {
0447:
0448: return (this .servletClass);
0449:
0450: }
0451:
0452: /**
0453: * Set the fully qualified servlet class name for this servlet.
0454: *
0455: * @param servletClass Servlet class name
0456: */
0457: public void setServletClass(String servletClass) {
0458:
0459: String oldServletClass = this .servletClass;
0460: this .servletClass = servletClass;
0461: support.firePropertyChange("servletClass", oldServletClass,
0462: this .servletClass);
0463:
0464: }
0465:
0466: /**
0467: * Set the name of this servlet. This is an alias for the normal
0468: * <code>Container.setName()</code> method, and complements the
0469: * <code>getServletName()</code> method required by the
0470: * <code>ServletConfig</code> interface.
0471: *
0472: * @param name The new name of this servlet
0473: */
0474: public void setServletName(String name) {
0475:
0476: setName(name);
0477:
0478: }
0479:
0480: /**
0481: * Return <code>true</code> if the servlet class represented by this
0482: * component implements the <code>SingleThreadModel</code> interface.
0483: */
0484: public boolean isSingleThreadModel() {
0485:
0486: try {
0487: loadServlet();
0488: } catch (Throwable t) {
0489: ;
0490: }
0491: return (singleThreadModel);
0492:
0493: }
0494:
0495: /**
0496: * Is this servlet currently unavailable?
0497: */
0498: public boolean isUnavailable() {
0499:
0500: if (available == 0L)
0501: return (false);
0502: else if (available <= System.currentTimeMillis()) {
0503: available = 0L;
0504: return (false);
0505: } else
0506: return (true);
0507:
0508: }
0509:
0510: // --------------------------------------------------------- Public Methods
0511:
0512: /**
0513: * Refuse to add a child Container, because Wrappers are the lowest level
0514: * of the Container hierarchy.
0515: *
0516: * @param child Child container to be added
0517: */
0518: public void addChild(Container child) {
0519:
0520: throw new IllegalStateException(sm
0521: .getString("standardWrapper.notChild"));
0522:
0523: }
0524:
0525: /**
0526: * Add a new servlet initialization parameter for this servlet.
0527: *
0528: * @param name Name of this initialization parameter to add
0529: * @param value Value of this initialization parameter to add
0530: */
0531: public void addInitParameter(String name, String value) {
0532:
0533: synchronized (parameters) {
0534: parameters.put(name, value);
0535: }
0536: fireContainerEvent("addInitParameter", name);
0537:
0538: }
0539:
0540: /**
0541: * Add a new listener interested in InstanceEvents.
0542: *
0543: * @param listener The new listener
0544: */
0545: public void addInstanceListener(InstanceListener listener) {
0546:
0547: instanceSupport.addInstanceListener(listener);
0548:
0549: }
0550:
0551: /**
0552: * Add a new security role reference record to the set of records for
0553: * this servlet.
0554: *
0555: * @param name Role name used within this servlet
0556: * @param link Role name used within the web application
0557: */
0558: public void addSecurityReference(String name, String link) {
0559:
0560: synchronized (references) {
0561: references.put(name, link);
0562: }
0563: fireContainerEvent("addSecurityReference", name);
0564:
0565: }
0566:
0567: /**
0568: * Allocate an initialized instance of this Servlet that is ready to have
0569: * its <code>service()</code> method called. If the servlet class does
0570: * not implement <code>SingleThreadModel</code>, the (only) initialized
0571: * instance may be returned immediately. If the servlet class implements
0572: * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
0573: * that this instance is not allocated again until it is deallocated by a
0574: * call to <code>deallocate()</code>.
0575: *
0576: * @exception ServletException if the servlet init() method threw
0577: * an exception
0578: * @exception ServletException if a loading error occurs
0579: */
0580: public Servlet allocate() throws ServletException {
0581:
0582: if (debug >= 1)
0583: log("Allocating an instance");
0584:
0585: // If we are currently unloading this servlet, throw an exception
0586: if (unloading)
0587: throw new ServletException(sm.getString(
0588: "standardWrapper.unloading", getName()));
0589:
0590: // If not SingleThreadedModel, return the same instance every time
0591: if (!singleThreadModel) {
0592:
0593: // Load and initialize our instance if necessary
0594: if (instance == null) {
0595: synchronized (this ) {
0596: if (instance == null) {
0597: try {
0598: instance = loadServlet();
0599: } catch (ServletException e) {
0600: throw e;
0601: } catch (Throwable e) {
0602: throw new ServletException(
0603: sm
0604: .getString("standardWrapper.allocate"),
0605: e);
0606: }
0607: }
0608: }
0609: }
0610:
0611: if (!singleThreadModel) {
0612: if (debug >= 2)
0613: log(" Returning non-STM instance");
0614: countAllocated++;
0615: return (instance);
0616: }
0617:
0618: }
0619:
0620: synchronized (instancePool) {
0621:
0622: while (countAllocated >= nInstances) {
0623: // Allocate a new instance if possible, or else wait
0624: if (nInstances < maxInstances) {
0625: try {
0626: instancePool.push(loadServlet());
0627: nInstances++;
0628: } catch (ServletException e) {
0629: throw e;
0630: } catch (Throwable e) {
0631: throw new ServletException(sm
0632: .getString("standardWrapper.allocate"),
0633: e);
0634: }
0635: } else {
0636: try {
0637: instancePool.wait();
0638: } catch (InterruptedException e) {
0639: ;
0640: }
0641: }
0642: }
0643: if (debug >= 2)
0644: log(" Returning allocated STM instance");
0645: countAllocated++;
0646: return (Servlet) instancePool.pop();
0647:
0648: }
0649:
0650: }
0651:
0652: /**
0653: * Return this previously allocated servlet to the pool of available
0654: * instances. If this servlet class does not implement SingleThreadModel,
0655: * no action is actually required.
0656: *
0657: * @param servlet The servlet to be returned
0658: *
0659: * @exception ServletException if a deallocation error occurs
0660: */
0661: public void deallocate(Servlet servlet) throws ServletException {
0662:
0663: // If not SingleThreadModel, no action is required
0664: if (!singleThreadModel) {
0665: countAllocated--;
0666: return;
0667: }
0668:
0669: // Unlock and free this instance
0670: synchronized (instancePool) {
0671: countAllocated--;
0672: instancePool.push(servlet);
0673: instancePool.notify();
0674: }
0675:
0676: }
0677:
0678: /**
0679: * Return the value for the specified initialization parameter name,
0680: * if any; otherwise return <code>null</code>.
0681: *
0682: * @param name Name of the requested initialization parameter
0683: */
0684: public String findInitParameter(String name) {
0685:
0686: synchronized (parameters) {
0687: return ((String) parameters.get(name));
0688: }
0689:
0690: }
0691:
0692: /**
0693: * Return the names of all defined initialization parameters for this
0694: * servlet.
0695: */
0696: public String[] findInitParameters() {
0697:
0698: synchronized (parameters) {
0699: String results[] = new String[parameters.size()];
0700: return ((String[]) parameters.keySet().toArray(results));
0701: }
0702:
0703: }
0704:
0705: /**
0706: * Return the security role link for the specified security role
0707: * reference name, if any; otherwise return <code>null</code>.
0708: *
0709: * @param name Security role reference used within this servlet
0710: */
0711: public String findSecurityReference(String name) {
0712:
0713: synchronized (references) {
0714: return ((String) references.get(name));
0715: }
0716:
0717: }
0718:
0719: /**
0720: * Return the set of security role reference names associated with
0721: * this servlet, if any; otherwise return a zero-length array.
0722: */
0723: public String[] findSecurityReferences() {
0724:
0725: synchronized (references) {
0726: String results[] = new String[references.size()];
0727: return ((String[]) references.keySet().toArray(results));
0728: }
0729:
0730: }
0731:
0732: /**
0733: * Load and initialize an instance of this servlet, if there is not already
0734: * at least one initialized instance. This can be used, for example, to
0735: * load servlets that are marked in the deployment descriptor to be loaded
0736: * at server startup time.
0737: * <p>
0738: * <b>IMPLEMENTATION NOTE</b>: Servlets whose classnames begin with
0739: * <code>org.apache.catalina.</code> (so-called "container" servlets)
0740: * are loaded by the same classloader that loaded this class, rather than
0741: * the classloader for the current web application.
0742: * This gives such classes access to Catalina internals, which are
0743: * prevented for classes loaded for web applications.
0744: *
0745: * @exception ServletException if the servlet init() method threw
0746: * an exception
0747: * @exception ServletException if some other loading problem occurs
0748: */
0749: public synchronized void load() throws ServletException {
0750: instance = loadServlet();
0751: }
0752:
0753: /**
0754: * Load and initialize an instance of this servlet, if there is not already
0755: * at least one initialized instance. This can be used, for example, to
0756: * load servlets that are marked in the deployment descriptor to be loaded
0757: * at server startup time.
0758: */
0759: public synchronized Servlet loadServlet() throws ServletException {
0760:
0761: // Nothing to do if we already have an instance or an instance pool
0762: if (!singleThreadModel && (instance != null))
0763: return instance;
0764:
0765: PrintStream out = System.out;
0766: SystemLogHandler.startCapture();
0767: Servlet servlet = null;
0768: try {
0769: // If this "servlet" is really a JSP file, get the right class.
0770: // HOLD YOUR NOSE - this is a kludge that avoids having to do special
0771: // case Catalina-specific code in Jasper - it also requires that the
0772: // servlet path be replaced by the <jsp-file> element content in
0773: // order to be completely effective
0774: String actualClass = servletClass;
0775: if ((actualClass == null) && (jspFile != null)) {
0776: Wrapper jspWrapper = (Wrapper) ((Context) getParent())
0777: .findChild(Constants.JSP_SERVLET_NAME);
0778: if (jspWrapper != null)
0779: actualClass = jspWrapper.getServletClass();
0780: }
0781:
0782: // Complain if no servlet class has been specified
0783: if (actualClass == null) {
0784: unavailable(null);
0785: throw new ServletException(sm.getString(
0786: "standardWrapper.notClass", getName()));
0787: }
0788:
0789: // Acquire an instance of the class loader to be used
0790: Loader loader = getLoader();
0791: if (loader == null) {
0792: unavailable(null);
0793: throw new ServletException(sm.getString(
0794: "standardWrapper.missingLoader", getName()));
0795: }
0796:
0797: ClassLoader classLoader = loader.getClassLoader();
0798:
0799: // Special case class loader for a container provided servlet
0800: if (isContainerProvidedServlet(actualClass)) {
0801: classLoader = this .getClass().getClassLoader();
0802: log(sm.getString("standardWrapper.containerServlet",
0803: getName()));
0804: }
0805:
0806: // Load the specified servlet class from the appropriate class loader
0807: Class classClass = null;
0808: try {
0809: if (classLoader != null) {
0810: classClass = classLoader.loadClass(actualClass);
0811: } else {
0812: classClass = Class.forName(actualClass);
0813: }
0814: } catch (ClassNotFoundException e) {
0815: unavailable(null);
0816: throw new ServletException(sm.getString(
0817: "standardWrapper.missingClass", actualClass), e);
0818: }
0819: if (classClass == null) {
0820: unavailable(null);
0821: throw new ServletException(sm.getString(
0822: "standardWrapper.missingClass", actualClass));
0823: }
0824:
0825: // Instantiate and initialize an instance of the servlet class itself
0826: try {
0827: servlet = (Servlet) classClass.newInstance();
0828: } catch (ClassCastException e) {
0829: unavailable(null);
0830: // Restore the context ClassLoader
0831: throw new ServletException(sm.getString(
0832: "standardWrapper.notServlet", actualClass), e);
0833: } catch (Throwable e) {
0834: unavailable(null);
0835: // Restore the context ClassLoader
0836: throw new ServletException(sm.getString(
0837: "standardWrapper.instantiate", actualClass), e);
0838: }
0839:
0840: // Check if loading the servlet in this web application should be
0841: // allowed
0842: if (!isServletAllowed(servlet)) {
0843: throw new SecurityException(sm.getString(
0844: "standardWrapper.privilegedServlet",
0845: actualClass));
0846: }
0847:
0848: // Special handling for ContainerServlet instances
0849: if ((servlet instanceof ContainerServlet)
0850: && isContainerProvidedServlet(actualClass)) {
0851: ((ContainerServlet) servlet).setWrapper(this );
0852: }
0853:
0854: // Call the initialization method of this servlet
0855: try {
0856: instanceSupport.fireInstanceEvent(
0857: InstanceEvent.BEFORE_INIT_EVENT, servlet);
0858: servlet.init(facade);
0859: // Invoke jspInit on JSP pages
0860: if ((loadOnStartup > 0) && (jspFile != null)) {
0861: // Invoking jspInit
0862: HttpRequestBase req = new HttpRequestBase();
0863: HttpResponseBase res = new HttpResponseBase();
0864: req.setServletPath(jspFile);
0865: req.setQueryString("jsp_precompile=true");
0866: servlet.service(req, res);
0867: }
0868: instanceSupport.fireInstanceEvent(
0869: InstanceEvent.AFTER_INIT_EVENT, servlet);
0870: } catch (UnavailableException f) {
0871: instanceSupport.fireInstanceEvent(
0872: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
0873: unavailable(f);
0874: throw f;
0875: } catch (ServletException f) {
0876: instanceSupport.fireInstanceEvent(
0877: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
0878: // If the servlet wanted to be unavailable it would have
0879: // said so, so do not call unavailable(null).
0880: throw f;
0881: } catch (Throwable f) {
0882: instanceSupport.fireInstanceEvent(
0883: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
0884: // If the servlet wanted to be unavailable it would have
0885: // said so, so do not call unavailable(null).
0886: throw new ServletException(sm.getString(
0887: "standardWrapper.initException", getName()), f);
0888: }
0889:
0890: // Register our newly initialized instance
0891: singleThreadModel = servlet instanceof SingleThreadModel;
0892: if (singleThreadModel) {
0893: if (instancePool == null)
0894: instancePool = new Stack();
0895: }
0896: fireContainerEvent("load", this );
0897: } finally {
0898: String log = SystemLogHandler.stopCapture();
0899: if (log != null && log.length() > 0) {
0900: if (getServletContext() != null) {
0901: getServletContext().log(log);
0902: } else {
0903: out.println(log);
0904: }
0905: }
0906: }
0907: return servlet;
0908:
0909: }
0910:
0911: /**
0912: * Remove the specified initialization parameter from this servlet.
0913: *
0914: * @param name Name of the initialization parameter to remove
0915: */
0916: public void removeInitParameter(String name) {
0917:
0918: synchronized (parameters) {
0919: parameters.remove(name);
0920: }
0921: fireContainerEvent("removeInitParameter", name);
0922:
0923: }
0924:
0925: /**
0926: * Remove a listener no longer interested in InstanceEvents.
0927: *
0928: * @param listener The listener to remove
0929: */
0930: public void removeInstanceListener(InstanceListener listener) {
0931:
0932: instanceSupport.removeInstanceListener(listener);
0933:
0934: }
0935:
0936: /**
0937: * Remove any security role reference for the specified role name.
0938: *
0939: * @param name Security role used within this servlet to be removed
0940: */
0941: public void removeSecurityReference(String name) {
0942:
0943: synchronized (references) {
0944: references.remove(name);
0945: }
0946: fireContainerEvent("removeSecurityReference", name);
0947:
0948: }
0949:
0950: /**
0951: * Return a String representation of this component.
0952: */
0953: public String toString() {
0954:
0955: StringBuffer sb = new StringBuffer();
0956: if (getParent() != null) {
0957: sb.append(getParent().toString());
0958: sb.append(".");
0959: }
0960: sb.append("StandardWrapper[");
0961: sb.append(getName());
0962: sb.append("]");
0963: return (sb.toString());
0964:
0965: }
0966:
0967: /**
0968: * Process an UnavailableException, marking this servlet as unavailable
0969: * for the specified amount of time.
0970: *
0971: * @param unavailable The exception that occurred, or <code>null</code>
0972: * to mark this servlet as permanently unavailable
0973: */
0974: public void unavailable(UnavailableException unavailable) {
0975: log(sm.getString("standardWrapper.unavailable", getName()));
0976: if (unavailable == null)
0977: setAvailable(Long.MAX_VALUE);
0978: else if (unavailable.isPermanent())
0979: setAvailable(Long.MAX_VALUE);
0980: else {
0981: int unavailableSeconds = unavailable
0982: .getUnavailableSeconds();
0983: if (unavailableSeconds <= 0)
0984: unavailableSeconds = 60; // Arbitrary default
0985: setAvailable(System.currentTimeMillis()
0986: + (unavailableSeconds * 1000L));
0987: }
0988:
0989: }
0990:
0991: /**
0992: * Unload all initialized instances of this servlet, after calling the
0993: * <code>destroy()</code> method for each instance. This can be used,
0994: * for example, prior to shutting down the entire servlet engine, or
0995: * prior to reloading all of the classes from the Loader associated with
0996: * our Loader's repository.
0997: *
0998: * @exception ServletException if an exception is thrown by the
0999: * destroy() method
1000: */
1001: public synchronized void unload() throws ServletException {
1002:
1003: // Nothing to do if we have never loaded the instance
1004: if (!singleThreadModel && (instance == null))
1005: return;
1006: unloading = true;
1007:
1008: // Loaf a while if the current instance is allocated
1009: // (possibly more than once if non-STM)
1010: if (countAllocated > 0) {
1011: int nRetries = 0;
1012: while (nRetries < 10) {
1013: if (nRetries == 0) {
1014: log("Waiting for " + countAllocated
1015: + " instance(s) to be deallocated");
1016: }
1017: try {
1018: Thread.sleep(50);
1019: } catch (InterruptedException e) {
1020: ;
1021: }
1022: nRetries++;
1023: }
1024: }
1025:
1026: ClassLoader oldCtxClassLoader = Thread.currentThread()
1027: .getContextClassLoader();
1028: ClassLoader classLoader = instance.getClass().getClassLoader();
1029:
1030: // Call the servlet destroy() method
1031: try {
1032: instanceSupport.fireInstanceEvent(
1033: InstanceEvent.BEFORE_DESTROY_EVENT, instance);
1034: Thread.currentThread().setContextClassLoader(classLoader);
1035: instance.destroy();
1036: instanceSupport.fireInstanceEvent(
1037: InstanceEvent.AFTER_DESTROY_EVENT, instance);
1038: } catch (Throwable t) {
1039: instanceSupport.fireInstanceEvent(
1040: InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
1041: instance = null;
1042: instancePool = null;
1043: nInstances = 0;
1044: fireContainerEvent("unload", this );
1045: unloading = false;
1046: throw new ServletException(sm.getString(
1047: "standardWrapper.destroyException", getName()), t);
1048: } finally {
1049: // restore the context ClassLoader
1050: Thread.currentThread().setContextClassLoader(
1051: oldCtxClassLoader);
1052: }
1053:
1054: // Deregister the destroyed instance
1055: instance = null;
1056:
1057: if (singleThreadModel && (instancePool != null)) {
1058: try {
1059: Thread.currentThread().setContextClassLoader(
1060: classLoader);
1061: while (!instancePool.isEmpty()) {
1062: ((Servlet) instancePool.pop()).destroy();
1063: }
1064: } catch (Throwable t) {
1065: instancePool = null;
1066: nInstances = 0;
1067: unloading = false;
1068: fireContainerEvent("unload", this );
1069: throw new ServletException(sm.getString(
1070: "standardWrapper.destroyException", getName()),
1071: t);
1072: } finally {
1073: // restore the context ClassLoader
1074: Thread.currentThread().setContextClassLoader(
1075: oldCtxClassLoader);
1076: }
1077: instancePool = null;
1078: nInstances = 0;
1079: }
1080:
1081: unloading = false;
1082: fireContainerEvent("unload", this );
1083:
1084: }
1085:
1086: // -------------------------------------------------- ServletConfig Methods
1087:
1088: /**
1089: * Return the initialization parameter value for the specified name,
1090: * if any; otherwise return <code>null</code>.
1091: *
1092: * @param name Name of the initialization parameter to retrieve
1093: */
1094: public String getInitParameter(String name) {
1095:
1096: return (findInitParameter(name));
1097:
1098: }
1099:
1100: /**
1101: * Return the set of initialization parameter names defined for this
1102: * servlet. If none are defined, an empty Enumeration is returned.
1103: */
1104: public Enumeration getInitParameterNames() {
1105:
1106: synchronized (parameters) {
1107: return (new Enumerator(parameters.keySet()));
1108: }
1109:
1110: }
1111:
1112: /**
1113: * Return the servlet context with which this servlet is associated.
1114: */
1115: public ServletContext getServletContext() {
1116:
1117: if (parent == null)
1118: return (null);
1119: else if (!(parent instanceof Context))
1120: return (null);
1121: else
1122: return (((Context) parent).getServletContext());
1123:
1124: }
1125:
1126: /**
1127: * Return the name of this servlet.
1128: */
1129: public String getServletName() {
1130:
1131: return (getName());
1132:
1133: }
1134:
1135: // -------------------------------------------------------- Package Methods
1136:
1137: // -------------------------------------------------------- Private Methods
1138:
1139: /**
1140: * Add a default Mapper implementation if none have been configured
1141: * explicitly.
1142: *
1143: * @param mapperClass Java class name of the default Mapper
1144: */
1145: protected void addDefaultMapper(String mapperClass) {
1146:
1147: ; // No need for a default Mapper on a Wrapper
1148:
1149: }
1150:
1151: /**
1152: * Return <code>true</code> if the specified class name represents a
1153: * container provided servlet class that should be loaded by the
1154: * server class loader.
1155: *
1156: * @param name Name of the class to be checked
1157: */
1158: private boolean isContainerProvidedServlet(String classname) {
1159:
1160: if (classname.startsWith("org.apache.catalina.")) {
1161: return (true);
1162: }
1163: try {
1164: Class clazz = this .getClass().getClassLoader().loadClass(
1165: classname);
1166: return (ContainerServlet.class.isAssignableFrom(clazz));
1167: } catch (Throwable t) {
1168: return (false);
1169: }
1170:
1171: }
1172:
1173: /**
1174: * Return <code>true</code> if loading this servlet is allowed.
1175: */
1176: private boolean isServletAllowed(Object servlet) {
1177:
1178: if (servlet instanceof ContainerServlet) {
1179: if (((Context) getParent()).getPrivileged()
1180: || (servlet.getClass().getName()
1181: .equals("org.apache.catalina.servlets.InvokerServlet"))) {
1182: return (true);
1183: } else {
1184: return (false);
1185: }
1186: }
1187:
1188: return (true);
1189:
1190: }
1191:
1192: /**
1193: * Log the abbreviated name of this Container for logging messages.
1194: */
1195: protected String logName() {
1196:
1197: StringBuffer sb = new StringBuffer("StandardWrapper[");
1198: if (getParent() != null)
1199: sb.append(getParent().getName());
1200: else
1201: sb.append("null");
1202: sb.append(':');
1203: sb.append(getName());
1204: sb.append(']');
1205: return (sb.toString());
1206:
1207: }
1208:
1209: // ------------------------------------------------------ Lifecycle Methods
1210:
1211: /**
1212: * Start this component, pre-loading the servlet if the load-on-startup
1213: * value is set appropriately.
1214: *
1215: * @exception LifecycleException if a fatal error occurs during startup
1216: */
1217: public void start() throws LifecycleException {
1218:
1219: // Start up this component
1220: super .start();
1221:
1222: // Load and initialize an instance of this servlet if requested
1223: // MOVED TO StandardContext START() METHOD
1224:
1225: }
1226:
1227: /**
1228: * Stop this component, gracefully shutting down the servlet if it has
1229: * been initialized.
1230: *
1231: * @exception LifecycleException if a fatal error occurs during shutdown
1232: */
1233: public void stop() throws LifecycleException {
1234:
1235: // Shut down our servlet instance (if it has been initialized)
1236: try {
1237: unload();
1238: } catch (ServletException e) {
1239: log(sm.getString("standardWrapper.unloadException",
1240: getName()), e);
1241: }
1242:
1243: // Shut down this component
1244: super.stop();
1245:
1246: }
1247:
1248: }
|