0001: /*
0002: * Copyright 1999-2002,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.catalina.core;
0018:
0019: import java.io.PrintStream;
0020: import java.util.ArrayList;
0021: import java.util.Enumeration;
0022: import java.util.HashMap;
0023: import java.util.Stack;
0024: import java.security.AccessController;
0025: import java.security.PrivilegedActionException;
0026: import java.security.PrivilegedExceptionAction;
0027: import javax.servlet.Servlet;
0028: import javax.servlet.ServletConfig;
0029: import javax.servlet.ServletContext;
0030: import javax.servlet.ServletException;
0031: import javax.servlet.ServletRequest;
0032: import javax.servlet.ServletResponse;
0033: import javax.servlet.SingleThreadModel;
0034: import javax.servlet.UnavailableException;
0035: import javax.management.Notification;
0036: import javax.management.NotificationBroadcasterSupport;
0037: import javax.management.ObjectName;
0038:
0039: import org.apache.catalina.Container;
0040: import org.apache.catalina.ContainerServlet;
0041: import org.apache.catalina.Context;
0042: import org.apache.catalina.InstanceEvent;
0043: import org.apache.catalina.InstanceListener;
0044: import org.apache.catalina.LifecycleException;
0045: import org.apache.catalina.Loader;
0046: import org.apache.catalina.Wrapper;
0047: import org.apache.catalina.security.SecurityUtil;
0048: import org.apache.catalina.util.Enumerator;
0049: import org.apache.catalina.util.InstanceSupport;
0050: import org.apache.tomcat.util.log.SystemLogHandler;
0051: import org.apache.commons.modeler.Registry;
0052:
0053: /**
0054: * Standard implementation of the <b>Wrapper</b> interface that represents
0055: * an individual servlet definition. No child Containers are allowed, and
0056: * the parent Container must be a Context.
0057: *
0058: * @author Craig R. McClanahan
0059: * @author Remy Maucherat
0060: * @version $Revision: 1.43 $ $Date: 2004/06/07 12:02:34 $
0061: */
0062: public class StandardWrapper extends ContainerBase implements
0063: ServletConfig, Wrapper {
0064:
0065: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
0066: .getLog(StandardWrapper.class);
0067:
0068: // ----------------------------------------------------------- Constructors
0069:
0070: /**
0071: * Create a new StandardWrapper component with the default basic Valve.
0072: */
0073: public StandardWrapper() {
0074:
0075: super ();
0076: swValve = new StandardWrapperValve();
0077: pipeline.setBasic(swValve);
0078: broadcaster = new NotificationBroadcasterSupport();
0079:
0080: }
0081:
0082: // ----------------------------------------------------- Instance Variables
0083:
0084: /**
0085: * The date and time at which this servlet will become available (in
0086: * milliseconds since the epoch), or zero if the servlet is available.
0087: * If this value equals Long.MAX_VALUE, the unavailability of this
0088: * servlet is considered permanent.
0089: */
0090: private long available = 0L;
0091:
0092: /**
0093: * The broadcaster that sends j2ee notifications.
0094: */
0095: private NotificationBroadcasterSupport broadcaster = null;
0096:
0097: /**
0098: * The count of allocations that are currently active (even if they
0099: * are for the same instance, as will be true on a non-STM servlet).
0100: */
0101: private int countAllocated = 0;
0102:
0103: /**
0104: * The debugging detail level for this component.
0105: */
0106: private int debug = 0;
0107:
0108: /**
0109: * The facade associated with this wrapper.
0110: */
0111: private StandardWrapperFacade facade = new StandardWrapperFacade(
0112: this );
0113:
0114: /**
0115: * The descriptive information string for this implementation.
0116: */
0117: private static final String info = "org.apache.catalina.core.StandardWrapper/1.0";
0118:
0119: /**
0120: * The (single) initialized instance of this servlet.
0121: */
0122: private Servlet instance = null;
0123:
0124: /**
0125: * The support object for our instance listeners.
0126: */
0127: private InstanceSupport instanceSupport = new InstanceSupport(this );
0128:
0129: /**
0130: * The context-relative URI of the JSP file for this servlet.
0131: */
0132: private String jspFile = null;
0133:
0134: /**
0135: * The load-on-startup order value (negative value means load on
0136: * first call) for this servlet.
0137: */
0138: private int loadOnStartup = -1;
0139:
0140: /**
0141: * Mappings associated with the wrapper.
0142: */
0143: private ArrayList mappings = new ArrayList();
0144:
0145: /**
0146: * The initialization parameters for this servlet, keyed by
0147: * parameter name.
0148: */
0149: private HashMap parameters = new HashMap();
0150:
0151: /**
0152: * The security role references for this servlet, keyed by role name
0153: * used in the servlet. The corresponding value is the role name of
0154: * the web application itself.
0155: */
0156: private HashMap references = new HashMap();
0157:
0158: /**
0159: * The run-as identity for this servlet.
0160: */
0161: private String runAs = null;
0162:
0163: /**
0164: * The notification sequence number.
0165: */
0166: private long sequenceNumber = 0;
0167:
0168: /**
0169: * The fully qualified servlet class name for this servlet.
0170: */
0171: private String servletClass = null;
0172:
0173: /**
0174: * Does this servlet implement the SingleThreadModel interface?
0175: */
0176: private boolean singleThreadModel = false;
0177:
0178: /**
0179: * Are we unloading our servlet instance at the moment?
0180: */
0181: private boolean unloading = false;
0182:
0183: /**
0184: * Maximum number of STM instances.
0185: */
0186: private int maxInstances = 20;
0187:
0188: /**
0189: * Number of instances currently loaded for a STM servlet.
0190: */
0191: private int nInstances = 0;
0192:
0193: /**
0194: * Stack containing the STM instances.
0195: */
0196: private Stack instancePool = null;
0197:
0198: /**
0199: * True if this StandardWrapper is for the JspServlet
0200: */
0201: private boolean isJspServlet;
0202:
0203: /**
0204: * The ObjectName of the JSP monitoring mbean
0205: */
0206: private ObjectName jspMonitorON;
0207:
0208: /**
0209: * Should we swallow System.out
0210: */
0211: private boolean swallowOutput = false;
0212:
0213: // To support jmx attributes
0214: private StandardWrapperValve swValve;
0215: private long loadTime = 0;
0216: private int classLoadTime = 0;
0217:
0218: // ------------------------------------------------------------- Properties
0219:
0220: /**
0221: * Return the available date/time for this servlet, in milliseconds since
0222: * the epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
0223: * that unavailability is permanent and any request for this servlet will return
0224: * an SC_NOT_FOUND error. If this date/time is in the future, any request for
0225: * this servlet will return an SC_SERVICE_UNAVAILABLE error. If it is zero,
0226: * the servlet is currently available.
0227: */
0228: public long getAvailable() {
0229:
0230: return (this .available);
0231:
0232: }
0233:
0234: /**
0235: * Set the available date/time for this servlet, in milliseconds since the
0236: * epoch. If this date/time is Long.MAX_VALUE, it is considered to mean
0237: * that unavailability is permanent and any request for this servlet will return
0238: * an SC_NOT_FOUND error. If this date/time is in the future, any request for
0239: * this servlet will return an SC_SERVICE_UNAVAILABLE error.
0240: *
0241: * @param available The new available date/time
0242: */
0243: public void setAvailable(long available) {
0244:
0245: long oldAvailable = this .available;
0246: if (available > System.currentTimeMillis())
0247: this .available = available;
0248: else
0249: this .available = 0L;
0250: support.firePropertyChange("available", new Long(oldAvailable),
0251: new Long(this .available));
0252:
0253: }
0254:
0255: /**
0256: * Return the number of active allocations of this servlet, even if they
0257: * are all for the same instance (as will be true for servlets that do
0258: * not implement <code>SingleThreadModel</code>.
0259: */
0260: public int getCountAllocated() {
0261:
0262: return (this .countAllocated);
0263:
0264: }
0265:
0266: /**
0267: * Return the debugging detail level for this component.
0268: */
0269: public int getDebug() {
0270:
0271: return (this .debug);
0272:
0273: }
0274:
0275: /**
0276: * Set the debugging detail level for this component.
0277: *
0278: * @param debug The new debugging detail level
0279: */
0280: public void setDebug(int debug) {
0281:
0282: int oldDebug = this .debug;
0283: this .debug = debug;
0284: support.firePropertyChange("debug", new Integer(oldDebug),
0285: new Long(this .debug));
0286:
0287: }
0288:
0289: public String getEngineName() {
0290: return ((StandardContext) getParent()).getEngineName();
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: if (isJspServlet && loadOnStartup < 0) {
0346: /*
0347: * JspServlet must always be preloaded, because its instance is
0348: * used during registerJMX (when registering the JSP
0349: * monitoring mbean)
0350: */
0351: return Integer.MAX_VALUE;
0352: } else {
0353: return (this .loadOnStartup);
0354: }
0355: }
0356:
0357: /**
0358: * Set the load-on-startup order value (negative value means
0359: * load on first call).
0360: *
0361: * @param value New load-on-startup value
0362: */
0363: public void setLoadOnStartup(int value) {
0364:
0365: int oldLoadOnStartup = this .loadOnStartup;
0366: this .loadOnStartup = value;
0367: support.firePropertyChange("loadOnStartup", new Integer(
0368: oldLoadOnStartup), new Integer(this .loadOnStartup));
0369:
0370: }
0371:
0372: /**
0373: * Set the load-on-startup order value from a (possibly null) string.
0374: * Per the specification, any missing or non-numeric value is converted
0375: * to a zero, so that this servlet will still be loaded at startup
0376: * time, but in an arbitrary order.
0377: *
0378: * @param value New load-on-startup value
0379: */
0380: public void setLoadOnStartupString(String value) {
0381:
0382: try {
0383: setLoadOnStartup(Integer.parseInt(value));
0384: } catch (NumberFormatException e) {
0385: setLoadOnStartup(0);
0386: }
0387: }
0388:
0389: public String getLoadOnStartupString() {
0390: return Integer.toString(getLoadOnStartup());
0391: }
0392:
0393: /**
0394: * Return maximum number of instances that will be allocated when a single
0395: * thread model servlet is used.
0396: */
0397: public int getMaxInstances() {
0398:
0399: return (this .maxInstances);
0400:
0401: }
0402:
0403: /**
0404: * Set the maximum number of instances that will be allocated when a single
0405: * thread model servlet is used.
0406: *
0407: * @param maxInstances New value of maxInstances
0408: */
0409: public void setMaxInstances(int maxInstances) {
0410:
0411: int oldMaxInstances = this .maxInstances;
0412: this .maxInstances = maxInstances;
0413: support.firePropertyChange("maxInstances", oldMaxInstances,
0414: this .maxInstances);
0415:
0416: }
0417:
0418: /**
0419: * Set the parent Container of this Wrapper, but only if it is a Context.
0420: *
0421: * @param container Proposed parent Container
0422: */
0423: public void setParent(Container container) {
0424:
0425: if ((container != null) && !(container instanceof Context))
0426: throw new IllegalArgumentException(sm
0427: .getString("standardWrapper.notContext"));
0428: if (container instanceof StandardContext) {
0429: swallowOutput = ((StandardContext) container)
0430: .getSwallowOutput();
0431: }
0432: super .setParent(container);
0433:
0434: }
0435:
0436: /**
0437: * Return the run-as identity for this servlet.
0438: */
0439: public String getRunAs() {
0440:
0441: return (this .runAs);
0442:
0443: }
0444:
0445: /**
0446: * Set the run-as identity for this servlet.
0447: *
0448: * @param runAs New run-as identity value
0449: */
0450: public void setRunAs(String runAs) {
0451:
0452: String oldRunAs = this .runAs;
0453: this .runAs = runAs;
0454: support.firePropertyChange("runAs", oldRunAs, this .runAs);
0455:
0456: }
0457:
0458: /**
0459: * Return the fully qualified servlet class name for this servlet.
0460: */
0461: public String getServletClass() {
0462:
0463: return (this .servletClass);
0464:
0465: }
0466:
0467: /**
0468: * Set the fully qualified servlet class name for this servlet.
0469: *
0470: * @param servletClass Servlet class name
0471: */
0472: public void setServletClass(String servletClass) {
0473:
0474: String oldServletClass = this .servletClass;
0475: this .servletClass = servletClass;
0476: support.firePropertyChange("servletClass", oldServletClass,
0477: this .servletClass);
0478: if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) {
0479: isJspServlet = true;
0480: }
0481: }
0482:
0483: /**
0484: * Set the name of this servlet. This is an alias for the normal
0485: * <code>Container.setName()</code> method, and complements the
0486: * <code>getServletName()</code> method required by the
0487: * <code>ServletConfig</code> interface.
0488: *
0489: * @param name The new name of this servlet
0490: */
0491: public void setServletName(String name) {
0492:
0493: setName(name);
0494:
0495: }
0496:
0497: /**
0498: * Return <code>true</code> if the servlet class represented by this
0499: * component implements the <code>SingleThreadModel</code> interface.
0500: */
0501: public boolean isSingleThreadModel() {
0502:
0503: try {
0504: loadServlet();
0505: } catch (Throwable t) {
0506: ;
0507: }
0508: return (singleThreadModel);
0509:
0510: }
0511:
0512: /**
0513: * Is this servlet currently unavailable?
0514: */
0515: public boolean isUnavailable() {
0516:
0517: if (available == 0L)
0518: return (false);
0519: else if (available <= System.currentTimeMillis()) {
0520: available = 0L;
0521: return (false);
0522: } else
0523: return (true);
0524:
0525: }
0526:
0527: // --------------------------------------------------------- Public Methods
0528:
0529: /**
0530: * Refuse to add a child Container, because Wrappers are the lowest level
0531: * of the Container hierarchy.
0532: *
0533: * @param child Child container to be added
0534: */
0535: public void addChild(Container child) {
0536:
0537: throw new IllegalStateException(sm
0538: .getString("standardWrapper.notChild"));
0539:
0540: }
0541:
0542: /**
0543: * Add a new servlet initialization parameter for this servlet.
0544: *
0545: * @param name Name of this initialization parameter to add
0546: * @param value Value of this initialization parameter to add
0547: */
0548: public void addInitParameter(String name, String value) {
0549:
0550: synchronized (parameters) {
0551: parameters.put(name, value);
0552: }
0553: fireContainerEvent("addInitParameter", name);
0554:
0555: }
0556:
0557: /**
0558: * Add a new listener interested in InstanceEvents.
0559: *
0560: * @param listener The new listener
0561: */
0562: public void addInstanceListener(InstanceListener listener) {
0563:
0564: instanceSupport.addInstanceListener(listener);
0565:
0566: }
0567:
0568: /**
0569: * Add a mapping associated with the Wrapper.
0570: *
0571: * @param mapping The new wrapper mapping
0572: */
0573: public void addMapping(String mapping) {
0574:
0575: synchronized (mappings) {
0576: mappings.add(mapping);
0577: }
0578: fireContainerEvent("addMapping", mapping);
0579:
0580: }
0581:
0582: /**
0583: * Add a new security role reference record to the set of records for
0584: * this servlet.
0585: *
0586: * @param name Role name used within this servlet
0587: * @param link Role name used within the web application
0588: */
0589: public void addSecurityReference(String name, String link) {
0590:
0591: synchronized (references) {
0592: references.put(name, link);
0593: }
0594: fireContainerEvent("addSecurityReference", name);
0595:
0596: }
0597:
0598: /**
0599: * Allocate an initialized instance of this Servlet that is ready to have
0600: * its <code>service()</code> method called. If the servlet class does
0601: * not implement <code>SingleThreadModel</code>, the (only) initialized
0602: * instance may be returned immediately. If the servlet class implements
0603: * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
0604: * that this instance is not allocated again until it is deallocated by a
0605: * call to <code>deallocate()</code>.
0606: *
0607: * @exception ServletException if the servlet init() method threw
0608: * an exception
0609: * @exception ServletException if a loading error occurs
0610: */
0611: public Servlet allocate() throws ServletException {
0612:
0613: // If we are currently unloading this servlet, throw an exception
0614: if (unloading)
0615: throw new ServletException(sm.getString(
0616: "standardWrapper.unloading", getName()));
0617:
0618: // If not SingleThreadedModel, return the same instance every time
0619: if (!singleThreadModel) {
0620:
0621: // Load and initialize our instance if necessary
0622: if (instance == null) {
0623: synchronized (this ) {
0624: if (instance == null) {
0625: try {
0626: if (log.isDebugEnabled())
0627: log
0628: .debug("Allocating non-STM instance");
0629:
0630: instance = loadServlet();
0631: } catch (ServletException e) {
0632: throw e;
0633: } catch (Throwable e) {
0634: throw new ServletException(
0635: sm
0636: .getString("standardWrapper.allocate"),
0637: e);
0638: }
0639: }
0640: }
0641: }
0642:
0643: if (!singleThreadModel) {
0644: if (log.isTraceEnabled())
0645: log.trace(" Returning non-STM instance");
0646: countAllocated++;
0647: return (instance);
0648: }
0649:
0650: }
0651:
0652: synchronized (instancePool) {
0653:
0654: while (countAllocated >= nInstances) {
0655: // Allocate a new instance if possible, or else wait
0656: if (nInstances < maxInstances) {
0657: try {
0658: instancePool.push(loadServlet());
0659: nInstances++;
0660: } catch (ServletException e) {
0661: throw e;
0662: } catch (Throwable e) {
0663: throw new ServletException(sm
0664: .getString("standardWrapper.allocate"),
0665: e);
0666: }
0667: } else {
0668: try {
0669: instancePool.wait();
0670: } catch (InterruptedException e) {
0671: ;
0672: }
0673: }
0674: }
0675: if (log.isTraceEnabled())
0676: log.trace(" Returning allocated STM instance");
0677: countAllocated++;
0678: return (Servlet) instancePool.pop();
0679:
0680: }
0681:
0682: }
0683:
0684: /**
0685: * Return this previously allocated servlet to the pool of available
0686: * instances. If this servlet class does not implement SingleThreadModel,
0687: * no action is actually required.
0688: *
0689: * @param servlet The servlet to be returned
0690: *
0691: * @exception ServletException if a deallocation error occurs
0692: */
0693: public void deallocate(Servlet servlet) throws ServletException {
0694:
0695: // If not SingleThreadModel, no action is required
0696: if (!singleThreadModel) {
0697: countAllocated--;
0698: return;
0699: }
0700:
0701: // Unlock and free this instance
0702: synchronized (instancePool) {
0703: countAllocated--;
0704: instancePool.push(servlet);
0705: instancePool.notify();
0706: }
0707:
0708: }
0709:
0710: /**
0711: * Return the value for the specified initialization parameter name,
0712: * if any; otherwise return <code>null</code>.
0713: *
0714: * @param name Name of the requested initialization parameter
0715: */
0716: public String findInitParameter(String name) {
0717:
0718: synchronized (parameters) {
0719: return ((String) parameters.get(name));
0720: }
0721:
0722: }
0723:
0724: /**
0725: * Return the names of all defined initialization parameters for this
0726: * servlet.
0727: */
0728: public String[] findInitParameters() {
0729:
0730: synchronized (parameters) {
0731: String results[] = new String[parameters.size()];
0732: return ((String[]) parameters.keySet().toArray(results));
0733: }
0734:
0735: }
0736:
0737: /**
0738: * Return the mappings associated with this wrapper.
0739: */
0740: public String[] findMappings() {
0741:
0742: synchronized (mappings) {
0743: return (String[]) mappings.toArray(new String[mappings
0744: .size()]);
0745: }
0746:
0747: }
0748:
0749: /**
0750: * Return the security role link for the specified security role
0751: * reference name, if any; otherwise return <code>null</code>.
0752: *
0753: * @param name Security role reference used within this servlet
0754: */
0755: public String findSecurityReference(String name) {
0756:
0757: synchronized (references) {
0758: return ((String) references.get(name));
0759: }
0760:
0761: }
0762:
0763: /**
0764: * Return the set of security role reference names associated with
0765: * this servlet, if any; otherwise return a zero-length array.
0766: */
0767: public String[] findSecurityReferences() {
0768:
0769: synchronized (references) {
0770: String results[] = new String[references.size()];
0771: return ((String[]) references.keySet().toArray(results));
0772: }
0773:
0774: }
0775:
0776: /**
0777: * FIXME: Fooling introspection ...
0778: */
0779: public Wrapper findMappingObject() {
0780: return (Wrapper) getMappingObject();
0781: }
0782:
0783: /**
0784: * Load and initialize an instance of this servlet, if there is not already
0785: * at least one initialized instance. This can be used, for example, to
0786: * load servlets that are marked in the deployment descriptor to be loaded
0787: * at server startup time.
0788: * <p>
0789: * <b>IMPLEMENTATION NOTE</b>: Servlets whose classnames begin with
0790: * <code>org.apache.catalina.</code> (so-called "container" servlets)
0791: * are loaded by the same classloader that loaded this class, rather than
0792: * the classloader for the current web application.
0793: * This gives such classes access to Catalina internals, which are
0794: * prevented for classes loaded for web applications.
0795: *
0796: * @exception ServletException if the servlet init() method threw
0797: * an exception
0798: * @exception ServletException if some other loading problem occurs
0799: */
0800: public synchronized void load() throws ServletException {
0801: instance = loadServlet();
0802: }
0803:
0804: /**
0805: * Load and initialize an instance of this servlet, if there is not already
0806: * at least one initialized instance. This can be used, for example, to
0807: * load servlets that are marked in the deployment descriptor to be loaded
0808: * at server startup time.
0809: */
0810: public synchronized Servlet loadServlet() throws ServletException {
0811:
0812: // Nothing to do if we already have an instance or an instance pool
0813: if (!singleThreadModel && (instance != null))
0814: return instance;
0815:
0816: PrintStream out = System.out;
0817: if (swallowOutput) {
0818: SystemLogHandler.startCapture();
0819: }
0820:
0821: Servlet servlet;
0822: try {
0823: long t1 = System.currentTimeMillis();
0824: // If this "servlet" is really a JSP file, get the right class.
0825: // HOLD YOUR NOSE - this is a kludge that avoids having to do special
0826: // case Catalina-specific code in Jasper - it also requires that the
0827: // servlet path be replaced by the <jsp-file> element content in
0828: // order to be completely effective
0829: String actualClass = servletClass;
0830: if ((actualClass == null) && (jspFile != null)) {
0831: Wrapper jspWrapper = (Wrapper) ((Context) getParent())
0832: .findChild(Constants.JSP_SERVLET_NAME);
0833: if (jspWrapper != null) {
0834: actualClass = jspWrapper.getServletClass();
0835: // Merge init parameters
0836: String paramNames[] = jspWrapper
0837: .findInitParameters();
0838: for (int i = 0; i < paramNames.length; i++) {
0839: if (parameters.get(paramNames[i]) == null) {
0840: parameters.put(paramNames[i], jspWrapper
0841: .findInitParameter(paramNames[i]));
0842: }
0843: }
0844: }
0845: }
0846:
0847: // Complain if no servlet class has been specified
0848: if (actualClass == null) {
0849: unavailable(null);
0850: throw new ServletException(sm.getString(
0851: "standardWrapper.notClass", getName()));
0852: }
0853:
0854: // Acquire an instance of the class loader to be used
0855: Loader loader = getLoader();
0856: if (loader == null) {
0857: unavailable(null);
0858: throw new ServletException(sm.getString(
0859: "standardWrapper.missingLoader", getName()));
0860: }
0861:
0862: ClassLoader classLoader = loader.getClassLoader();
0863:
0864: // Special case class loader for a container provided servlet
0865: //
0866: if (isContainerProvidedServlet(actualClass)
0867: && !((Context) getParent()).getPrivileged()) {
0868: // If it is a priviledged context - using its own
0869: // class loader will work, since it's a child of the container
0870: // loader
0871: classLoader = this .getClass().getClassLoader();
0872: }
0873:
0874: // Load the specified servlet class from the appropriate class loader
0875: Class classClass = null;
0876: try {
0877: if (System.getSecurityManager() != null) {
0878: final ClassLoader fclassLoader = classLoader;
0879: final String factualClass = actualClass;
0880: try {
0881: classClass = (Class) AccessController
0882: .doPrivileged(new PrivilegedExceptionAction() {
0883: public Object run()
0884: throws Exception {
0885: if (fclassLoader != null) {
0886: return fclassLoader
0887: .loadClass(factualClass);
0888: } else {
0889: return Class
0890: .forName(factualClass);
0891: }
0892: }
0893: });
0894: } catch (PrivilegedActionException pax) {
0895: Exception ex = pax.getException();
0896: if (ex instanceof ClassNotFoundException) {
0897: throw (ClassNotFoundException) ex;
0898: } else {
0899: getServletContext().log(
0900: "Error loading " + fclassLoader
0901: + " " + factualClass, ex);
0902: }
0903: }
0904: } else {
0905: if (classLoader != null) {
0906: classClass = classLoader.loadClass(actualClass);
0907: } else {
0908: classClass = Class.forName(actualClass);
0909: }
0910: }
0911: } catch (ClassNotFoundException e) {
0912: unavailable(null);
0913:
0914: getServletContext().log(
0915: "Error loading " + classLoader + " "
0916: + actualClass, e);
0917: throw new ServletException(sm.getString(
0918: "standardWrapper.missingClass", actualClass), e);
0919: }
0920:
0921: if (classClass == null) {
0922: unavailable(null);
0923: throw new ServletException(sm.getString(
0924: "standardWrapper.missingClass", actualClass));
0925: }
0926:
0927: // Instantiate and initialize an instance of the servlet class itself
0928: try {
0929: servlet = (Servlet) classClass.newInstance();
0930: } catch (ClassCastException e) {
0931: unavailable(null);
0932: // Restore the context ClassLoader
0933: throw new ServletException(sm.getString(
0934: "standardWrapper.notServlet", actualClass), e);
0935: } catch (Throwable e) {
0936: unavailable(null);
0937: // Restore the context ClassLoader
0938: throw new ServletException(sm.getString(
0939: "standardWrapper.instantiate", actualClass), e);
0940: }
0941:
0942: // Check if loading the servlet in this web application should be
0943: // allowed
0944: if (!isServletAllowed(servlet)) {
0945: throw new SecurityException(sm.getString(
0946: "standardWrapper.privilegedServlet",
0947: actualClass));
0948: }
0949:
0950: // Special handling for ContainerServlet instances
0951: if ((servlet instanceof ContainerServlet)
0952: && (isContainerProvidedServlet(actualClass) || ((Context) getParent())
0953: .getPrivileged())) {
0954: ((ContainerServlet) servlet).setWrapper(this );
0955: }
0956:
0957: classLoadTime = (int) (System.currentTimeMillis() - t1);
0958: // Call the initialization method of this servlet
0959: try {
0960: instanceSupport.fireInstanceEvent(
0961: InstanceEvent.BEFORE_INIT_EVENT, servlet);
0962:
0963: if (System.getSecurityManager() != null) {
0964: Class[] classType = new Class[] { ServletConfig.class };
0965: Object[] args = new Object[] { ((ServletConfig) facade) };
0966: SecurityUtil.doAsPrivilege("init", servlet,
0967: classType, args);
0968: } else {
0969: servlet.init(facade);
0970: }
0971:
0972: // Invoke jspInit on JSP pages
0973: if ((loadOnStartup >= 0) && (jspFile != null)) {
0974: // Invoking jspInit
0975: DummyRequest req = new DummyRequest();
0976: req.setServletPath(jspFile);
0977: req.setQueryString("jsp_precompile=true");
0978: DummyResponse res = new DummyResponse();
0979:
0980: if (System.getSecurityManager() != null) {
0981: Class[] classType = new Class[] {
0982: ServletRequest.class,
0983: ServletResponse.class };
0984: Object[] args = new Object[] { req, res };
0985: SecurityUtil.doAsPrivilege("service", servlet,
0986: classType, args);
0987: } else {
0988: servlet.service(req, res);
0989: }
0990: }
0991: instanceSupport.fireInstanceEvent(
0992: InstanceEvent.AFTER_INIT_EVENT, servlet);
0993: } catch (UnavailableException f) {
0994: instanceSupport.fireInstanceEvent(
0995: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
0996: unavailable(f);
0997: throw f;
0998: } catch (ServletException f) {
0999: instanceSupport.fireInstanceEvent(
1000: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
1001: // If the servlet wanted to be unavailable it would have
1002: // said so, so do not call unavailable(null).
1003: throw f;
1004: } catch (Throwable f) {
1005: getServletContext().log("StandardWrapper.Throwable", f);
1006: instanceSupport.fireInstanceEvent(
1007: InstanceEvent.AFTER_INIT_EVENT, servlet, f);
1008: // If the servlet wanted to be unavailable it would have
1009: // said so, so do not call unavailable(null).
1010: throw new ServletException(sm.getString(
1011: "standardWrapper.initException", getName()), f);
1012: }
1013:
1014: // Register our newly initialized instance
1015: singleThreadModel = servlet instanceof SingleThreadModel;
1016: if (singleThreadModel) {
1017: if (instancePool == null)
1018: instancePool = new Stack();
1019: }
1020: fireContainerEvent("load", this );
1021:
1022: loadTime = System.currentTimeMillis() - t1;
1023: } finally {
1024: if (swallowOutput) {
1025: String log = SystemLogHandler.stopCapture();
1026: if (log != null && log.length() > 0) {
1027: if (getServletContext() != null) {
1028: getServletContext().log(log);
1029: } else {
1030: out.println(log);
1031: }
1032: }
1033: }
1034: }
1035: return servlet;
1036:
1037: }
1038:
1039: /**
1040: * Remove the specified initialization parameter from this servlet.
1041: *
1042: * @param name Name of the initialization parameter to remove
1043: */
1044: public void removeInitParameter(String name) {
1045:
1046: synchronized (parameters) {
1047: parameters.remove(name);
1048: }
1049: fireContainerEvent("removeInitParameter", name);
1050:
1051: }
1052:
1053: /**
1054: * Remove a listener no longer interested in InstanceEvents.
1055: *
1056: * @param listener The listener to remove
1057: */
1058: public void removeInstanceListener(InstanceListener listener) {
1059:
1060: instanceSupport.removeInstanceListener(listener);
1061:
1062: }
1063:
1064: /**
1065: * Remove a mapping associated with the wrapper.
1066: *
1067: * @param mapping The pattern to remove
1068: */
1069: public void removeMapping(String mapping) {
1070:
1071: synchronized (mappings) {
1072: mappings.remove(mapping);
1073: }
1074: fireContainerEvent("removeMapping", mapping);
1075:
1076: }
1077:
1078: /**
1079: * Remove any security role reference for the specified role name.
1080: *
1081: * @param name Security role used within this servlet to be removed
1082: */
1083: public void removeSecurityReference(String name) {
1084:
1085: synchronized (references) {
1086: references.remove(name);
1087: }
1088: fireContainerEvent("removeSecurityReference", name);
1089:
1090: }
1091:
1092: /**
1093: * Return a String representation of this component.
1094: */
1095: public String toString() {
1096:
1097: StringBuffer sb = new StringBuffer();
1098: if (getParent() != null) {
1099: sb.append(getParent().toString());
1100: sb.append(".");
1101: }
1102: sb.append("StandardWrapper[");
1103: sb.append(getName());
1104: sb.append("]");
1105: return (sb.toString());
1106:
1107: }
1108:
1109: /**
1110: * Process an UnavailableException, marking this servlet as unavailable
1111: * for the specified amount of time.
1112: *
1113: * @param unavailable The exception that occurred, or <code>null</code>
1114: * to mark this servlet as permanently unavailable
1115: */
1116: public void unavailable(UnavailableException unavailable) {
1117: getServletContext().log(
1118: sm.getString("standardWrapper.unavailable", getName()));
1119: if (unavailable == null)
1120: setAvailable(Long.MAX_VALUE);
1121: else if (unavailable.isPermanent())
1122: setAvailable(Long.MAX_VALUE);
1123: else {
1124: int unavailableSeconds = unavailable
1125: .getUnavailableSeconds();
1126: if (unavailableSeconds <= 0)
1127: unavailableSeconds = 60; // Arbitrary default
1128: setAvailable(System.currentTimeMillis()
1129: + (unavailableSeconds * 1000L));
1130: }
1131:
1132: }
1133:
1134: /**
1135: * Unload all initialized instances of this servlet, after calling the
1136: * <code>destroy()</code> method for each instance. This can be used,
1137: * for example, prior to shutting down the entire servlet engine, or
1138: * prior to reloading all of the classes from the Loader associated with
1139: * our Loader's repository.
1140: *
1141: * @exception ServletException if an exception is thrown by the
1142: * destroy() method
1143: */
1144: public synchronized void unload() throws ServletException {
1145:
1146: // Nothing to do if we have never loaded the instance
1147: if (!singleThreadModel && (instance == null))
1148: return;
1149: unloading = true;
1150:
1151: // Loaf a while if the current instance is allocated
1152: // (possibly more than once if non-STM)
1153: if (countAllocated > 0) {
1154: int nRetries = 0;
1155: while ((nRetries < 21) && (countAllocated > 0)) {
1156: if ((nRetries % 10) == 0) {
1157: log.info(sm.getString("standardWrapper.waiting",
1158: new Integer(countAllocated)));
1159: }
1160: try {
1161: Thread.sleep(100);
1162: } catch (InterruptedException e) {
1163: ;
1164: }
1165: nRetries++;
1166: }
1167: }
1168:
1169: ClassLoader oldCtxClassLoader = Thread.currentThread()
1170: .getContextClassLoader();
1171: ClassLoader classLoader = instance.getClass().getClassLoader();
1172:
1173: PrintStream out = System.out;
1174: if (swallowOutput) {
1175: SystemLogHandler.startCapture();
1176: }
1177:
1178: // Call the servlet destroy() method
1179: try {
1180: instanceSupport.fireInstanceEvent(
1181: InstanceEvent.BEFORE_DESTROY_EVENT, instance);
1182:
1183: Thread.currentThread().setContextClassLoader(classLoader);
1184: if (System.getSecurityManager() != null) {
1185: SecurityUtil.doAsPrivilege("destroy", instance);
1186: SecurityUtil.remove(instance);
1187: } else {
1188: instance.destroy();
1189: }
1190:
1191: instanceSupport.fireInstanceEvent(
1192: InstanceEvent.AFTER_DESTROY_EVENT, instance);
1193: } catch (Throwable t) {
1194: instanceSupport.fireInstanceEvent(
1195: InstanceEvent.AFTER_DESTROY_EVENT, instance, t);
1196: instance = null;
1197: instancePool = null;
1198: nInstances = 0;
1199: fireContainerEvent("unload", this );
1200: unloading = false;
1201: throw new ServletException(sm.getString(
1202: "standardWrapper.destroyException", getName()), t);
1203: } finally {
1204: // restore the context ClassLoader
1205: Thread.currentThread().setContextClassLoader(
1206: oldCtxClassLoader);
1207: // Write captured output
1208: if (swallowOutput) {
1209: String log = SystemLogHandler.stopCapture();
1210: if (log != null && log.length() > 0) {
1211: if (getServletContext() != null) {
1212: getServletContext().log(log);
1213: } else {
1214: out.println(log);
1215: }
1216: }
1217: }
1218: }
1219:
1220: // Deregister the destroyed instance
1221: instance = null;
1222:
1223: if (singleThreadModel && (instancePool != null)) {
1224: try {
1225: Thread.currentThread().setContextClassLoader(
1226: classLoader);
1227: while (!instancePool.isEmpty()) {
1228: if (System.getSecurityManager() != null) {
1229: SecurityUtil.doAsPrivilege("destroy",
1230: ((Servlet) instancePool.pop()));
1231: SecurityUtil.remove(instance);
1232: } else {
1233: ((Servlet) instancePool.pop()).destroy();
1234: }
1235: }
1236: } catch (Throwable t) {
1237: instancePool = null;
1238: nInstances = 0;
1239: unloading = false;
1240: fireContainerEvent("unload", this );
1241: throw new ServletException(sm.getString(
1242: "standardWrapper.destroyException", getName()),
1243: t);
1244: } finally {
1245: // restore the context ClassLoader
1246: Thread.currentThread().setContextClassLoader(
1247: oldCtxClassLoader);
1248: }
1249: instancePool = null;
1250: nInstances = 0;
1251: }
1252:
1253: singleThreadModel = false;
1254:
1255: unloading = false;
1256: fireContainerEvent("unload", this );
1257:
1258: }
1259:
1260: // -------------------------------------------------- ServletConfig Methods
1261:
1262: /**
1263: * Return the initialization parameter value for the specified name,
1264: * if any; otherwise return <code>null</code>.
1265: *
1266: * @param name Name of the initialization parameter to retrieve
1267: */
1268: public String getInitParameter(String name) {
1269:
1270: return (findInitParameter(name));
1271:
1272: }
1273:
1274: /**
1275: * Return the set of initialization parameter names defined for this
1276: * servlet. If none are defined, an empty Enumeration is returned.
1277: */
1278: public Enumeration getInitParameterNames() {
1279:
1280: synchronized (parameters) {
1281: return (new Enumerator(parameters.keySet()));
1282: }
1283:
1284: }
1285:
1286: /**
1287: * Return the servlet context with which this servlet is associated.
1288: */
1289: public ServletContext getServletContext() {
1290:
1291: if (parent == null)
1292: return (null);
1293: else if (!(parent instanceof Context))
1294: return (null);
1295: else
1296: return (((Context) parent).getServletContext());
1297:
1298: }
1299:
1300: /**
1301: * Return the name of this servlet.
1302: */
1303: public String getServletName() {
1304:
1305: return (getName());
1306:
1307: }
1308:
1309: public long getProcessingTime() {
1310: return swValve.getProcessingTime();
1311: }
1312:
1313: public void setProcessingTime(long processingTime) {
1314: swValve.setProcessingTime(processingTime);
1315: }
1316:
1317: public long getMaxTime() {
1318: return swValve.getMaxTime();
1319: }
1320:
1321: public void setMaxTime(long maxTime) {
1322: swValve.setMaxTime(maxTime);
1323: }
1324:
1325: public long getMinTime() {
1326: return swValve.getMinTime();
1327: }
1328:
1329: public void setMinTime(long minTime) {
1330: swValve.setMinTime(minTime);
1331: }
1332:
1333: public int getRequestCount() {
1334: return swValve.getRequestCount();
1335: }
1336:
1337: public void setRequestCount(int requestCount) {
1338: swValve.setRequestCount(requestCount);
1339: }
1340:
1341: public int getErrorCount() {
1342: return swValve.getErrorCount();
1343: }
1344:
1345: public void setErrorCount(int errorCount) {
1346: swValve.setErrorCount(errorCount);
1347: }
1348:
1349: /**
1350: * Increment the error count used for monitoring.
1351: */
1352: public void incrementErrorCount() {
1353: swValve.setErrorCount(swValve.getErrorCount() + 1);
1354: }
1355:
1356: public long getLoadTime() {
1357: return loadTime;
1358: }
1359:
1360: public void setLoadTime(long loadTime) {
1361: this .loadTime = loadTime;
1362: }
1363:
1364: public int getClassLoadTime() {
1365: return classLoadTime;
1366: }
1367:
1368: // -------------------------------------------------------- Package Methods
1369:
1370: // -------------------------------------------------------- Private Methods
1371:
1372: /**
1373: * Add a default Mapper implementation if none have been configured
1374: * explicitly.
1375: *
1376: * @param mapperClass Java class name of the default Mapper
1377: */
1378: protected void addDefaultMapper(String mapperClass) {
1379:
1380: ; // No need for a default Mapper on a Wrapper
1381:
1382: }
1383:
1384: /**
1385: * Return <code>true</code> if the specified class name represents a
1386: * container provided servlet class that should be loaded by the
1387: * server class loader.
1388: *
1389: * @param classname Name of the class to be checked
1390: */
1391: private boolean isContainerProvidedServlet(String classname) {
1392:
1393: if (classname.startsWith("org.apache.catalina.")) {
1394: return (true);
1395: }
1396: try {
1397: Class clazz = this .getClass().getClassLoader().loadClass(
1398: classname);
1399: return (ContainerServlet.class.isAssignableFrom(clazz));
1400: } catch (Throwable t) {
1401: return (false);
1402: }
1403:
1404: }
1405:
1406: /**
1407: * Return <code>true</code> if loading this servlet is allowed.
1408: */
1409: private boolean isServletAllowed(Object servlet) {
1410:
1411: if (servlet instanceof ContainerServlet) {
1412: if (((Context) getParent()).getPrivileged()
1413: || (servlet.getClass().getName()
1414: .equals("org.apache.catalina.servlets.InvokerServlet"))) {
1415: return (true);
1416: } else {
1417: return (false);
1418: }
1419: }
1420:
1421: return (true);
1422:
1423: }
1424:
1425: /**
1426: * Log the abbreviated name of this Container for logging messages.
1427: */
1428: protected String logName() {
1429:
1430: StringBuffer sb = new StringBuffer("StandardWrapper[");
1431: if (getParent() != null)
1432: sb.append(getParent().getName());
1433: else
1434: sb.append("null");
1435: sb.append(':');
1436: sb.append(getName());
1437: sb.append(']');
1438: return (sb.toString());
1439:
1440: }
1441:
1442: // ------------------------------------------------------ Lifecycle Methods
1443:
1444: /**
1445: * Start this component, pre-loading the servlet if the load-on-startup
1446: * value is set appropriately.
1447: *
1448: * @exception LifecycleException if a fatal error occurs during startup
1449: */
1450: public void start() throws LifecycleException {
1451:
1452: // Send j2ee.state.starting notification
1453: if (this .getObjectName() != null) {
1454: Notification notification = new Notification(
1455: "j2ee.state.starting", this .getObjectName(),
1456: sequenceNumber++);
1457: broadcaster.sendNotification(notification);
1458: }
1459:
1460: // Start up this component
1461: super .start();
1462:
1463: if (oname != null)
1464: registerJMX((StandardContext) getParent());
1465:
1466: // Load and initialize an instance of this servlet if requested
1467: // MOVED TO StandardContext START() METHOD
1468:
1469: setAvailable(0L);
1470:
1471: // Send j2ee.state.running notification
1472: if (this .getObjectName() != null) {
1473: Notification notification = new Notification(
1474: "j2ee.state.running", this .getObjectName(),
1475: sequenceNumber++);
1476: broadcaster.sendNotification(notification);
1477: }
1478:
1479: }
1480:
1481: /**
1482: * Stop this component, gracefully shutting down the servlet if it has
1483: * been initialized.
1484: *
1485: * @exception LifecycleException if a fatal error occurs during shutdown
1486: */
1487: public void stop() throws LifecycleException {
1488:
1489: setAvailable(Long.MAX_VALUE);
1490:
1491: // Send j2ee.state.stopping notification
1492: if (this .getObjectName() != null) {
1493: Notification notification = new Notification(
1494: "j2ee.state.stopping", this .getObjectName(),
1495: sequenceNumber++);
1496: broadcaster.sendNotification(notification);
1497: }
1498:
1499: // Shut down our servlet instance (if it has been initialized)
1500: try {
1501: unload();
1502: } catch (ServletException e) {
1503: getServletContext().log(
1504: sm.getString("standardWrapper.unloadException",
1505: getName()), e);
1506: }
1507:
1508: // Shut down this component
1509: super .stop();
1510:
1511: // Send j2ee.state.stoppped notification
1512: if (this .getObjectName() != null) {
1513: Notification notification = new Notification(
1514: "j2ee.state.stopped", this .getObjectName(),
1515: sequenceNumber++);
1516: broadcaster.sendNotification(notification);
1517: }
1518:
1519: if (oname != null) {
1520: Registry.getRegistry(null, null).unregisterComponent(oname);
1521:
1522: // Send j2ee.object.deleted notification
1523: Notification notification = new Notification(
1524: "j2ee.object.deleted", this .getObjectName(),
1525: sequenceNumber++);
1526: broadcaster.sendNotification(notification);
1527: }
1528:
1529: if (isJspServlet && jspMonitorON != null) {
1530: Registry.getRegistry(null, null).unregisterComponent(
1531: jspMonitorON);
1532: }
1533:
1534: }
1535:
1536: protected void registerJMX(StandardContext ctx) {
1537:
1538: String parentName = ctx.getName();
1539: parentName = ("".equals(parentName)) ? "/" : parentName;
1540:
1541: String hostName = ctx.getParent().getName();
1542: hostName = (hostName == null) ? "DEFAULT" : hostName;
1543:
1544: String domain = ctx.getDomain();
1545:
1546: String webMod = "//" + hostName + parentName;
1547: String onameStr = domain + ":j2eeType=Servlet,name="
1548: + getName() + ",WebModule=" + webMod
1549: + ",J2EEApplication=" + ctx.getJ2EEApplication()
1550: + ",J2EEServer=" + ctx.getJ2EEServer();
1551: try {
1552: oname = new ObjectName(onameStr);
1553: controller = oname;
1554: Registry.getRegistry(null, null).registerComponent(this ,
1555: oname, null);
1556:
1557: // Send j2ee.object.created notification
1558: if (this .getObjectName() != null) {
1559: Notification notification = new Notification(
1560: "j2ee.object.created", this .getObjectName(),
1561: sequenceNumber++);
1562: broadcaster.sendNotification(notification);
1563: }
1564: } catch (Exception ex) {
1565: log.info("Error registering servlet with jmx " + this );
1566: }
1567:
1568: if (isJspServlet) {
1569: // Register JSP monitoring mbean
1570: onameStr = domain + ":type=JspMonitor,WebModule=" + webMod
1571: + ",J2EEApplication=" + ctx.getJ2EEApplication()
1572: + ",J2EEServer=" + ctx.getJ2EEServer();
1573: try {
1574: jspMonitorON = new ObjectName(onameStr);
1575: Registry.getRegistry(null, null).registerComponent(
1576: instance, jspMonitorON, null);
1577: } catch (Exception ex) {
1578: log.info("Error registering JSP monitoring with jmx "
1579: + instance);
1580: }
1581: }
1582:
1583: }
1584:
1585: // ------------------------------------------------------------- Attributes
1586:
1587: public boolean isEventProvider() {
1588: return false;
1589: }
1590:
1591: public boolean isStateManageable() {
1592: return false;
1593: }
1594:
1595: public boolean isStatisticsProvider() {
1596: return false;
1597: }
1598:
1599: }
|