0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/Embedded.java,v 1.16 2002/06/09 02:19:44 remm Exp $
0003: * $Revision: 1.16 $
0004: * $Date: 2002/06/09 02:19:44 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.startup;
0065:
0066: import java.beans.PropertyChangeListener;
0067: import java.beans.PropertyChangeSupport;
0068: import java.net.InetAddress;
0069: import java.util.Enumeration;
0070: import java.util.Properties;
0071:
0072: import org.apache.tomcat.util.IntrospectionUtils;
0073:
0074: import org.apache.catalina.Connector;
0075: import org.apache.catalina.Container;
0076: import org.apache.catalina.Context;
0077: import org.apache.catalina.Engine;
0078: import org.apache.catalina.Host;
0079: import org.apache.catalina.Lifecycle;
0080: import org.apache.catalina.LifecycleEvent;
0081: import org.apache.catalina.LifecycleException;
0082: import org.apache.catalina.LifecycleListener;
0083: import org.apache.catalina.Loader;
0084: import org.apache.catalina.Logger;
0085: import org.apache.catalina.Realm;
0086: import org.apache.catalina.core.StandardContext;
0087: import org.apache.catalina.core.StandardEngine;
0088: import org.apache.catalina.core.StandardHost;
0089: import org.apache.catalina.loader.WebappLoader;
0090: import org.apache.catalina.logger.FileLogger;
0091: import org.apache.catalina.logger.SystemOutLogger;
0092: import org.apache.catalina.net.ServerSocketFactory;
0093: import org.apache.catalina.realm.MemoryRealm;
0094: import org.apache.catalina.util.LifecycleSupport;
0095: import org.apache.catalina.util.StringManager;
0096:
0097: /**
0098: * Convenience class to embed a Catalina servlet container environment
0099: * inside another application. You must call the methods of this class in the
0100: * following order to ensure correct operation.
0101: *
0102: * <ul>
0103: * <li>Instantiate a new instance of this class.</li>
0104: * <li>Set the relevant properties of this object itself. In particular,
0105: * you will want to establish the default Logger to be used, as well
0106: * as the default Realm if you are using container-managed security.</li>
0107: * <li>Call <code>createEngine()</code> to create an Engine object, and then
0108: * call its property setters as desired.</li>
0109: * <li>Call <code>createHost()</code> to create at least one virtual Host
0110: * associated with the newly created Engine, and then call its property
0111: * setters as desired. After you customize this Host, add it to the
0112: * corresponding Engine with <code>engine.addChild(host)</code>.</li>
0113: * <li>Call <code>createContext()</code> to create at least one Context
0114: * associated with each newly created Host, and then call its property
0115: * setters as desired. You <strong>SHOULD</strong> create a Context with
0116: * a pathname equal to a zero-length string, which will be used to process
0117: * all requests not mapped to some other Context. After you customize
0118: * this Context, add it to the corresponding Host with
0119: * <code>host.addChild(context)</code>.</li>
0120: * <li>Call <code>addEngine()</code> to attach this Engine to the set of
0121: * defined Engines for this object.</li>
0122: * <li>Call <code>createConnector()</code> to create at least one TCP/IP
0123: * connector, and then call its property setters as desired.</li>
0124: * <li>Call <code>addConnector()</code> to attach this Connector to the set
0125: * of defined Connectors for this object. The added Connector will use
0126: * the most recently added Engine to process its received requests.</li>
0127: * <li>Repeat the above series of steps as often as required (although there
0128: * will typically be only one Engine instance created).</li>
0129: * <li>Call <code>start()</code> to initiate normal operations of all the
0130: * attached components.</li>
0131: * </ul>
0132: *
0133: * After normal operations have begun, you can add and remove Connectors,
0134: * Engines, Hosts, and Contexts on the fly. However, once you have removed
0135: * a particular component, it must be thrown away -- you can create a new one
0136: * with the same characteristics if you merely want to do a restart.
0137: * <p>
0138: * To initiate a normal shutdown, call the <code>stop()</code> method of
0139: * this object.
0140: * <p>
0141: * <strong>IMPLEMENTATION NOTE</strong>: The <code>main()</code> method of
0142: * this class is a simple example that exercizes the features of dynamically
0143: * starting and stopping various components. You can execute this by executing
0144: * the following steps (on a Unix platform):
0145: * <pre>
0146: * cd $CATALINA_HOME
0147: * ./bin/catalina.sh embedded
0148: * </pre>
0149: *
0150: * @author Craig R. McClanahan
0151: * @version $Revision: 1.16 $ $Date: 2002/06/09 02:19:44 $
0152: */
0153:
0154: public class Embedded implements Lifecycle {
0155:
0156: // ----------------------------------------------------------- Constructors
0157:
0158: /**
0159: * Construct a new instance of this class with default properties.
0160: */
0161: public Embedded() {
0162:
0163: this (null, null);
0164:
0165: }
0166:
0167: /**
0168: * Construct a new instance of this class with specified properties.
0169: *
0170: * @param logger Logger implementation to be inherited by all components
0171: * (unless overridden further down the container hierarchy)
0172: * @param realm Realm implementation to be inherited by all components
0173: * (unless overridden further down the container hierarchy)
0174: */
0175: public Embedded(Logger logger, Realm realm) {
0176:
0177: super ();
0178: setLogger(logger);
0179: setRealm(realm);
0180:
0181: }
0182:
0183: // ----------------------------------------------------- Instance Variables
0184:
0185: /**
0186: * The set of Connectors that have been deployed in this server.
0187: */
0188: protected Connector connectors[] = new Connector[0];
0189:
0190: /**
0191: * The debugging detail level for this component.
0192: */
0193: protected int debug = 0;
0194:
0195: /**
0196: * Is naming enabled ?
0197: */
0198: protected boolean useNaming = true;
0199:
0200: /**
0201: * The set of Engines that have been deployed in this server. Normally
0202: * there will only be one.
0203: */
0204: protected Engine engines[] = new Engine[0];
0205:
0206: /**
0207: * Descriptive information about this server implementation.
0208: */
0209: protected static final String info = "org.apache.catalina.startup.Embedded/1.0";
0210:
0211: /**
0212: * The lifecycle event support for this component.
0213: */
0214: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0215:
0216: /**
0217: * The default logger to be used by this component itself. Unless this
0218: * is overridden, log messages will be writted to standard output.
0219: */
0220: protected Logger logger = null;
0221:
0222: /**
0223: * The default realm to be used by all containers associated with
0224: * this compoennt.
0225: */
0226: protected Realm realm = null;
0227:
0228: /**
0229: * The string manager for this package.
0230: */
0231: protected static StringManager sm = StringManager
0232: .getManager(Constants.Package);
0233:
0234: /**
0235: * The socket factory that will be used when a <code>secure</code>
0236: * Connector is created. If a standard Connector is created, the
0237: * internal (to the Connector class default socket factory class)
0238: * will be used instead.
0239: */
0240: protected String socketFactory = "org.apache.catalina.net.SSLSocketFactory";
0241:
0242: /**
0243: * Has this component been started yet?
0244: */
0245: protected boolean started = false;
0246:
0247: /**
0248: * The property change support for this component.
0249: */
0250: protected PropertyChangeSupport support = new PropertyChangeSupport(
0251: this );
0252:
0253: // ------------------------------------------------------------- Properties
0254:
0255: /**
0256: * Return the debugging detail level for this component.
0257: */
0258: public int getDebug() {
0259:
0260: return (this .debug);
0261:
0262: }
0263:
0264: /**
0265: * Set the debugging detail level for this component.
0266: *
0267: * @param debug The new debugging detail level
0268: */
0269: public void setDebug(int debug) {
0270:
0271: int oldDebug = this .debug;
0272: this .debug = debug;
0273: support.firePropertyChange("debug", new Integer(oldDebug),
0274: new Integer(this .debug));
0275:
0276: }
0277:
0278: /**
0279: * Return true if naming is enabled.
0280: */
0281: public boolean isUseNaming() {
0282:
0283: return (this .useNaming);
0284:
0285: }
0286:
0287: /**
0288: * Enables or disables naming support.
0289: *
0290: * @param useNaming The new use naming value
0291: */
0292: public void setUseNaming(boolean useNaming) {
0293:
0294: boolean oldUseNaming = this .useNaming;
0295: this .useNaming = useNaming;
0296: support.firePropertyChange("useNaming", new Boolean(
0297: oldUseNaming), new Boolean(this .useNaming));
0298:
0299: }
0300:
0301: /**
0302: * Return the Logger for this component.
0303: */
0304: public Logger getLogger() {
0305:
0306: return (this .logger);
0307:
0308: }
0309:
0310: /**
0311: * Set the Logger for this component.
0312: *
0313: * @param logger The new logger
0314: */
0315: public void setLogger(Logger logger) {
0316:
0317: Logger oldLogger = this .logger;
0318: this .logger = logger;
0319: support.firePropertyChange("logger", oldLogger, this .logger);
0320:
0321: }
0322:
0323: /**
0324: * Return the default Realm for our Containers.
0325: */
0326: public Realm getRealm() {
0327:
0328: return (this .realm);
0329:
0330: }
0331:
0332: /**
0333: * Set the default Realm for our Containers.
0334: *
0335: * @param realm The new default realm
0336: */
0337: public void setRealm(Realm realm) {
0338:
0339: Realm oldRealm = this .realm;
0340: this .realm = realm;
0341: support.firePropertyChange("realm", oldRealm, this .realm);
0342:
0343: }
0344:
0345: /**
0346: * Return the secure socket factory class name.
0347: */
0348: public String getSocketFactory() {
0349:
0350: return (this .socketFactory);
0351:
0352: }
0353:
0354: /**
0355: * Set the secure socket factory class name.
0356: *
0357: * @param socketFactory The new secure socket factory class name
0358: */
0359: public void setSocketFactory(String socketFactory) {
0360:
0361: this .socketFactory = socketFactory;
0362:
0363: }
0364:
0365: // --------------------------------------------------------- Public Methods
0366:
0367: /**
0368: * Add a new Connector to the set of defined Connectors. The newly
0369: * added Connector will be associated with the most recently added Engine.
0370: *
0371: * @param connector The connector to be added
0372: *
0373: * @exception IllegalStateException if no engines have been added yet
0374: */
0375: public synchronized void addConnector(Connector connector) {
0376:
0377: if (debug >= 1) {
0378: logger
0379: .log("Adding connector (" + connector.getInfo()
0380: + ")");
0381: }
0382:
0383: // Make sure we have a Container to send requests to
0384: if (engines.length < 1)
0385: throw new IllegalStateException(sm
0386: .getString("embedded.noEngines"));
0387:
0388: // Configure this Connector as needed
0389: connector.setContainer(engines[engines.length - 1]);
0390:
0391: // Add this Connector to our set of defined Connectors
0392: Connector results[] = new Connector[connectors.length + 1];
0393: for (int i = 0; i < connectors.length; i++)
0394: results[i] = connectors[i];
0395: results[connectors.length] = connector;
0396: connectors = results;
0397:
0398: // Start this Connector if necessary
0399: if (started) {
0400: try {
0401: connector.initialize();
0402: if (connector instanceof Lifecycle) {
0403: ((Lifecycle) connector).start();
0404: }
0405: } catch (LifecycleException e) {
0406: logger.log("Connector.start", e);
0407: }
0408: }
0409:
0410: }
0411:
0412: /**
0413: * Add a new Engine to the set of defined Engines.
0414: *
0415: * @param engine The engine to be added
0416: */
0417: public synchronized void addEngine(Engine engine) {
0418:
0419: if (debug >= 1)
0420: logger.log("Adding engine (" + engine.getInfo() + ")");
0421:
0422: // Add this Engine to our set of defined Engines
0423: Engine results[] = new Engine[engines.length + 1];
0424: for (int i = 0; i < engines.length; i++)
0425: results[i] = engines[i];
0426: results[engines.length] = engine;
0427: engines = results;
0428:
0429: // Start this Engine if necessary
0430: if (started && (engine instanceof Lifecycle)) {
0431: try {
0432: ((Lifecycle) engine).start();
0433: } catch (LifecycleException e) {
0434: logger.log("Engine.start", e);
0435: }
0436: }
0437:
0438: }
0439:
0440: /**
0441: * Add a property change listener to this component.
0442: *
0443: * @param listener The listener to add
0444: */
0445: public void addPropertyChangeListener(
0446: PropertyChangeListener listener) {
0447:
0448: support.addPropertyChangeListener(listener);
0449:
0450: }
0451:
0452: /**
0453: * Create, configure, and return a new TCP/IP socket connector
0454: * based on the specified properties.
0455: *
0456: * @param address InetAddress to listen to, or <code>null</code>
0457: * to listen on all address on this server
0458: * @param port Port number to listen to
0459: * @param secure Should this port be SSL-enabled?
0460: */
0461: public Connector createConnector(InetAddress address, int port,
0462: boolean secure) {
0463:
0464: if (debug >= 1)
0465: logger.log("Creating connector for address='"
0466: + ((address == null) ? "ALL" : address
0467: .getHostAddress()) + "' port='" + port
0468: + "' secure='" + secure + "'");
0469:
0470: String protocol = "http";
0471: if (secure) {
0472: protocol = "https";
0473: }
0474:
0475: return createConnector(address, port, protocol);
0476:
0477: }
0478:
0479: public Connector createConnector(InetAddress address, int port,
0480: String protocol) {
0481:
0482: Connector connector = null;
0483:
0484: try {
0485:
0486: Class clazz = Class
0487: .forName("org.apache.coyote.tomcat4.CoyoteConnector");
0488: connector = (Connector) clazz.newInstance();
0489:
0490: if (address != null) {
0491: IntrospectionUtils.setProperty(connector, "address", ""
0492: + address);
0493: }
0494: IntrospectionUtils
0495: .setProperty(connector, "port", "" + port);
0496: IntrospectionUtils.setProperty(connector,
0497: "useURIValidationHack", "" + false);
0498:
0499: if (protocol.equals("ajp")) {
0500: IntrospectionUtils.setProperty(connector,
0501: "protocolHandlerClassName",
0502: "org.apache.jk.server.JkCoyoteHandler");
0503: } else if (protocol.equals("https")) {
0504: connector.setScheme("https");
0505: connector.setSecure(true);
0506: try {
0507: Class serverSocketFactoryClass = Class
0508: .forName("org.apache.coyote.tomcat4.CoyoteServerSocketFactory");
0509: ServerSocketFactory factory = (ServerSocketFactory) serverSocketFactoryClass
0510: .newInstance();
0511: connector.setFactory(factory);
0512: } catch (Exception e) {
0513: logger
0514: .log("Couldn't load SSL server socket factory.");
0515: }
0516: }
0517:
0518: } catch (Exception e) {
0519: logger.log("Couldn't create connector.");
0520: }
0521:
0522: return (connector);
0523:
0524: }
0525:
0526: /**
0527: * Create, configure, and return a Context that will process all
0528: * HTTP requests received from one of the associated Connectors,
0529: * and directed to the specified context path on the virtual host
0530: * to which this Context is connected.
0531: * <p>
0532: * After you have customized the properties, listeners, and Valves
0533: * for this Context, you must attach it to the corresponding Host
0534: * by calling:
0535: * <pre>
0536: * host.addChild(context);
0537: * </pre>
0538: * which will also cause the Context to be started if the Host has
0539: * already been started.
0540: *
0541: * @param path Context path of this application ("" for the default
0542: * application for this host, must start with a slash otherwise)
0543: * @param docBase Absolute pathname to the document base directory
0544: * for this web application
0545: *
0546: * @exception IllegalArgumentException if an invalid parameter
0547: * is specified
0548: */
0549: public Context createContext(String path, String docBase) {
0550:
0551: if (debug >= 1)
0552: logger.log("Creating context '" + path + "' with docBase '"
0553: + docBase + "'");
0554:
0555: StandardContext context = new StandardContext();
0556:
0557: context.setDebug(debug);
0558: context.setDocBase(docBase);
0559: context.setPath(path);
0560:
0561: ContextConfig config = new ContextConfig();
0562: config.setDebug(debug);
0563: ((Lifecycle) context).addLifecycleListener(config);
0564:
0565: return (context);
0566:
0567: }
0568:
0569: /**
0570: * Create, configure, and return an Engine that will process all
0571: * HTTP requests received from one of the associated Connectors,
0572: * based on the specified properties.
0573: */
0574: public Engine createEngine() {
0575:
0576: if (debug >= 1)
0577: logger.log("Creating engine");
0578:
0579: StandardEngine engine = new StandardEngine();
0580:
0581: engine.setDebug(debug);
0582: // Default host will be set to the first host added
0583: engine.setLogger(logger); // Inherited by all children
0584: engine.setRealm(realm); // Inherited by all children
0585:
0586: return (engine);
0587:
0588: }
0589:
0590: /**
0591: * Create, configure, and return a Host that will process all
0592: * HTTP requests received from one of the associated Connectors,
0593: * and directed to the specified virtual host.
0594: * <p>
0595: * After you have customized the properties, listeners, and Valves
0596: * for this Host, you must attach it to the corresponding Engine
0597: * by calling:
0598: * <pre>
0599: * engine.addChild(host);
0600: * </pre>
0601: * which will also cause the Host to be started if the Engine has
0602: * already been started. If this is the default (or only) Host you
0603: * will be defining, you may also tell the Engine to pass all requests
0604: * not assigned to another virtual host to this one:
0605: * <pre>
0606: * engine.setDefaultHost(host.getName());
0607: * </pre>
0608: *
0609: * @param name Canonical name of this virtual host
0610: * @param appBase Absolute pathname to the application base directory
0611: * for this virtual host
0612: *
0613: * @exception IllegalArgumentException if an invalid parameter
0614: * is specified
0615: */
0616: public Host createHost(String name, String appBase) {
0617:
0618: if (debug >= 1)
0619: logger.log("Creating host '" + name + "' with appBase '"
0620: + appBase + "'");
0621:
0622: StandardHost host = new StandardHost();
0623:
0624: host.setAppBase(appBase);
0625: host.setDebug(debug);
0626: host.setName(name);
0627:
0628: return (host);
0629:
0630: }
0631:
0632: /**
0633: * Create and return a class loader manager that can be customized, and
0634: * then attached to a Context, before it is started.
0635: *
0636: * @param parent ClassLoader that will be the parent of the one
0637: * created by this Loader
0638: */
0639: public Loader createLoader(ClassLoader parent) {
0640:
0641: if (debug >= 1)
0642: logger.log("Creating Loader with parent class loader '"
0643: + parent + "'");
0644:
0645: WebappLoader loader = new WebappLoader(parent);
0646: return (loader);
0647:
0648: }
0649:
0650: /**
0651: * Return descriptive information about this Server implementation and
0652: * the corresponding version number, in the format
0653: * <code><description>/<version></code>.
0654: */
0655: public String getInfo() {
0656:
0657: return (this .info);
0658:
0659: }
0660:
0661: /**
0662: * Remove the specified Connector from the set of defined Connectors.
0663: *
0664: * @param connector The Connector to be removed
0665: */
0666: public synchronized void removeConnector(Connector connector) {
0667:
0668: if (debug >= 1) {
0669: logger.log("Removing connector (" + connector.getInfo()
0670: + ")");
0671: }
0672:
0673: // Is the specified Connector actually defined?
0674: int j = -1;
0675: for (int i = 0; i < connectors.length; i++) {
0676: if (connector == connectors[i]) {
0677: j = i;
0678: break;
0679: }
0680: }
0681: if (j < 0)
0682: return;
0683:
0684: // Stop this Connector if necessary
0685: if (connector instanceof Lifecycle) {
0686: if (debug >= 1)
0687: logger.log(" Stopping this Connector");
0688: try {
0689: ((Lifecycle) connector).stop();
0690: } catch (LifecycleException e) {
0691: logger.log("Connector.stop", e);
0692: }
0693: }
0694:
0695: // Remove this Connector from our set of defined Connectors
0696: if (debug >= 1)
0697: logger.log(" Removing this Connector");
0698: int k = 0;
0699: Connector results[] = new Connector[connectors.length - 1];
0700: for (int i = 0; i < connectors.length; i++) {
0701: if (i != j)
0702: results[k++] = connectors[i];
0703: }
0704: connectors = results;
0705:
0706: }
0707:
0708: /**
0709: * Remove the specified Context from the set of defined Contexts for its
0710: * associated Host. If this is the last Context for this Host, the Host
0711: * will also be removed.
0712: *
0713: * @param context The Context to be removed
0714: */
0715: public synchronized void removeContext(Context context) {
0716:
0717: if (debug >= 1)
0718: logger.log("Removing context[" + context.getPath() + "]");
0719:
0720: // Is this Context actually among those that are defined?
0721: boolean found = false;
0722: for (int i = 0; i < engines.length; i++) {
0723: Container hosts[] = engines[i].findChildren();
0724: for (int j = 0; j < hosts.length; j++) {
0725: Container contexts[] = hosts[j].findChildren();
0726: for (int k = 0; k < contexts.length; k++) {
0727: if (context == (Context) contexts[k]) {
0728: found = true;
0729: break;
0730: }
0731: }
0732: if (found)
0733: break;
0734: }
0735: if (found)
0736: break;
0737: }
0738: if (!found)
0739: return;
0740:
0741: // Remove this Context from the associated Host
0742: if (debug >= 1)
0743: logger.log(" Removing this Context");
0744: context.getParent().removeChild(context);
0745:
0746: }
0747:
0748: /**
0749: * Remove the specified Engine from the set of defined Engines, along with
0750: * all of its related Hosts and Contexts. All associated Connectors are
0751: * also removed.
0752: *
0753: * @param engine The Engine to be removed
0754: */
0755: public synchronized void removeEngine(Engine engine) {
0756:
0757: if (debug >= 1)
0758: logger.log("Removing engine (" + engine.getInfo() + ")");
0759:
0760: // Is the specified Engine actually defined?
0761: int j = -1;
0762: for (int i = 0; i < engines.length; i++) {
0763: if (engine == engines[i]) {
0764: j = i;
0765: break;
0766: }
0767: }
0768: if (j < 0)
0769: return;
0770:
0771: // Remove any Connector that is using this Engine
0772: if (debug >= 1)
0773: logger.log(" Removing related Containers");
0774: while (true) {
0775: int n = -1;
0776: for (int i = 0; i < connectors.length; i++) {
0777: if (connectors[i].getContainer() == (Container) engine) {
0778: n = i;
0779: break;
0780: }
0781: }
0782: if (n < 0)
0783: break;
0784: removeConnector(connectors[n]);
0785: }
0786:
0787: // Stop this Engine if necessary
0788: if (engine instanceof Lifecycle) {
0789: if (debug >= 1)
0790: logger.log(" Stopping this Engine");
0791: try {
0792: ((Lifecycle) engine).stop();
0793: } catch (LifecycleException e) {
0794: logger.log("Engine.stop", e);
0795: }
0796: }
0797:
0798: // Remove this Engine from our set of defined Engines
0799: if (debug >= 1)
0800: logger.log(" Removing this Engine");
0801: int k = 0;
0802: Engine results[] = new Engine[engines.length - 1];
0803: for (int i = 0; i < engines.length; i++) {
0804: if (i != j)
0805: results[k++] = engines[i];
0806: }
0807: engines = results;
0808:
0809: }
0810:
0811: /**
0812: * Remove the specified Host, along with all of its related Contexts,
0813: * from the set of defined Hosts for its associated Engine. If this is
0814: * the last Host for this Engine, the Engine will also be removed.
0815: *
0816: * @param host The Host to be removed
0817: */
0818: public synchronized void removeHost(Host host) {
0819:
0820: if (debug >= 1)
0821: logger.log("Removing host[" + host.getName() + "]");
0822:
0823: // Is this Host actually among those that are defined?
0824: boolean found = false;
0825: for (int i = 0; i < engines.length; i++) {
0826: Container hosts[] = engines[i].findChildren();
0827: for (int j = 0; j < hosts.length; j++) {
0828: if (host == (Host) hosts[j]) {
0829: found = true;
0830: break;
0831:
0832: }
0833: }
0834: if (found)
0835: break;
0836: }
0837: if (!found)
0838: return;
0839:
0840: // Remove this Host from the associated Engine
0841: if (debug >= 1)
0842: logger.log(" Removing this Host");
0843: host.getParent().removeChild(host);
0844:
0845: }
0846:
0847: /**
0848: * Remove a property change listener from this component.
0849: *
0850: * @param listener The listener to remove
0851: */
0852: public void removePropertyChangeListener(
0853: PropertyChangeListener listener) {
0854:
0855: support.removePropertyChangeListener(listener);
0856:
0857: }
0858:
0859: // ------------------------------------------------------ Lifecycle Methods
0860:
0861: /**
0862: * Add a lifecycle event listener to this component.
0863: *
0864: * @param listener The listener to add
0865: */
0866: public void addLifecycleListener(LifecycleListener listener) {
0867:
0868: lifecycle.addLifecycleListener(listener);
0869:
0870: }
0871:
0872: /**
0873: * Get the lifecycle listeners associated with this lifecycle. If this
0874: * Lifecycle has no listeners registered, a zero-length array is returned.
0875: */
0876: public LifecycleListener[] findLifecycleListeners() {
0877:
0878: return lifecycle.findLifecycleListeners();
0879:
0880: }
0881:
0882: /**
0883: * Remove a lifecycle event listener from this component.
0884: *
0885: * @param listener The listener to remove
0886: */
0887: public void removeLifecycleListener(LifecycleListener listener) {
0888:
0889: lifecycle.removeLifecycleListener(listener);
0890:
0891: }
0892:
0893: /**
0894: * Prepare for the beginning of active use of the public methods of this
0895: * component. This method should be called after <code>configure()</code>,
0896: * and before any of the public methods of the component are utilized.
0897: *
0898: * @exception LifecycleException if this component detects a fatal error
0899: * that prevents this component from being used
0900: */
0901: public void start() throws LifecycleException {
0902:
0903: if (debug >= 1)
0904: logger.log("Starting embedded server");
0905:
0906: // Validate the setup of our required system properties
0907: if (System.getProperty("catalina.home") == null) {
0908: // Backwards compatibility patch for J2EE RI 1.3
0909: String j2eeHome = System
0910: .getProperty("com.sun.enterprise.home");
0911: if (j2eeHome != null)
0912: System.setProperty("catalina.home", System
0913: .getProperty("com.sun.enterprise.home"));
0914: else
0915: throw new LifecycleException(
0916: "Must set 'catalina.home' system property");
0917: }
0918: if (System.getProperty("catalina.base") == null)
0919: System.setProperty("catalina.base", System
0920: .getProperty("catalina.home"));
0921:
0922: // Validate and update our current component state
0923: if (started)
0924: throw new LifecycleException(sm
0925: .getString("embedded.alreadyStarted"));
0926: lifecycle.fireLifecycleEvent(START_EVENT, null);
0927: started = true;
0928:
0929: // Initialize some naming specific properties
0930: if (!useNaming) {
0931: System.setProperty("catalina.useNaming", "false");
0932: } else {
0933: System.setProperty("catalina.useNaming", "true");
0934: String value = "org.apache.naming";
0935: String oldValue = System
0936: .getProperty(javax.naming.Context.URL_PKG_PREFIXES);
0937: if (oldValue != null) {
0938: value = oldValue + ":" + value;
0939: }
0940: System.setProperty(javax.naming.Context.URL_PKG_PREFIXES,
0941: value);
0942: System.setProperty(
0943: javax.naming.Context.INITIAL_CONTEXT_FACTORY,
0944: "org.apache.naming.java.javaURLContextFactory");
0945: }
0946:
0947: // Start our defined Engines first
0948: for (int i = 0; i < engines.length; i++) {
0949: if (engines[i] instanceof Lifecycle)
0950: ((Lifecycle) engines[i]).start();
0951: }
0952:
0953: // Start our defined Connectors second
0954: for (int i = 0; i < connectors.length; i++) {
0955: connectors[i].initialize();
0956: if (connectors[i] instanceof Lifecycle)
0957: ((Lifecycle) connectors[i]).start();
0958: }
0959:
0960: }
0961:
0962: /**
0963: * Gracefully terminate the active use of the public methods of this
0964: * component. This method should be the last one called on a given
0965: * instance of this component.
0966: *
0967: * @exception LifecycleException if this component detects a fatal error
0968: * that needs to be reported
0969: */
0970: public void stop() throws LifecycleException {
0971:
0972: if (debug >= 1)
0973: logger.log("Stopping embedded server");
0974:
0975: // Validate and update our current component state
0976: if (!started)
0977: throw new LifecycleException(sm
0978: .getString("embedded.notStarted"));
0979: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
0980: started = false;
0981:
0982: // Stop our defined Connectors first
0983: for (int i = 0; i < connectors.length; i++) {
0984: if (connectors[i] instanceof Lifecycle)
0985: ((Lifecycle) connectors[i]).stop();
0986: }
0987:
0988: // Stop our defined Engines second
0989: for (int i = 0; i < engines.length; i++) {
0990: if (engines[i] instanceof Lifecycle)
0991: ((Lifecycle) engines[i]).stop();
0992: }
0993:
0994: }
0995:
0996: // ------------------------------------------------------ Protected Methods
0997:
0998: // -------------------------------------------------------- Private Methods
0999:
1000: // ----------------------------------------------------------- Main Program
1001:
1002: /**
1003: * This main program is a unit test to exercize the various methods of
1004: * the Embedded class. It can be used as an example of the type of code
1005: * that would be used in a real environment.
1006: *
1007: * @param args The command line arguments
1008: */
1009: public static void main(String args[]) {
1010:
1011: Embedded embedded = new Embedded(new SystemOutLogger(),
1012: new MemoryRealm());
1013: embedded.setDebug(5);
1014: embedded.setLogger(new SystemOutLogger());
1015: String home = System.getProperty("catalina.home");
1016: if (home == null) {
1017: System.err
1018: .println("You must set the 'catalina.home' system property");
1019: System.exit(1);
1020: }
1021: String base = System.getProperty("catalina.base");
1022: if (base == null) {
1023: base = home;
1024: System.setProperty("catalina.base", base);
1025: }
1026:
1027: // Start up this embedded server (to prove we can dynamically
1028: // add and remove containers and connectors later)
1029: try {
1030: embedded.start();
1031: } catch (LifecycleException e) {
1032: System.err.println("start: " + e.toString());
1033: e.printStackTrace();
1034: }
1035:
1036: // Assemble and install a very basic container hierarchy
1037: // that simulates a portion of the one configured in server.xml
1038: // by default
1039: Engine engine = embedded.createEngine();
1040: engine.setDefaultHost("localhost");
1041:
1042: Host host = embedded.createHost("localhost", home + "/webapps");
1043: engine.addChild(host);
1044:
1045: Context root = embedded.createContext("", home
1046: + "/webapps/ROOT");
1047: host.addChild(root);
1048:
1049: Context examples = embedded.createContext("/examples", home
1050: + "/webapps/examples");
1051: customize(examples); // Special customization for this web-app
1052: host.addChild(examples);
1053:
1054: // As an alternative to the three lines above, there is also a very
1055: // simple method to deploy a new application that has default values
1056: // for all context properties:
1057: // String contextPath = ... context path for this app ...
1058: // URL docRoot = ... URL of WAR file or unpacked directory ...
1059: // ((Deployer) host).deploy(contextPath, docRoot);
1060:
1061: // Install the assembled container hierarchy
1062: embedded.addEngine(engine);
1063:
1064: // Assemble and install a non-secure connector for port 8080
1065: Connector connector = embedded.createConnector(null, 8080,
1066: false);
1067: embedded.addConnector(connector);
1068:
1069: // Pause for a while to allow brief testing
1070: // (In reality this would last until the enclosing application
1071: // needs to be shut down)
1072: try {
1073: Thread.sleep(2 * 60 * 1000L); // Two minutes
1074: } catch (InterruptedException e) {
1075: ;
1076: }
1077:
1078: // Remove the examples context dynamically
1079: embedded.removeContext(examples);
1080:
1081: // Remove the engine (which should trigger removing the connector)
1082: embedded.removeEngine(engine);
1083:
1084: // Shut down this embedded server (should have nothing left to do)
1085: try {
1086: embedded.stop();
1087: } catch (LifecycleException e) {
1088: System.err.println("stop: " + e.toString());
1089: e.printStackTrace();
1090: }
1091:
1092: }
1093:
1094: /**
1095: * Customize the specified context to have its own log file instead of
1096: * inheriting the default one. This is just an example of what you can
1097: * do; pretty much anything (such as installing special Valves) can
1098: * be done prior to calling <code>start()</code>.
1099: *
1100: * @param context Context to receive a specialized logger
1101: */
1102: private static void customize(Context context) {
1103:
1104: // Create a customized file logger for this context
1105: String basename = context.getPath();
1106: if (basename.length() < 1)
1107: basename = "ROOT";
1108: else
1109: basename = basename.substring(1);
1110:
1111: FileLogger special = new FileLogger();
1112: special.setPrefix(basename + "_log.");
1113: special.setSuffix(".txt");
1114: special.setTimestamp(true);
1115:
1116: // Override the default logger for this context
1117: context.setLogger(special);
1118:
1119: }
1120:
1121: }
|