0001: /*
0002: * Copyright 1999-2001,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.beans.PropertyChangeListener;
0020: import java.beans.PropertyChangeSupport;
0021: import java.io.IOException;
0022: import java.io.Serializable;
0023: import java.security.AccessController;
0024: import java.security.PrivilegedAction;
0025: import java.util.ArrayList;
0026: import java.util.HashMap;
0027: import java.util.Hashtable;
0028: import java.util.Iterator;
0029:
0030: import javax.management.MBeanRegistration;
0031: import javax.management.MBeanServer;
0032: import javax.management.MalformedObjectNameException;
0033: import javax.management.ObjectName;
0034: import javax.naming.directory.DirContext;
0035: import javax.servlet.ServletException;
0036:
0037: import org.apache.catalina.Cluster;
0038: import org.apache.catalina.Container;
0039: import org.apache.catalina.ContainerEvent;
0040: import org.apache.catalina.ContainerListener;
0041: import org.apache.catalina.Lifecycle;
0042: import org.apache.catalina.LifecycleException;
0043: import org.apache.catalina.LifecycleListener;
0044: import org.apache.catalina.Loader;
0045: import org.apache.catalina.Logger;
0046: import org.apache.catalina.Manager;
0047: import org.apache.catalina.Pipeline;
0048: import org.apache.catalina.Realm;
0049: import org.apache.catalina.Request;
0050: import org.apache.catalina.Response;
0051: import org.apache.catalina.Valve;
0052: import org.apache.catalina.logger.LoggerBase;
0053: import org.apache.catalina.util.LifecycleSupport;
0054: import org.apache.catalina.util.StringManager;
0055: import org.apache.commons.modeler.Registry;
0056: import org.apache.naming.resources.ProxyDirContext;
0057:
0058: /**
0059: * Abstract implementation of the <b>Container</b> interface, providing common
0060: * functionality required by nearly every implementation. Classes extending
0061: * this base class must implement <code>getInfo()</code>, and may implement
0062: * a replacement for <code>invoke()</code>.
0063: * <p>
0064: * All subclasses of this abstract base class will include support for a
0065: * Pipeline object that defines the processing to be performed for each request
0066: * received by the <code>invoke()</code> method of this class, utilizing the
0067: * "Chain of Responsibility" design pattern. A subclass should encapsulate its
0068: * own processing functionality as a <code>Valve</code>, and configure this
0069: * Valve into the pipeline by calling <code>setBasic()</code>.
0070: * <p>
0071: * This implementation fires property change events, per the JavaBeans design
0072: * pattern, for changes in singleton properties. In addition, it fires the
0073: * following <code>ContainerEvent</code> events to listeners who register
0074: * themselves with <code>addContainerListener()</code>:
0075: * <table border=1>
0076: * <tr>
0077: * <th>Type</th>
0078: * <th>Data</th>
0079: * <th>Description</th>
0080: * </tr>
0081: * <tr>
0082: * <td align=center><code>addChild</code></td>
0083: * <td align=center><code>Container</code></td>
0084: * <td>Child container added to this Container.</td>
0085: * </tr>
0086: * <tr>
0087: * <td align=center><code>addValve</code></td>
0088: * <td align=center><code>Valve</code></td>
0089: * <td>Valve added to this Container.</td>
0090: * </tr>
0091: * <tr>
0092: * <td align=center><code>removeChild</code></td>
0093: * <td align=center><code>Container</code></td>
0094: * <td>Child container removed from this Container.</td>
0095: * </tr>
0096: * <tr>
0097: * <td align=center><code>removeValve</code></td>
0098: * <td align=center><code>Valve</code></td>
0099: * <td>Valve removed from this Container.</td>
0100: * </tr>
0101: * <tr>
0102: * <td align=center><code>start</code></td>
0103: * <td align=center><code>null</code></td>
0104: * <td>Container was started.</td>
0105: * </tr>
0106: * <tr>
0107: * <td align=center><code>stop</code></td>
0108: * <td align=center><code>null</code></td>
0109: * <td>Container was stopped.</td>
0110: * </tr>
0111: * </table>
0112: * Subclasses that fire additional events should document them in the
0113: * class comments of the implementation class.
0114: *
0115: * @author Craig R. McClanahan
0116: */
0117:
0118: public abstract class ContainerBase implements Container, Lifecycle,
0119: Pipeline, MBeanRegistration, Serializable {
0120:
0121: private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
0122: .getLog(ContainerBase.class);
0123:
0124: /**
0125: * Perform addChild with the permissions of this class.
0126: * addChild can be called with the XML parser on the stack,
0127: * this allows the XML parser to have fewer privileges than
0128: * Tomcat.
0129: */
0130: protected class PrivilegedAddChild implements PrivilegedAction {
0131:
0132: private Container child;
0133:
0134: PrivilegedAddChild(Container child) {
0135: this .child = child;
0136: }
0137:
0138: public Object run() {
0139: addChildInternal(child);
0140: return null;
0141: }
0142:
0143: }
0144:
0145: // ----------------------------------------------------- Instance Variables
0146:
0147: /**
0148: * The child Containers belonging to this Container, keyed by name.
0149: */
0150: protected HashMap children = new HashMap();
0151:
0152: /**
0153: * The debugging detail level for this component.
0154: */
0155: protected int debug = 0;
0156:
0157: /**
0158: * The processor delay for this component.
0159: */
0160: protected int backgroundProcessorDelay = -1;
0161:
0162: /**
0163: * The lifecycle event support for this component.
0164: */
0165: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0166:
0167: /**
0168: * The container event listeners for this Container.
0169: */
0170: protected ArrayList listeners = new ArrayList();
0171:
0172: /**
0173: * The Loader implementation with which this Container is associated.
0174: */
0175: protected Loader loader = null;
0176:
0177: /**
0178: * The Logger implementation with which this Container is associated.
0179: */
0180: protected Logger logger = null;
0181:
0182: /**
0183: * The Manager implementation with which this Container is associated.
0184: */
0185: protected Manager manager = null;
0186:
0187: /**
0188: * The cluster with which this Container is associated.
0189: */
0190: protected Cluster cluster = null;
0191:
0192: /**
0193: * The human-readable name of this Container.
0194: */
0195: protected String name = null;
0196:
0197: /**
0198: * The parent Container to which this Container is a child.
0199: */
0200: protected Container parent = null;
0201:
0202: /**
0203: * The parent class loader to be configured when we install a Loader.
0204: */
0205: protected ClassLoader parentClassLoader = null;
0206:
0207: /**
0208: * The Pipeline object with which this Container is associated.
0209: */
0210: protected Pipeline pipeline = new StandardPipeline(this );
0211:
0212: /**
0213: * The Realm with which this Container is associated.
0214: */
0215: protected Realm realm = null;
0216:
0217: /**
0218: * The resources DirContext object with which this Container is associated.
0219: */
0220: protected DirContext resources = null;
0221:
0222: /**
0223: * The string manager for this package.
0224: */
0225: protected static StringManager sm = StringManager
0226: .getManager(Constants.Package);
0227:
0228: /**
0229: * Has this component been started?
0230: */
0231: protected boolean started = false;
0232:
0233: protected boolean initialized = false;
0234:
0235: /**
0236: * The property change support for this component.
0237: */
0238: protected PropertyChangeSupport support = new PropertyChangeSupport(
0239: this );
0240:
0241: /**
0242: * The background thread.
0243: */
0244: private Thread thread = null;
0245:
0246: /**
0247: * The background thread completion semaphore.
0248: */
0249: private boolean threadDone = false;
0250:
0251: // ------------------------------------------------------------- Properties
0252:
0253: /**
0254: * Return the debugging detail level for this component.
0255: */
0256: public int getDebug() {
0257:
0258: return (this .debug);
0259:
0260: }
0261:
0262: /**
0263: * Set the debugging detail level for this component.
0264: *
0265: * @param debug The new debugging detail level
0266: */
0267: public void setDebug(int debug) {
0268:
0269: int oldDebug = this .debug;
0270: this .debug = debug;
0271: support.firePropertyChange("debug", new Integer(oldDebug),
0272: new Integer(this .debug));
0273:
0274: }
0275:
0276: /**
0277: * Get the delay between the invocation of the backgroundProcess method on
0278: * this container and its children. Child containers will not be invoked
0279: * if their delay value is not negative (which would mean they are using
0280: * their own thread). Setting this to a positive value will cause
0281: * a thread to be spawn. After waiting the specified amount of time,
0282: * the thread will invoke the executePeriodic method on this container
0283: * and all its children.
0284: */
0285: public int getBackgroundProcessorDelay() {
0286: return backgroundProcessorDelay;
0287: }
0288:
0289: /**
0290: * Set the delay between the invocation of the execute method on this
0291: * container and its children.
0292: *
0293: * @param delay The delay in seconds between the invocation of
0294: * backgroundProcess methods
0295: */
0296: public void setBackgroundProcessorDelay(int delay) {
0297: backgroundProcessorDelay = delay;
0298: }
0299:
0300: /**
0301: * Return descriptive information about this Container implementation and
0302: * the corresponding version number, in the format
0303: * <code><description>/<version></code>.
0304: */
0305: public String getInfo() {
0306: return this .getClass().getName();
0307: }
0308:
0309: /**
0310: * Return the Loader with which this Container is associated. If there is
0311: * no associated Loader, return the Loader associated with our parent
0312: * Container (if any); otherwise, return <code>null</code>.
0313: */
0314: public Loader getLoader() {
0315:
0316: if (loader != null)
0317: return (loader);
0318: if (parent != null)
0319: return (parent.getLoader());
0320: return (null);
0321:
0322: }
0323:
0324: /**
0325: * Set the Loader with which this Container is associated.
0326: *
0327: * @param loader The newly associated loader
0328: */
0329: public synchronized void setLoader(Loader loader) {
0330:
0331: // Change components if necessary
0332: Loader oldLoader = this .loader;
0333: if (oldLoader == loader)
0334: return;
0335: this .loader = loader;
0336:
0337: // Stop the old component if necessary
0338: if (started && (oldLoader != null)
0339: && (oldLoader instanceof Lifecycle)) {
0340: try {
0341: ((Lifecycle) oldLoader).stop();
0342: } catch (LifecycleException e) {
0343: log.error("ContainerBase.setLoader: stop: ", e);
0344: }
0345: }
0346:
0347: // Start the new component if necessary
0348: if (loader != null)
0349: loader.setContainer(this );
0350: if (started && (loader != null)
0351: && (loader instanceof Lifecycle)) {
0352: try {
0353: ((Lifecycle) loader).start();
0354: } catch (LifecycleException e) {
0355: log.error("ContainerBase.setLoader: start: ", e);
0356: }
0357: }
0358:
0359: // Report this property change to interested listeners
0360: support.firePropertyChange("loader", oldLoader, this .loader);
0361:
0362: }
0363:
0364: /**
0365: * Return the Logger with which this Container is associated. If there is
0366: * no associated Logger, return the Logger associated with our parent
0367: * Container (if any); otherwise return <code>null</code>.
0368: */
0369: public Logger getLogger() {
0370:
0371: if (logger != null)
0372: return (logger);
0373: if (parent != null)
0374: return (parent.getLogger());
0375: return (null);
0376:
0377: }
0378:
0379: /**
0380: * Set the Logger with which this Container is associated.
0381: *
0382: * @param logger The newly associated Logger
0383: */
0384: public synchronized void setLogger(Logger logger) {
0385:
0386: // Change components if necessary
0387: Logger oldLogger = this .logger;
0388: if (oldLogger == logger)
0389: return;
0390: this .logger = logger;
0391:
0392: // Stop the old component if necessary
0393: if (started && (oldLogger != null)
0394: && (oldLogger instanceof Lifecycle)) {
0395: try {
0396: ((Lifecycle) oldLogger).stop();
0397: } catch (LifecycleException e) {
0398: log.error("ContainerBase.setLogger: stop: ", e);
0399: }
0400: }
0401:
0402: // Start the new component if necessary
0403: if (logger != null)
0404: logger.setContainer(this );
0405: if (started && (logger != null)
0406: && (logger instanceof Lifecycle)) {
0407: try {
0408: ((Lifecycle) logger).start();
0409: } catch (LifecycleException e) {
0410: log.error("ContainerBase.setLogger: start: ", e);
0411: }
0412: }
0413:
0414: // Report this property change to interested listeners
0415: support.firePropertyChange("logger", oldLogger, this .logger);
0416:
0417: }
0418:
0419: /**
0420: * Return the Manager with which this Container is associated. If there is
0421: * no associated Manager, return the Manager associated with our parent
0422: * Container (if any); otherwise return <code>null</code>.
0423: */
0424: public Manager getManager() {
0425:
0426: if (manager != null)
0427: return (manager);
0428: if (parent != null)
0429: return (parent.getManager());
0430: return (null);
0431:
0432: }
0433:
0434: /**
0435: * Set the Manager with which this Container is associated.
0436: *
0437: * @param manager The newly associated Manager
0438: */
0439: public synchronized void setManager(Manager manager) {
0440:
0441: // Change components if necessary
0442: Manager oldManager = this .manager;
0443: if (oldManager == manager)
0444: return;
0445: this .manager = manager;
0446:
0447: // Stop the old component if necessary
0448: if (started && (oldManager != null)
0449: && (oldManager instanceof Lifecycle)) {
0450: try {
0451: ((Lifecycle) oldManager).stop();
0452: } catch (LifecycleException e) {
0453: log.error("ContainerBase.setManager: stop: ", e);
0454: }
0455: }
0456:
0457: // Start the new component if necessary
0458: if (manager != null)
0459: manager.setContainer(this );
0460: if (started && (manager != null)
0461: && (manager instanceof Lifecycle)) {
0462: try {
0463: ((Lifecycle) manager).start();
0464: } catch (LifecycleException e) {
0465: log.error("ContainerBase.setManager: start: ", e);
0466: }
0467: }
0468:
0469: // Report this property change to interested listeners
0470: support.firePropertyChange("manager", oldManager, this .manager);
0471:
0472: }
0473:
0474: /**
0475: * Return an object which may be utilized for mapping to this component.
0476: */
0477: public Object getMappingObject() {
0478: return this ;
0479: }
0480:
0481: /**
0482: * Return the Cluster with which this Container is associated. If there is
0483: * no associated Cluster, return the Cluster associated with our parent
0484: * Container (if any); otherwise return <code>null</code>.
0485: */
0486: public Cluster getCluster() {
0487: if (cluster != null)
0488: return (cluster);
0489:
0490: if (parent != null)
0491: return (parent.getCluster());
0492:
0493: return (null);
0494: }
0495:
0496: /**
0497: * Set the Cluster with which this Container is associated.
0498: *
0499: * @param cluster The newly associated Cluster
0500: */
0501: public synchronized void setCluster(Cluster cluster) {
0502: // Change components if necessary
0503: Cluster oldCluster = this .cluster;
0504: if (oldCluster == cluster)
0505: return;
0506: this .cluster = cluster;
0507:
0508: // Stop the old component if necessary
0509: if (started && (oldCluster != null)
0510: && (oldCluster instanceof Lifecycle)) {
0511: try {
0512: ((Lifecycle) oldCluster).stop();
0513: } catch (LifecycleException e) {
0514: log.error("ContainerBase.setCluster: stop: ", e);
0515: }
0516: }
0517:
0518: // Start the new component if necessary
0519: if (cluster != null)
0520: cluster.setContainer(this );
0521:
0522: if (started && (cluster != null)
0523: && (cluster instanceof Lifecycle)) {
0524: try {
0525: ((Lifecycle) cluster).start();
0526: } catch (LifecycleException e) {
0527: log.error("ContainerBase.setCluster: start: ", e);
0528: }
0529: }
0530:
0531: // Report this property change to interested listeners
0532: support.firePropertyChange("cluster", oldCluster, this .cluster);
0533: }
0534:
0535: /**
0536: * Return a name string (suitable for use by humans) that describes this
0537: * Container. Within the set of child containers belonging to a particular
0538: * parent, Container names must be unique.
0539: */
0540: public String getName() {
0541:
0542: return (name);
0543:
0544: }
0545:
0546: /**
0547: * Set a name string (suitable for use by humans) that describes this
0548: * Container. Within the set of child containers belonging to a particular
0549: * parent, Container names must be unique.
0550: *
0551: * @param name New name of this container
0552: *
0553: * @exception IllegalStateException if this Container has already been
0554: * added to the children of a parent Container (after which the name
0555: * may not be changed)
0556: */
0557: public void setName(String name) {
0558:
0559: String oldName = this .name;
0560: this .name = name;
0561: support.firePropertyChange("name", oldName, this .name);
0562: }
0563:
0564: /**
0565: * Return the Container for which this Container is a child, if there is
0566: * one. If there is no defined parent, return <code>null</code>.
0567: */
0568: public Container getParent() {
0569:
0570: return (parent);
0571:
0572: }
0573:
0574: /**
0575: * Set the parent Container to which this Container is being added as a
0576: * child. This Container may refuse to become attached to the specified
0577: * Container by throwing an exception.
0578: *
0579: * @param container Container to which this Container is being added
0580: * as a child
0581: *
0582: * @exception IllegalArgumentException if this Container refuses to become
0583: * attached to the specified Container
0584: */
0585: public void setParent(Container container) {
0586:
0587: Container oldParent = this .parent;
0588: this .parent = container;
0589: support.firePropertyChange("parent", oldParent, this .parent);
0590:
0591: }
0592:
0593: /**
0594: * Return the parent class loader (if any) for this web application.
0595: * This call is meaningful only <strong>after</strong> a Loader has
0596: * been configured.
0597: */
0598: public ClassLoader getParentClassLoader() {
0599: if (parentClassLoader != null)
0600: return (parentClassLoader);
0601: if (parent != null) {
0602: return (parent.getParentClassLoader());
0603: }
0604: return (ClassLoader.getSystemClassLoader());
0605:
0606: }
0607:
0608: /**
0609: * Set the parent class loader (if any) for this web application.
0610: * This call is meaningful only <strong>before</strong> a Loader has
0611: * been configured, and the specified value (if non-null) should be
0612: * passed as an argument to the class loader constructor.
0613: *
0614: *
0615: * @param parent The new parent class loader
0616: */
0617: public void setParentClassLoader(ClassLoader parent) {
0618: ClassLoader oldParentClassLoader = this .parentClassLoader;
0619: this .parentClassLoader = parent;
0620: support.firePropertyChange("parentClassLoader",
0621: oldParentClassLoader, this .parentClassLoader);
0622:
0623: }
0624:
0625: /**
0626: * Return the Pipeline object that manages the Valves associated with
0627: * this Container.
0628: */
0629: public Pipeline getPipeline() {
0630:
0631: return (this .pipeline);
0632:
0633: }
0634:
0635: /**
0636: * Return the Realm with which this Container is associated. If there is
0637: * no associated Realm, return the Realm associated with our parent
0638: * Container (if any); otherwise return <code>null</code>.
0639: */
0640: public Realm getRealm() {
0641:
0642: if (realm != null)
0643: return (realm);
0644: if (parent != null)
0645: return (parent.getRealm());
0646: return (null);
0647:
0648: }
0649:
0650: /**
0651: * Set the Realm with which this Container is associated.
0652: *
0653: * @param realm The newly associated Realm
0654: */
0655: public synchronized void setRealm(Realm realm) {
0656:
0657: // Change components if necessary
0658: Realm oldRealm = this .realm;
0659: if (oldRealm == realm)
0660: return;
0661: this .realm = realm;
0662:
0663: // Stop the old component if necessary
0664: if (started && (oldRealm != null)
0665: && (oldRealm instanceof Lifecycle)) {
0666: try {
0667: ((Lifecycle) oldRealm).stop();
0668: } catch (LifecycleException e) {
0669: log.error("ContainerBase.setRealm: stop: ", e);
0670: }
0671: }
0672:
0673: // Start the new component if necessary
0674: if (realm != null)
0675: realm.setContainer(this );
0676: if (started && (realm != null) && (realm instanceof Lifecycle)) {
0677: try {
0678: ((Lifecycle) realm).start();
0679: } catch (LifecycleException e) {
0680: log.error("ContainerBase.setRealm: start: ", e);
0681: }
0682: }
0683:
0684: // Report this property change to interested listeners
0685: support.firePropertyChange("realm", oldRealm, this .realm);
0686:
0687: }
0688:
0689: /**
0690: * Return the resources DirContext object with which this Container is
0691: * associated. If there is no associated resources object, return the
0692: * resources associated with our parent Container (if any); otherwise
0693: * return <code>null</code>.
0694: */
0695: public DirContext getResources() {
0696: if (resources != null)
0697: return (resources);
0698: if (parent != null)
0699: return (parent.getResources());
0700: return (null);
0701:
0702: }
0703:
0704: /**
0705: * Set the resources DirContext object with which this Container is
0706: * associated.
0707: *
0708: * @param resources The newly associated DirContext
0709: */
0710: public synchronized void setResources(DirContext resources) {
0711: // Called from StandardContext.setResources()
0712: // <- StandardContext.start()
0713: // <- ContainerBase.addChildInternal()
0714:
0715: // Change components if necessary
0716: DirContext oldResources = this .resources;
0717: if (oldResources == resources)
0718: return;
0719: Hashtable env = new Hashtable();
0720: if (getParent() != null)
0721: env.put(ProxyDirContext.HOST, getParent().getName());
0722: env.put(ProxyDirContext.CONTEXT, getName());
0723: this .resources = new ProxyDirContext(env, resources);
0724: // Report this property change to interested listeners
0725: support.firePropertyChange("resources", oldResources,
0726: this .resources);
0727:
0728: }
0729:
0730: // ------------------------------------------------------ Container Methods
0731:
0732: /**
0733: * Add a new child Container to those associated with this Container,
0734: * if supported. Prior to adding this Container to the set of children,
0735: * the child's <code>setParent()</code> method must be called, with this
0736: * Container as an argument. This method may thrown an
0737: * <code>IllegalArgumentException</code> if this Container chooses not
0738: * to be attached to the specified Container, in which case it is not added
0739: *
0740: * @param child New child Container to be added
0741: *
0742: * @exception IllegalArgumentException if this exception is thrown by
0743: * the <code>setParent()</code> method of the child Container
0744: * @exception IllegalArgumentException if the new child does not have
0745: * a name unique from that of existing children of this Container
0746: * @exception IllegalStateException if this Container does not support
0747: * child Containers
0748: */
0749: public void addChild(Container child) {
0750: if (System.getSecurityManager() != null) {
0751: PrivilegedAction dp = new PrivilegedAddChild(child);
0752: AccessController.doPrivileged(dp);
0753: } else {
0754: addChildInternal(child);
0755: }
0756: }
0757:
0758: private void addChildInternal(Container child) {
0759:
0760: if (log.isDebugEnabled())
0761: log.debug("Add child " + child + " " + this );
0762: synchronized (children) {
0763: if (children.get(child.getName()) != null)
0764: throw new IllegalArgumentException(
0765: "addChild: Child name '" + child.getName()
0766: + "' is not unique");
0767: child.setParent(this ); // May throw IAE
0768: if (started && (child instanceof Lifecycle)) {
0769: try {
0770: ((Lifecycle) child).start();
0771: } catch (LifecycleException e) {
0772: log.error("ContainerBase.addChild: start: ", e);
0773: throw new IllegalStateException(
0774: "ContainerBase.addChild: start: " + e);
0775: }
0776: }
0777: children.put(child.getName(), child);
0778:
0779: fireContainerEvent(ADD_CHILD_EVENT, child);
0780: }
0781:
0782: }
0783:
0784: /**
0785: * Add a container event listener to this component.
0786: *
0787: * @param listener The listener to add
0788: */
0789: public void addContainerListener(ContainerListener listener) {
0790:
0791: synchronized (listeners) {
0792: listeners.add(listener);
0793: }
0794:
0795: }
0796:
0797: /**
0798: * Add a property change listener to this component.
0799: *
0800: * @param listener The listener to add
0801: */
0802: public void addPropertyChangeListener(
0803: PropertyChangeListener listener) {
0804:
0805: support.addPropertyChangeListener(listener);
0806:
0807: }
0808:
0809: /**
0810: * Return the child Container, associated with this Container, with
0811: * the specified name (if any); otherwise, return <code>null</code>
0812: *
0813: * @param name Name of the child Container to be retrieved
0814: */
0815: public Container findChild(String name) {
0816:
0817: if (name == null)
0818: return (null);
0819: synchronized (children) { // Required by post-start changes
0820: return ((Container) children.get(name));
0821: }
0822:
0823: }
0824:
0825: /**
0826: * Return the set of children Containers associated with this Container.
0827: * If this Container has no children, a zero-length array is returned.
0828: */
0829: public Container[] findChildren() {
0830:
0831: synchronized (children) {
0832: Container results[] = new Container[children.size()];
0833: return ((Container[]) children.values().toArray(results));
0834: }
0835:
0836: }
0837:
0838: /**
0839: * Return the set of container listeners associated with this Container.
0840: * If this Container has no registered container listeners, a zero-length
0841: * array is returned.
0842: */
0843: public ContainerListener[] findContainerListeners() {
0844:
0845: synchronized (listeners) {
0846: ContainerListener[] results = new ContainerListener[listeners
0847: .size()];
0848: return ((ContainerListener[]) listeners.toArray(results));
0849: }
0850:
0851: }
0852:
0853: /**
0854: * Process the specified Request, to produce the corresponding Response,
0855: * by invoking the first Valve in our pipeline (if any), or the basic
0856: * Valve otherwise.
0857: *
0858: * @param request Request to be processed
0859: * @param response Response to be produced
0860: *
0861: * @exception IllegalStateException if neither a pipeline or a basic
0862: * Valve have been configured for this Container
0863: * @exception IOException if an input/output error occurred while
0864: * processing
0865: * @exception ServletException if a ServletException was thrown
0866: * while processing this request
0867: */
0868: public final void invoke(Request request, Response response)
0869: throws IOException, ServletException {
0870:
0871: pipeline.invoke(request, response);
0872:
0873: }
0874:
0875: /**
0876: * Remove an existing child Container from association with this parent
0877: * Container.
0878: *
0879: * @param child Existing child Container to be removed
0880: */
0881: public void removeChild(Container child) {
0882:
0883: synchronized (children) {
0884: if (children.get(child.getName()) == null)
0885: return;
0886: children.remove(child.getName());
0887: }
0888:
0889: if (started && (child instanceof Lifecycle)) {
0890: try {
0891: if (child instanceof ContainerBase) {
0892: if (((ContainerBase) child).started) {
0893: ((Lifecycle) child).stop();
0894: }
0895: } else {
0896: ((Lifecycle) child).stop();
0897: }
0898: } catch (LifecycleException e) {
0899: log.error("ContainerBase.removeChild: stop: ", e);
0900: }
0901: }
0902:
0903: fireContainerEvent(REMOVE_CHILD_EVENT, child);
0904:
0905: // child.setParent(null);
0906:
0907: }
0908:
0909: /**
0910: * Remove a container event listener from this component.
0911: *
0912: * @param listener The listener to remove
0913: */
0914: public void removeContainerListener(ContainerListener listener) {
0915:
0916: synchronized (listeners) {
0917: listeners.remove(listener);
0918: }
0919:
0920: }
0921:
0922: /**
0923: * Remove a property change listener from this component.
0924: *
0925: * @param listener The listener to remove
0926: */
0927: public void removePropertyChangeListener(
0928: PropertyChangeListener listener) {
0929:
0930: support.removePropertyChangeListener(listener);
0931:
0932: }
0933:
0934: // ------------------------------------------------------ Lifecycle Methods
0935:
0936: /**
0937: * Add a lifecycle event listener to this component.
0938: *
0939: * @param listener The listener to add
0940: */
0941: public void addLifecycleListener(LifecycleListener listener) {
0942:
0943: lifecycle.addLifecycleListener(listener);
0944:
0945: }
0946:
0947: /**
0948: * Get the lifecycle listeners associated with this lifecycle. If this
0949: * Lifecycle has no listeners registered, a zero-length array is returned.
0950: */
0951: public LifecycleListener[] findLifecycleListeners() {
0952:
0953: return lifecycle.findLifecycleListeners();
0954:
0955: }
0956:
0957: /**
0958: * Remove a lifecycle event listener from this component.
0959: *
0960: * @param listener The listener to remove
0961: */
0962: public void removeLifecycleListener(LifecycleListener listener) {
0963:
0964: lifecycle.removeLifecycleListener(listener);
0965:
0966: }
0967:
0968: /**
0969: * Prepare for active use of the public methods of this Component.
0970: *
0971: * @exception LifecycleException if this component detects a fatal error
0972: * that prevents it from being started
0973: */
0974: public synchronized void start() throws LifecycleException {
0975:
0976: // Validate and update our current component state
0977: if (started) {
0978: log.info(sm.getString("containerBase.alreadyStarted",
0979: logName()));
0980: return;
0981: }
0982:
0983: if (logger instanceof LoggerBase) {
0984: LoggerBase lb = (LoggerBase) logger;
0985: if (lb.getObjectName() == null) {
0986: ObjectName lname = lb.createObjectName();
0987: try {
0988: Registry.getRegistry(null, null).registerComponent(
0989: lb, lname, null);
0990: } catch (Exception ex) {
0991: log.error("Can't register logger " + lname, ex);
0992: }
0993: }
0994: }
0995:
0996: // Notify our interested LifecycleListeners
0997: lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
0998:
0999: started = true;
1000:
1001: // Start our subordinate components, if any
1002: if ((loader != null) && (loader instanceof Lifecycle))
1003: ((Lifecycle) loader).start();
1004: if ((logger != null) && (logger instanceof Lifecycle))
1005: ((Lifecycle) logger).start();
1006: if ((manager != null) && (manager instanceof Lifecycle))
1007: ((Lifecycle) manager).start();
1008: if ((cluster != null) && (cluster instanceof Lifecycle))
1009: ((Lifecycle) cluster).start();
1010: if ((realm != null) && (realm instanceof Lifecycle))
1011: ((Lifecycle) realm).start();
1012: if ((resources != null) && (resources instanceof Lifecycle))
1013: ((Lifecycle) resources).start();
1014:
1015: // Start our child containers, if any
1016: Container children[] = findChildren();
1017: for (int i = 0; i < children.length; i++) {
1018: if (children[i] instanceof Lifecycle)
1019: ((Lifecycle) children[i]).start();
1020: }
1021:
1022: // Start the Valves in our pipeline (including the basic), if any
1023: if (pipeline instanceof Lifecycle)
1024: ((Lifecycle) pipeline).start();
1025:
1026: // Notify our interested LifecycleListeners
1027: lifecycle.fireLifecycleEvent(START_EVENT, null);
1028:
1029: // Start our thread
1030: threadStart();
1031:
1032: // Notify our interested LifecycleListeners
1033: lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
1034:
1035: }
1036:
1037: /**
1038: * Gracefully shut down active use of the public methods of this Component.
1039: *
1040: * @exception LifecycleException if this component detects a fatal error
1041: * that needs to be reported
1042: */
1043: public synchronized void stop() throws LifecycleException {
1044:
1045: // Validate and update our current component state
1046: if (!started) {
1047: log.info(sm
1048: .getString("containerBase.notStarted", logName()));
1049: return;
1050: }
1051:
1052: // Notify our interested LifecycleListeners
1053: lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
1054:
1055: // Stop our thread
1056: threadStop();
1057:
1058: // Notify our interested LifecycleListeners
1059: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
1060: started = false;
1061:
1062: // Stop the Valves in our pipeline (including the basic), if any
1063: if (pipeline instanceof Lifecycle) {
1064: ((Lifecycle) pipeline).stop();
1065: }
1066:
1067: // Stop our child containers, if any
1068: Container children[] = findChildren();
1069: for (int i = 0; i < children.length; i++) {
1070: if (children[i] instanceof Lifecycle)
1071: ((Lifecycle) children[i]).stop();
1072: }
1073: // Remove children - so next start can work
1074: children = findChildren();
1075: for (int i = 0; i < children.length; i++) {
1076: removeChild(children[i]);
1077: }
1078:
1079: // Stop our subordinate components, if any
1080: if ((resources != null) && (resources instanceof Lifecycle)) {
1081: ((Lifecycle) resources).stop();
1082: }
1083: if ((realm != null) && (realm instanceof Lifecycle)) {
1084: ((Lifecycle) realm).stop();
1085: }
1086: if ((cluster != null) && (cluster instanceof Lifecycle)) {
1087: ((Lifecycle) cluster).stop();
1088: }
1089: if ((manager != null) && (manager instanceof Lifecycle)) {
1090: ((Lifecycle) manager).stop();
1091: }
1092: if ((logger != null) && (logger instanceof Lifecycle)) {
1093: ((Lifecycle) logger).stop();
1094: }
1095: if ((loader != null) && (loader instanceof Lifecycle)) {
1096: ((Lifecycle) loader).stop();
1097: }
1098:
1099: if (logger instanceof LoggerBase) {
1100: LoggerBase lb = (LoggerBase) logger;
1101: if (lb.getObjectName() != null) {
1102: try {
1103: Registry.getRegistry(null, null)
1104: .unregisterComponent(lb.getObjectName());
1105: } catch (Exception ex) {
1106: log.error("Can't unregister logger "
1107: + lb.getObjectName(), ex);
1108: }
1109: }
1110: }
1111:
1112: // Notify our interested LifecycleListeners
1113: lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
1114:
1115: }
1116:
1117: /** Init method, part of the MBean lifecycle.
1118: * If the container was added via JMX, it'll register itself with the
1119: * parent, using the ObjectName conventions to locate the parent.
1120: *
1121: * If the container was added directly and it doesn't have an ObjectName,
1122: * it'll create a name and register itself with the JMX console. On destroy(),
1123: * the object will unregister.
1124: *
1125: * @throws Exception
1126: */
1127: public void init() throws Exception {
1128:
1129: if (this .getParent() == null) {
1130: // "Life" update
1131: ObjectName parentName = getParentName();
1132:
1133: //log.info("Register " + parentName );
1134: if (parentName != null && mserver.isRegistered(parentName)) {
1135: mserver
1136: .invoke(
1137: parentName,
1138: "addChild",
1139: new Object[] { this },
1140: new String[] { "org.apache.catalina.Container" });
1141: }
1142: }
1143: initialized = true;
1144: }
1145:
1146: public ObjectName getParentName()
1147: throws MalformedObjectNameException {
1148: return null;
1149: }
1150:
1151: public void destroy() throws Exception {
1152: if (started) {
1153: stop();
1154: }
1155: initialized = false;
1156:
1157: // unregister this component
1158: if (oname != null) {
1159: try {
1160: if (controller == oname) {
1161: Registry.getRegistry(null, null)
1162: .unregisterComponent(oname);
1163: log.debug("unregistering " + oname);
1164: }
1165: } catch (Throwable t) {
1166: log.error("Error unregistering ", t);
1167: }
1168: }
1169:
1170: if (parent != null) {
1171: parent.removeChild(this );
1172: }
1173:
1174: // Stop our child containers, if any
1175: Container children[] = findChildren();
1176: for (int i = 0; i < children.length; i++) {
1177: removeChild(children[i]);
1178: }
1179:
1180: }
1181:
1182: // ------------------------------------------------------- Pipeline Methods
1183:
1184: /**
1185: * Add a new Valve to the end of the pipeline associated with this
1186: * Container. Prior to adding the Valve, the Valve's
1187: * <code>setContainer</code> method must be called, with this Container
1188: * as an argument. The method may throw an
1189: * <code>IllegalArgumentException</code> if this Valve chooses not to
1190: * be associated with this Container, or <code>IllegalStateException</code>
1191: * if it is already associated with a different Container.
1192: *
1193: * @param valve Valve to be added
1194: *
1195: * @exception IllegalArgumentException if this Container refused to
1196: * accept the specified Valve
1197: * @exception IllegalArgumentException if the specifie Valve refuses to be
1198: * associated with this Container
1199: * @exception IllegalStateException if the specified Valve is already
1200: * associated with a different Container
1201: */
1202: public synchronized void addValve(Valve valve) {
1203:
1204: pipeline.addValve(valve);
1205: fireContainerEvent(ADD_VALVE_EVENT, valve);
1206: }
1207:
1208: public ObjectName[] getValveObjectNames() {
1209: return ((StandardPipeline) pipeline).getValveObjectNames();
1210: }
1211:
1212: /**
1213: * <p>Return the Valve instance that has been distinguished as the basic
1214: * Valve for this Pipeline (if any).
1215: */
1216: public Valve getBasic() {
1217:
1218: return (pipeline.getBasic());
1219:
1220: }
1221:
1222: /**
1223: * Return the set of Valves in the pipeline associated with this
1224: * Container, including the basic Valve (if any). If there are no
1225: * such Valves, a zero-length array is returned.
1226: */
1227: public Valve[] getValves() {
1228:
1229: return (pipeline.getValves());
1230:
1231: }
1232:
1233: /**
1234: * Remove the specified Valve from the pipeline associated with this
1235: * Container, if it is found; otherwise, do nothing.
1236: *
1237: * @param valve Valve to be removed
1238: */
1239: public synchronized void removeValve(Valve valve) {
1240:
1241: pipeline.removeValve(valve);
1242: fireContainerEvent(REMOVE_VALVE_EVENT, valve);
1243: }
1244:
1245: /**
1246: * <p>Set the Valve instance that has been distinguished as the basic
1247: * Valve for this Pipeline (if any). Prioer to setting the basic Valve,
1248: * the Valve's <code>setContainer()</code> will be called, if it
1249: * implements <code>Contained</code>, with the owning Container as an
1250: * argument. The method may throw an <code>IllegalArgumentException</code>
1251: * if this Valve chooses not to be associated with this Container, or
1252: * <code>IllegalStateException</code> if it is already associated with
1253: * a different Container.</p>
1254: *
1255: * @param valve Valve to be distinguished as the basic Valve
1256: */
1257: public void setBasic(Valve valve) {
1258:
1259: pipeline.setBasic(valve);
1260:
1261: }
1262:
1263: /**
1264: * Execute a periodic task, such as reloading, etc. This method will be
1265: * invoked inside the classloading context of this container. Unexpected
1266: * throwables will be caught and logged.
1267: */
1268: public void backgroundProcess() {
1269: }
1270:
1271: // ------------------------------------------------------ Protected Methods
1272:
1273: /**
1274: * Notify all container event listeners that a particular event has
1275: * occurred for this Container. The default implementation performs
1276: * this notification synchronously using the calling thread.
1277: *
1278: * @param type Event type
1279: * @param data Event data
1280: */
1281: public void fireContainerEvent(String type, Object data) {
1282:
1283: if (listeners.size() < 1)
1284: return;
1285: ContainerEvent event = new ContainerEvent(this , type, data);
1286: ContainerListener list[] = new ContainerListener[0];
1287: synchronized (listeners) {
1288: list = (ContainerListener[]) listeners.toArray(list);
1289: }
1290: for (int i = 0; i < list.length; i++)
1291: ((ContainerListener) list[i]).containerEvent(event);
1292:
1293: }
1294:
1295: /**
1296: * Log the specified message to our current Logger (if any).
1297: *
1298: * @param message Message to be logged
1299: */
1300: protected void log(String message) {
1301:
1302: // Logger logger = getLogger();
1303: // if (logger != null)
1304: // logger.log(logName() + ": " + message);
1305: // else
1306: log.info(message);
1307: }
1308:
1309: /**
1310: * Log the specified message and exception to our current Logger
1311: * (if any).
1312: *
1313: * @param message Message to be logged
1314: * @param throwable Related exception
1315: */
1316: protected void log(String message, Throwable throwable) {
1317:
1318: Logger logger = getLogger();
1319: if (logger != null)
1320: logger.log(logName() + ": " + message, throwable);
1321: else {
1322: log.error(message, throwable);
1323: }
1324:
1325: }
1326:
1327: /**
1328: * Return the abbreviated name of this container for logging messsages
1329: */
1330: protected String logName() {
1331:
1332: String className = this .getClass().getName();
1333: int period = className.lastIndexOf(".");
1334: if (period >= 0)
1335: className = className.substring(period + 1);
1336: return (className + "[" + getName() + "]");
1337:
1338: }
1339:
1340: // -------------------- JMX and Registration --------------------
1341: protected String type;
1342: protected String domain;
1343: protected String suffix;
1344: protected ObjectName oname;
1345: protected ObjectName controller;
1346: protected transient MBeanServer mserver;
1347:
1348: public ObjectName getJmxName() {
1349: return oname;
1350: }
1351:
1352: public String getObjectName() {
1353: if (oname != null) {
1354: return oname.toString();
1355: } else
1356: return null;
1357: }
1358:
1359: public String getDomain() {
1360: if (domain == null) {
1361: Container parent = this ;
1362: while (parent != null
1363: && !(parent instanceof StandardEngine)) {
1364: parent = parent.getParent();
1365: }
1366: if (parent instanceof StandardEngine) {
1367: domain = ((StandardEngine) parent).getDomain();
1368: }
1369: }
1370: return domain;
1371: }
1372:
1373: public void setDomain(String domain) {
1374: this .domain = domain;
1375: }
1376:
1377: public String getType() {
1378: return type;
1379: }
1380:
1381: protected String getJSR77Suffix() {
1382: return suffix;
1383: }
1384:
1385: public ObjectName preRegister(MBeanServer server, ObjectName name)
1386: throws Exception {
1387: oname = name;
1388: mserver = server;
1389: if (name == null) {
1390: return null;
1391: }
1392:
1393: domain = name.getDomain();
1394:
1395: type = name.getKeyProperty("type");
1396: if (type == null) {
1397: type = name.getKeyProperty("j2eeType");
1398: }
1399:
1400: String j2eeApp = name.getKeyProperty("J2EEApplication");
1401: String j2eeServer = name.getKeyProperty("J2EEServer");
1402: if (j2eeApp == null) {
1403: j2eeApp = "none";
1404: }
1405: if (j2eeServer == null) {
1406: j2eeServer = "none";
1407: }
1408: suffix = ",J2EEApplication=" + j2eeApp + ",J2EEServer="
1409: + j2eeServer;
1410: return name;
1411: }
1412:
1413: public void postRegister(Boolean registrationDone) {
1414: }
1415:
1416: public void preDeregister() throws Exception {
1417: }
1418:
1419: public void postDeregister() {
1420: }
1421:
1422: public ObjectName[] getChildren() {
1423: ObjectName result[] = new ObjectName[children.size()];
1424: Iterator it = children.values().iterator();
1425: int i = 0;
1426: while (it.hasNext()) {
1427: Object next = it.next();
1428: if (next instanceof ContainerBase) {
1429: result[i++] = ((ContainerBase) next).getJmxName();
1430: }
1431: }
1432: return result;
1433: }
1434:
1435: public ObjectName createObjectName(String domain, ObjectName parent)
1436: throws Exception {
1437: if (log.isDebugEnabled())
1438: log.debug("Create ObjectName " + domain + " " + parent);
1439: return null;
1440: }
1441:
1442: public String getContainerSuffix() {
1443: Container container = this ;
1444: Container context = null;
1445: Container host = null;
1446: Container servlet = null;
1447:
1448: StringBuffer suffix = new StringBuffer();
1449:
1450: if (container instanceof StandardHost) {
1451: host = container;
1452: } else if (container instanceof StandardContext) {
1453: host = container.getParent();
1454: context = container;
1455: } else if (container instanceof StandardWrapper) {
1456: context = container.getParent();
1457: host = context.getParent();
1458: servlet = container;
1459: }
1460: if (context != null) {
1461: String path = ((StandardContext) context).getPath();
1462: suffix.append(",path=").append(
1463: (path.equals("")) ? "/" : path);
1464: }
1465: if (host != null)
1466: suffix.append(",host=").append(host.getName());
1467: if (servlet != null) {
1468: String name = container.getName();
1469: suffix.append(",servlet=");
1470: suffix.append((name == "") ? "/" : name);
1471: }
1472: return suffix.toString();
1473: }
1474:
1475: /**
1476: * Start the background thread that will periodically check for
1477: * session timeouts.
1478: */
1479: protected void threadStart() {
1480:
1481: if (thread != null)
1482: return;
1483: if (backgroundProcessorDelay <= 0)
1484: return;
1485:
1486: threadDone = false;
1487: String threadName = "ContainerBackgroundProcessor["
1488: + toString() + "]";
1489: thread = new Thread(new ContainerBackgroundProcessor(),
1490: threadName);
1491: thread.setDaemon(true);
1492: thread.start();
1493:
1494: }
1495:
1496: /**
1497: * Stop the background thread that is periodically checking for
1498: * session timeouts.
1499: */
1500: protected void threadStop() {
1501:
1502: if (thread == null)
1503: return;
1504:
1505: threadDone = true;
1506: thread.interrupt();
1507: try {
1508: thread.join();
1509: } catch (InterruptedException e) {
1510: ;
1511: }
1512:
1513: thread = null;
1514:
1515: }
1516:
1517: // -------------------------------------- ContainerExecuteDelay Inner Class
1518:
1519: /**
1520: * Private thread class to invoke the backgroundProcess method
1521: * of this container and its children after a fixed delay.
1522: */
1523: protected class ContainerBackgroundProcessor implements Runnable {
1524:
1525: public void run() {
1526: while (!threadDone) {
1527: try {
1528: Thread.sleep(backgroundProcessorDelay * 1000L);
1529: } catch (InterruptedException e) {
1530: ;
1531: }
1532: if (!threadDone) {
1533: Container parent = (Container) getMappingObject();
1534: ClassLoader cl = Thread.currentThread()
1535: .getContextClassLoader();
1536: if (parent.getLoader() != null) {
1537: cl = parent.getLoader().getClassLoader();
1538: }
1539: processChildren(parent, cl);
1540: }
1541: }
1542: }
1543:
1544: protected void processChildren(Container container,
1545: ClassLoader cl) {
1546: try {
1547: if (container.getLoader() != null) {
1548: Thread.currentThread().setContextClassLoader(
1549: container.getLoader().getClassLoader());
1550: }
1551: container.backgroundProcess();
1552: } catch (Throwable t) {
1553: log.error("Exception invoking periodic operation: ", t);
1554: } finally {
1555: Thread.currentThread().setContextClassLoader(cl);
1556: }
1557: Container[] children = container.findChildren();
1558: for (int i = 0; i < children.length; i++) {
1559: if (children[i].getBackgroundProcessorDelay() <= 0) {
1560: processChildren(children[i], cl);
1561: }
1562: }
1563: }
1564:
1565: }
1566:
1567: }
|