/*
 * Created on 2004-6-8
 *
 * To change the template for this generated file go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
package org.huihoo.willow.core;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.Engine;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Provider;
import org.huihoo.willow.Server;
import org.huihoo.willow.ServerFactory;
import org.huihoo.willow.Service;
import org.huihoo.willow.bootstrap.WebServerFactory;
import org.huihoo.willow.util.LifecycleSupport;
import org.huihoo.willow.util.StringManager;


/**
 * @author reic
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
public class StandardService implements Lifecycle, Service
{
	private static Log log = LogFactory.getLog(StandardService.class);

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

	/**
	 * The set of ServiceProviders associated with this Service.
	 */
	private Provider providers[] = new Provider[0];

	/**
	 * The Container associated with this Service.
	 */
	private Container container = null;

	/**
	 * The debugging detail level for this component.
	 */
	private int debug = 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 name of this service.
	 */
	private String name = null;

	/**
	 * The lifecycle event support for this component.
	 */
	private LifecycleSupport lifecycle = new LifecycleSupport(this);

	/**
	 * The string manager for this package.
	 */
	//private static final StringManager sm = StringManager.getManager(MatrixClient.PACKAGE);
	
	private static final StringManager sm = StringManager.getManager(Constants.PACKAGE);
	/**
	 * The <code>NamingServer</code> that owns this Service, if any.
	 */
	private Server server = null;

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

	/**
	 * The property change support for this component.
	 */
	protected PropertyChangeSupport support = new PropertyChangeSupport(this);

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

	/**
	 * Return the <code>Container</code> that handles requests for all
	 * <code>ServiceProviders</code> associated with this Service.
	 */
	public Container getContainer()
	{

		return (this.container);

	}

	/**
	 * Set the <code>Container</code> that handles requests for all
	 * <code>ServiceProviders</code> associated with this Service.
	 *
	 * @param container The new Container
	 */
	public void setContainer(Container container)
	{

		Container oldContainer = this.container;
		if ((oldContainer != null) && (oldContainer instanceof Engine))
			 ((Engine) oldContainer).setService((Service)null);
		{
			this.container = container;
		}
		
		if ((this.container != null) && (this.container instanceof Engine))
		{
				 ((Engine) this.container).setService(this);
		}
		
		if (started && (this.container != null) && (this.container instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) this.container).start();
			}
			catch (LifecycleException e)
			{
				;
			}
		}
		
		if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle))
		{
			try
			{
				((Lifecycle) oldContainer).stop();
			}
			catch (LifecycleException e)
			{
				;
			}
		}

		// Report this property change to interested listeners
		support.firePropertyChange("container", oldContainer, this.container);

	}

	/**
	 * Return the debugging detail level of this component.
	 */
	public int getDebug()
	{

		return (this.debug);

	}

	/**
	 * Set the debugging detail level of this component.
	 *
	 * @param debug The new debugging detail level
	 */
	public void setDebug(int debug)
	{

		this.debug = debug;

	}

	/**
	 * 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 (this.info);

	}

	/**
	 * Return the name of this Service.
	 */
	public String getName()
	{

		return (this.name);

	}

	/**
	 * Set the name of this Service.
	 *
	 * @param name The new service name
	 */
	public void setName(String name)
	{

		this.name = name;

	}

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

		return (this.server);

	}

	/**
	 * Set the <code>NamingServer</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 Methods

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

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

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

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

	}

	/**
	 * Add a property change listener to this component.
	 *
	 * @param listener The listener to add
	 */
	public void addPropertyChangeListener(PropertyChangeListener listener)
	{

		support.addPropertyChangeListener(listener);

	}

	/**
	 * Find and return the set of ServiceProviders associated with this Service.
	 */
	public Provider[] findProviders()
	{

		return (providers);

	}

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

		synchronized (providers)
		{
			int j = -1;
			for (int i = 0; i < providers.length; i++)
			{
				if (provider == providers[i])
				{
					j = i;
					break;
				}
			}
			if (j < 0)
				return;
			if (started && (providers[j] instanceof Lifecycle))
			{
				try
				{
					((Lifecycle) providers[j]).stop();
				}
				catch (LifecycleException e)
				{
					;
				}
			}
			provider.setContainer(null);
			provider.setService(null);
			int k = 0;
			Provider results[] = new Provider[providers.length - 1];
			for (int i = 0; i < providers.length; i++)
			{
				if (i != j)
					results[k++] = providers[i];
			}
			providers = results;

			// Report this property change to interested listeners
			support.firePropertyChange("provider", provider, 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());

	}

	// ------------------------------------------------------ Lifecycle Methods

	/**
	 * Add a LifecycleEvent listener to this component.
	 *
	 * @param listener The listener to add
	 */
	public void addLifecycleListener(LifecycleListener listener)
	{

		lifecycle.addLifecycleListener(listener);

	}

	/**
	 * Get the lifecycle listeners associated with this lifecycle. If this 
	 * Lifecycle has no listeners registered, a zero-length array is returned.
	 */
	public LifecycleListener[] findLifecycleListeners()
	{

		return lifecycle.findLifecycleListeners();

	}

	/**
	 * Remove a LifecycleEvent listener from this component.
	 *
	 * @param listener The listener to remove
	 */
	public void removeLifecycleListener(LifecycleListener listener)
	{

		lifecycle.removeLifecycleListener(listener);

	}

	/**
	 * 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)
		{
			log.info(sm.getString("standardService.start.started"));
		}

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

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

		lifecycle.fireLifecycleEvent(START_EVENT, null);		
		
		started = true;

		// Start our defined ServiceProviders second
		synchronized (providers)
		{			
			for (int i = 0; i < providers.length; i++)
			{
				if(providers[i].getContainer()==null)
				{
					providers[i].setContainer(this.getContainer());
				}
				
				if (providers[i] instanceof Lifecycle)
				{ 					
					((Lifecycle) providers[i]).start();				
				}
			}
		}
		
		// Start our defined Container first
		if (container != null)
		{
			synchronized (container)
			{
				if (container instanceof Lifecycle)
				{
					((Lifecycle) container).start();
				}
			}
		}

//-------------------------------------------------------------------------------
//--INSTALL WEB CONTAINER		
		
		String serverFactory=System.getProperty("webserver.factory");			
		if(serverFactory!=null && !"".equals(serverFactory.trim()))
		{
			log.info("*******************************INSTALL WEB SERVER*******************************");
			try
			{
				Class clazz=StandardServer.class.getClassLoader().loadClass(serverFactory);
				WebServerFactory webserverFactory=(WebServerFactory)clazz.newInstance();				
				String providerClassName=	webserverFactory.findProviderClass();				
				Class providerClass=StandardServer.class.getClassLoader().loadClass(providerClassName);		
				Provider provider=(Provider)providerClass.newInstance();
				addProvider(provider);
			}
			catch (Throwable t)
			{
				log.error("WebServerFactory creation threw exception", t);
			}			
		}
//	-------------------------------------------------------------------------------
		
		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

	}

	/**
	 * 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;
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

		lifecycle.fireLifecycleEvent(STOP_EVENT, null);

		log.info(sm.getString("standardService.stop.name", this.name));
		started = false;

		// Stop our defined Container second
		if (container != null)
		{
			synchronized (container)
			{
				if (container instanceof Lifecycle)
				{
					((Lifecycle) container).stop();
				}
			}
		}
		
		// Stop our defined ServiceProviders first
		synchronized (providers)
		{
			for (int i = 0; i < providers.length; i++)
			{
				if (providers[i] instanceof Lifecycle)
				{
					((Lifecycle) providers[i]).stop();
				}
			}
		}

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

	}
}