//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010  huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE-----------------------------
package org.huihoo.willow.core;

import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Connector;
import org.huihoo.willow.Container;
import org.huihoo.willow.Context;
import org.huihoo.willow.DefaultContext;
import org.huihoo.willow.Deployer;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.Manager;
import org.huihoo.willow.RealmDatabase;
import org.huihoo.willow.Server;
import org.huihoo.willow.ServerFactory;
import org.huihoo.willow.Service;
import org.huihoo.willow.realm.UserDatabaseRealm;
import org.huihoo.willow.session.StandardManager;
import org.huihoo.willow.startup.ServerInfo;

public class StandardService extends ContainerBase implements Deployer, Service
{
	private static Log log = LogFactory.getLog(StandardService.class);

	// ----------------------------------------------------- Instance Variables

	/**
	 * The set of Connectors associated with this Service.
	 */
	private Connector connectors[] = new Connector[0];

	/**
	 * Descriptive information about this component implementation.
	 */
	private static final String info = "org.huohoo.willow.core.StandardService/1.0";

	/**
	 * Has this component been initialized?
	 */
	private boolean initialized = false;

	/**
	 * The <code>Server</code> that owns this Service, if any.
	 */
	private Server server = null;

	/**
	 * The <code>Service</code> that owns this Service, if any.
	 */
	private Service service = null;

	/**
		* The application root for this Service.
		*/
	private String engineBase = ".";

	/**
		* The auto deploy flag for this Service.
		*/
	private boolean autoDeploy = true;

	/**
	 * The deploy on startup flag for this Service.
	 */
	private boolean deployOnStartup = true;

	/**
	 * The Java class name of the default context configuration class
	 * for deployed web applications.
	 */
	private String configClass = "org.huihoo.willow.startup.ContextConfig";

	/**
	 * The Java class name of the default StandardContext implementation class for
	 * deployed workflow applications.
	 */
	private String contextClass = "org.huihoo.willow.core.StandardContext";

	/**
	 * The <code>ServiceDeployer</code> to whom we delegate application
	 * deployment requests.
	 */
	private Deployer deployer = null;

	/**
	 * The session timeout (in minutes) for this web application.
	 */
	private int sessionTimeout = 30;

	/**
	 * Frequency of the session expiration, and related manager operations.
	 * SessionManager operations will be done once for the specified amount of
	 * backgrondProcess calls (ie, the lower the amount, the most often the
	 * checks will occur).
	 */
	private int managerChecksFrequency = 6;

	/**
	 * Iteration count for background processing.
	 */
	private int count = 0;

	/**
	 * Unpack WARs property.
	 */
	private boolean unpackWARs = true;

	/**
	 * DefaultContext config
	 */
	private DefaultContext defaultContext;

	/**
	 * Work Directory base for applications.
	 */
	private String workDir = null;

	/**
	 * The Realm with which this Container is associated.
	 */
	private RealmDatabase realmDatabase = null;

	/**
	 * The Manager implementation with which this Service is associated.
	 */
	protected Manager manager = null;

	/**
	 * Attribute value used to turn on/off XML validation
	 */
	private boolean xmlValidation = false;

	/**
	 * Attribute value used to turn on/off XML namespace awarenes.
	 */
	private boolean xmlNamespaceAware = false;

	// ----------------------------------------------------------- Constructors

	/**
	 * Create a new StandardService component with the default basic Valve.
	 */
	public StandardService()
	{

		super();

		// By default, the service will hold the reloading thread
		backgroundProcessorDelay = 10;

	}

	//------------------------------------------------------------- Properties

	/**
	 * Return the <code>Server</code> with which we are associated (if any).
	 */
	public Server getServer()
	{

		return (this.server);

	}

	/**
	 * Set the <code>Server</code> with which we are associated (if any).
	 *
	 * @param server The server that owns this Service
	 */
	public void setServer(Server server)
	{

		this.server = server;

	}

	public RealmDatabase getRealmDatabase()
	{
		return this.realmDatabase;
	}

	public void setRealmDatabase(RealmDatabase realmDatabase)
	{
		//	Change components if necessary
		RealmDatabase oldRealm = this.realmDatabase;
		if (oldRealm == realmDatabase)
			return;
		this.realmDatabase = realmDatabase;

		// Stop the old component if necessary
		if (started && (oldRealm != null) && (oldRealm instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) oldRealm).stop();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setRealmDatabase: stop: ", e);
			}
		}

		// Start the new component if necessary
		if ((realmDatabase != null) && (realmDatabase instanceof UserDatabaseRealm))
		{
			((UserDatabaseRealm) realmDatabase).setContainer(this);
		}

		if (started && (realmDatabase != null) && (realmDatabase instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) realmDatabase).start();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setRealmDatabase: start: ", e);
			}
		}

		support.firePropertyChange("realmDatabase", oldRealm, this.realmDatabase);
	}

	/**
	 * Return the application root for this Service.  This can be an absolute
	 * pathname, a relative pathname
	 */
	public String getAppBase()
	{
		return (this.engineBase);
	}

	/**
	 * Set the application root for this Service.  This can be an absolute
	 * pathname, a relative pathname
	 *
	 * @param appBase The new application root
	 */
	public void setAppBase(String engineBase)
	{

		String oldServiceBase = this.engineBase;
		this.engineBase = engineBase;

		File engineBaseFile = new File(this.engineBase);

		if (!engineBaseFile.isAbsolute())
		{
			engineBaseFile = new File(System.getProperty(Globals.PROPS_WILLOW_HOME), this.engineBase);
			this.engineBase = engineBaseFile.getAbsolutePath();
		}

		support.firePropertyChange("engineBase", oldServiceBase, this.engineBase);

	}

	/**
	 * Return the value of the auto deploy flag.  If true, it indicates that 
	 * this Service's child webapps will be dynamically deployed.
	 */
	public boolean getAutoDeploy()
	{

		return (this.autoDeploy);

	}

	/**
	 * Set the auto deploy flag value for this Service.
	 * 
	 * @param autoDeploy The new auto deploy flag
	 */
	public void setAutoDeploy(boolean autoDeploy)
	{

		boolean oldAutoDeploy = this.autoDeploy;
		this.autoDeploy = autoDeploy;
		support.firePropertyChange("autoDeploy", oldAutoDeploy, this.autoDeploy);

	}

	/**
	 * Return the Java class name of the context configuration class
	 * for new web applications.
	 */
	public String getConfigClass()
	{

		return (this.configClass);

	}

	/**
	 * Set the Java class name of the context configuration class
	 * for new web applications.
	 *
	 * @param configClass The new context configuration class
	 */
	public void setConfigClass(String configClass)
	{

		String oldConfigClass = this.configClass;
		this.configClass = configClass;
		support.firePropertyChange("configClass", oldConfigClass, this.configClass);

	}

	/**
	 * Set the DefaultContext
	 * for new web applications.
	 *
	 * @param defaultContext The new DefaultContext
	 */
	public void addDefaultContext(DefaultContext defaultContext)
	{

		DefaultContext oldDefaultContext = this.defaultContext;
		this.defaultContext = defaultContext;
		support.firePropertyChange("defaultContext", oldDefaultContext, this.defaultContext);

	}

	/**
	 * Retrieve the DefaultContext for new web applications.
	 */
	public DefaultContext getDefaultContext()
	{
		return (this.defaultContext);
	}

	/**
	 * Return the Java class name of the ParameterContext implementation class
	 * for new web applications.
	 */
	public String getContextClass()
	{

		return (this.contextClass);

	}

	/**
	 * Set the Java class name of the ParameterContext implementation class
	 * for new web applications.
	 *
	 * @param contextClass The new context implementation class
	 */
	public void setContextClass(String contextClass)
	{

		String oldContextClass = this.contextClass;
		this.contextClass = contextClass;
		support.firePropertyChange("contextClass", oldContextClass, this.contextClass);

	}

	/**
	 * Return the default session timeout (in minutes) for this
	 * org.huihoo.workflow client
	 */
	public int getSessionTimeout()
	{

		return (this.sessionTimeout);

	}

	/**
	 * Set the default session timeout (in minutes) for this
	 * org.huihoo.workflow client
	 *
	 * @param timeout The new default session timeout
	 */
	public void setSessionTimeout(int timeout)
	{

		int oldSessionTimeout = this.sessionTimeout;

		this.sessionTimeout = (timeout == 0) ? -1 : timeout;
		support.firePropertyChange("sessionTimeout", new Integer(oldSessionTimeout), new Integer(this.sessionTimeout));

	}

	/**
	 * Return the <code>Service</code> with which we are associated (if any).
	 */
	public Service getService()
	{

		return (this.service);

	}

	/**
	 * Set the <code>Service</code> with which we are associated (if any).
	 *
	 * @param service The service that owns this Service
	 */
	public void setService(Service service)
	{
		this.service = service;
	}

	/**
	 * Add a child Context
	 *
	 * @param child Child context to be added
	 */
	public void addChild(Container child)
	{
		if (!(child instanceof Context))
		{
			throw new IllegalArgumentException(sm.getString("standardService.notContext"));
		}

		Context context = (Context) child;
		super.addChild(child);

	}

	/**
	 * Return the value of the deploy on startup flag.  If true, it indicates 
	 * that this Service's child webapps should be discovred and automatically 
	 * deployed at startup time.
	 */
	public boolean getDeployOnStartup()
	{

		return (this.deployOnStartup);

	}

	/**
	 * Set the deploy on startup flag value for this Service.
	 * 
	 * @param autoDeploy The new deploy on startup flag
	 */
	public void setDeployOnStartup(boolean deployOnStartup)
	{

		boolean oldDeployOnStartup = this.deployOnStartup;
		this.deployOnStartup = deployOnStartup;
		support.firePropertyChange("deployOnStartup", oldDeployOnStartup, this.deployOnStartup);

	}

	/**
	 * Return the SessionManager with which this Service is associated. 
	 */
	public Manager getManager()
	{
		return (manager);
	}

	/**
	 * Set the SessionManager with which this Service is associated.
	 *
	 * @param manager The newly associated SessionManager
	 */
	public synchronized void setManager(Manager manager)
	{

		// Change components if necessary
		Manager oldManager = this.manager;
		if (oldManager == manager)
		{
			return;
		}
		this.manager = manager;

		// Stop the old component if necessary
		if (started && (oldManager != null) && (oldManager instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) oldManager).stop();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setSessionManager: stop: ", e);
			}
		}

		// Start the new component if necessary
		if (manager != null)
		{
			manager.setService(this);
		}
		if (started && (manager != null) && (manager instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) manager).start();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setSessionManager: start: ", e);
			}
		}

		// Report this property change to interested listeners
		support.firePropertyChange("sessionManager", oldManager, this.manager);

	}

	/**
	 * Return the canonical, fully qualified, name of the engine
	 * this Container represents.
	 */
	public String getName()
	{

		return (name);

	}

	/**
	 * Set the canonical, fully qualified, name of the virtual Service
	 * this Container represents.
	 *
	 * @param name Virtual Service name
	 *
	 * @exception IllegalArgumentException if name is null
	 */
	public void setName(String name)
	{

		if (name == null)
		{
			throw new IllegalArgumentException(sm.getString("standardService.nullName"));
		}

		name = name.toLowerCase(); // Internally all names are lower case

		String oldName = this.name;
		this.name = name;
		support.firePropertyChange("name", oldName, this.name);

	}

	/**
	 * Unpack WARs flag accessor.
	 */
	public boolean isUnpackWARs()
	{

		return (unpackWARs);

	}

	/**
	 * Unpack WARs flag mutator.
	 */
	public void setUnpackWARs(boolean unpackWARs)
	{

		this.unpackWARs = unpackWARs;

	}

	/**
	* Set the validation feature of the XML parser used when
	* parsing xml instances.
	* @param xmlValidation true to enable xml instance validation
	*/
	public void setXmlValidation(boolean xmlValidation)
	{

		this.xmlValidation = xmlValidation;

	}

	/**
		* Get the server.xml <host> attribute's xmlValidation.
		* @return true if validation is enabled.
		*
		*/
	public boolean getXmlValidation()
	{
		return xmlValidation;
	}

	/**
		* Get the server.xml <host> attribute's xmlNamespaceAware.
		* @return true if namespace awarenes is enabled.
		*
		*/
	public boolean getXmlNamespaceAware()
	{
		return xmlNamespaceAware;
	}

	/**
		* Set the namespace aware feature of the XML parser used when
		* parsing xml instances.
		* @param xmlNamespaceAware true to enable namespace awareness
		*/
	public void setXmlNamespaceAware(boolean xmlNamespaceAware)
	{
		this.xmlNamespaceAware = xmlNamespaceAware;
	}

	/**
	 * Service work directory base.
	 */
	public String getWorkDir()
	{
		if (workDir != null)
		{
			return workDir;
		}

		workDir = org.huihoo.willow.Globals.WORK_DIRECTORY;

		return (workDir);
	}

	public File getAbsoluteWorkDir()
	{
		String absolute_workDir = getWorkDir();
		File absolute_workDirFile = new File(absolute_workDir);

		if (!absolute_workDirFile.isAbsolute())
		{
			absolute_workDirFile = new File(System.getProperty(Globals.PROPS_WILLOW_HOME), absolute_workDir);
		}
		return absolute_workDirFile;
	}

	/**
	 * Service work directory base.
	 */
	public void setWorkDir(String workDir)
	{

		this.workDir = workDir;
	}

	// --------------------------------------------------------- Public Methods

	/**
	 * Install the StandardContext portion of the DefaultContext
	 * configuration into current Context.
	 *
	 * @param context current web application context
	 */
	public void installDefaultContext(Context context)
	{

		if (defaultContext != null && defaultContext instanceof StandardDefaultContext)
		{

			((StandardDefaultContext) defaultContext).installDefaultContext(context);
		}

	}

	/**
	 * Import the DefaultContext config into a web application context.
	 *
	 * @param context web application context to import default context
	 */
	public void importDefaultContext(Context context)
	{

		if (this.defaultContext != null)
			this.defaultContext.importDefaultContext(context);

	}

	// --------------------------------------------------------- Public Methods

	/**
	 * Return descriptive information about this Service implementation and
	 * the corresponding version number, in the format
	 * <code>&lt;description&gt;/&lt;version&gt;</code>.
	 */
	public String getInfo()
	{

		return (info);

	}

	// ------------------------------------------------------- ServiceDeployer Methods

	/**
	 * Install a new web application, whose web application archive is at the
	 * specified URL, into this container with the specified context path.
	 * A context path of "" (the empty string) should be used for the root
	 * application for this container.  Otherwise, the context path must
	 * start with a slash.
	 * <p>
	 * If this application is successfully installed, a ContainerEvent of type
	 * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
	 * with the newly created <code>ParameterContext</code> as an argument.
	 *
	 * @param contextName The context path to which this application should
	 *  be installed (must be unique)
	 * @param war A URL of type "jar:" that points to a WAR file, or type
	 *  "file:" that points to an unpacked directory structure containing
	 *  the web application to be installed
	 *
	 * @exception IllegalArgumentException if the specified context path
	 *  is malformed (it must be "" or start with a slash)
	 * @exception IllegalStateException if the specified context path
	 *  is already attached to an existing web application
	 * @exception IOException if an input/output error was encountered
	 *  during installation
	 */
	public void install(String contextName, URL war) throws IOException
	{
		getDeployer().install(contextName, war);
	}

	/**
	 * Return the Context for the deployed application that is associated
	 * with the specified context path (if any); otherwise return
	 * <code>null</code>.
	 *
	 * @param contextName The context path of the requested web application
	 */
	public Context findDeployedApp(String contextName)
	{

		return (getDeployer().findDeployedApp(contextName));

	}

	/**
	 * Return the context paths of all deployed web applications in this
	 * Container.  If there are no deployed applications, a zero-length
	 * array is returned.
	 */
	public String[] findDeployedApps()
	{

		return (getDeployer().findDeployedApps());

	}

	/**
	 * Remove an existing web application, attached to the specified context
	 * path.  If this application is successfully removed, a
	 * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
	 * registered listeners, with the removed <code>Context</code> as
	 * an argument.
	 *
	 * @param contextName The context path of the application to be removed
	 *
	 * @exception IllegalArgumentException if the specified context path
	 *  is malformed (it must be "" or start with a slash)
	 * @exception IllegalArgumentException if the specified context path does
	 *  not identify a currently installed web application
	 * @exception IOException if an input/output error occurs during
	 *  removal
	 */
	public void remove(String contextName) throws IOException
	{

		getDeployer().remove(contextName);

	}

	/**
	 * Remove an existing web application, attached to the specified context
	 * path.  If this application is successfully removed, a
	 * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
	 * registered listeners, with the removed <code>Context</code> as
	 * an argument. Deletes the web application war file and/or directory
	 * if they exist in the Service's appBase.
	 *
	 * @param contextName The context path of the application to be removed
	 * @param undeploy boolean flag to remove web application from server
	 *
	 * @exception IllegalArgumentException if the specified context path
	 *  is malformed (it must be "" or start with a slash)
	 * @exception IllegalArgumentException if the specified context path does
	 *  not identify a currently installed web application
	 * @exception IOException if an input/output error occurs during
	 *  removal
	 */
	public void remove(String contextName, boolean undeploy) throws IOException
	{

		getDeployer().remove(contextName, undeploy);

	}

	/**
	 * Start an existing web application, attached to the specified context
	 * path.  Only starts a web application if it is not running.
	 *
	 * @param contextName The context path of the application to be started
	 *
	 * @exception IllegalArgumentException if the specified context path
	 *  is malformed (it must be "" or start with a slash)
	 * @exception IllegalArgumentException if the specified context path does
	 *  not identify a currently installed web application
	 * @exception IOException if an input/output error occurs during
	 *  startup
	 */
	public void start(String contextName) throws IOException
	{

		getDeployer().start(contextName);

	}

	/**
	 * Stop an existing web application, attached to the specified context
	 * path.  Only stops a web application if it is running.
	 *
	 * @param contextName The context path of the application to be stopped
	 *
	 * @exception IllegalArgumentException if the specified context path
	 *  is malformed (it must be "" or start with a slash)
	 * @exception IllegalArgumentException if the specified context path does
	 *  not identify a currently installed web application
	 * @exception IOException if an input/output error occurs while stopping
	 *  the web application
	 */
	public void stop(String contextName) throws IOException
	{

		getDeployer().stop(contextName);

	}

	/**
	 * Execute a periodic task, such as reloading, etc. This method will be
	 * invoked inside the classloading context of this container. Unexpected
	 * throwables will be caught and logged.
	 */
	public void backgroundProcess()
	{
		lifecycle.fireLifecycleEvent("check", null);

		count = (count + 1) % managerChecksFrequency;

		if ((getManager() != null) && (count == 0))
		{
			if (getManager() instanceof StandardManager)
			{
				((StandardManager) getManager()).processExpires();
			}
		}
	}
	// ------------------------------------------------------ Protected Methods

	static String STANDARD_SERVICE_DEPLOYER = "org.huihoo.willow.core.StandardServiceDeployer";

	public Deployer getDeployer()
	{
		if (deployer != null)
		{
			return deployer;
		}

		log.info("Create Service deployer for direct deployment ");

		try
		{
			Class c = Class.forName(STANDARD_SERVICE_DEPLOYER);
			deployer = (Deployer) c.newInstance();
			Method m = c.getMethod("setService", new Class[] { Service.class });
			m.invoke(deployer, new Object[] { this });
		}
		catch (Throwable t)
		{
			log.error("Error creating deployer ", t);
		}
		return deployer;
	}

	public void setDeployer(Deployer d)
	{
		this.deployer = d;
	}
	// --------------------------------------------------------- Public Methods

	/**
	 * Add a new Connector to the set of defined Connectors, and associate it
	 * with this Service's Container.
	 *
	 * @param connector The Connector to be added
	 */
	public void addConnector(Connector connector)
	{
		synchronized (connector)
		{
			connector.setService(this);
			Connector results[] = new Connector[connectors.length + 1];
			System.arraycopy(connectors, 0, results, 0, connectors.length);
			results[connectors.length] = connector;
			connectors = results;

			if (initialized)
			{
				try
				{
					connector.initialize();
				}
				catch (LifecycleException e)
				{
					e.printStackTrace(System.err);
				}
			}

			if (started && (connector instanceof Lifecycle))
			{
				try
				{
					((Lifecycle) connector).start();
				}
				catch (LifecycleException e)
				{
					e.printStackTrace(System.err);
				}
			}

			// Report this property change to interested listeners
			support.firePropertyChange("connector", null, connector);
		}

	}

	/**
	 * Find and return the set of Connectors associated with this Service.
	 */
	public Connector[] findConnectors()
	{

		return (connectors);

	}

	/**
	 * Remove the specified Connector from the set associated from this
	 * Service.  The removed Connector will also be disassociated from our
	 * Container.
	 *
	 * @param Connector The Connector to be removed
	 */
	public void removeConnector(Connector connector)
	{

		synchronized (connectors)
		{
			int j = -1;
			for (int i = 0; i < connectors.length; i++)
			{
				if (connector == connectors[i])
				{
					j = i;
					break;
				}
			}
			if (j < 0)
				return;
			if (started && (connectors[j] instanceof Lifecycle))
			{
				try
				{
					((Lifecycle) connectors[j]).stop();
				}
				catch (LifecycleException e)
				{
					;
				}
			}

			connector.setService(null);
			int k = 0;
			Connector results[] = new Connector[connectors.length - 1];
			for (int i = 0; i < connectors.length; i++)
			{
				if (i != j)
					results[k++] = connectors[i];
			}
			connectors = results;

			// Report this property change to interested listeners
			support.firePropertyChange("connector", connector, null);
		}

	}

	/**
	 * Remove a property change listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	public void removePropertyChangeListener(PropertyChangeListener listener)
	{

		support.removePropertyChangeListener(listener);

	}

	/**
	 * Return a String representation of this component.
	 */
	public String toString()
	{

		StringBuffer sb = new StringBuffer("StandardService[");
		sb.append(getName());
		sb.append("]");
		return (sb.toString());

	}

	/**
	 * Prepare for the beginning of active use of the public methods of this
	 * component.  This method should be called before any of the public
	 * methods of this component are utilized.  It should also send a
	 * LifecycleEvent of type START_EVENT to any registered listeners.
	 *
	 * @exception LifecycleException if this component detects a fatal error
	 *  that prevents this component from being used
	 */
	public void start() throws LifecycleException
	{
		// Validate and update our current component state
		if (started)
		{
			return;
		}

		if (server == null)
		{
			ServerFactory.getServer().addService(this);
		}

		// Log our server identification information
		log.info("Starting  Service: " + ServerInfo.getServerInfo());

		if (manager == null)
		{
			manager = new StandardManager();
			manager.setService(this);
		}

		if ((manager != null) && (manager instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) manager).start();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setSessionManager: start: ", e);
			}
		}

		if ((realmDatabase != null) && (realmDatabase instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) realmDatabase).start();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setRealmDatabase: start: ", e);
			}
		}

		// Start our defined Connectors second
		synchronized (connectors)
		{
			for (int i = 0; i < connectors.length; i++)
			{
				if (connectors[i] instanceof Lifecycle)
				{
					((Lifecycle) connectors[i]).start();
				}
			}
		}

		// Standard container startup
		super.start();
	}

	/**
	 * Gracefully terminate the active use of the public methods of this
	 * component.  This method should be the last one called on a given
	 * instance of this component.  It should also send a LifecycleEvent
	 * of type STOP_EVENT to any registered listeners.
	 *
	 * @exception LifecycleException if this component detects a fatal error
	 *  that needs to be reported
	 */
	public void stop() throws LifecycleException
	{
		// Validate and update our current component state
		if (!started)
		{
			return;
		}

		// Stop our defined Connectors first
		synchronized (connectors)
		{
			for (int i = 0; i < connectors.length; i++)
			{
				if (connectors[i] instanceof Lifecycle)
				{
					((Lifecycle) connectors[i]).stop();
				}
			}
		}

		if ((realmDatabase != null) && (realmDatabase instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) realmDatabase).stop();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setRealmDatabase: stop: ", e);
			}
		}

		if ((manager != null) && (manager instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) manager).start();
			}
			catch (LifecycleException e)
			{
				log.error("StandardService.setSessionManager: stop: ", e);
			}
		}

		super.stop();
	}

	/**
	 * Invoke a pre-startup initialization. This is used to allow connectors
	 * to bind to restricted ports under Unix operating environments.
	 */
	public void initialize() throws LifecycleException
	{
		// Service shouldn't be used with embeded, so it doesn't matter
		if (initialized)
		{
			log.info(sm.getString("standardService.initialize.initialized"));
			return;
		}
		initialized = true;

		if (server == null)
		{
			// Register with the server 
			// HACK: ServerFactory should be removed...
			ServerFactory.getServer().addService(this);
		}

		// Initialize our defined Connectors
		synchronized (connectors)
		{
			for (int i = 0; i < connectors.length; i++)
			{
				connectors[i].initialize();
			}
		}
	}
}