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