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

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.commons.digester.Digester;
import org.apache.commons.digester.Rule;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.Server;
import org.huihoo.willow.core.StandardServer;
import org.huihoo.willow.store.DatabaseLoader;
import org.huihoo.willow.util.WillowDigester;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;

import com.zosatapo.commons.util.log.SystemLogHandler;
import com.zosatapo.workflow.wfmc.xpdl.parser.xmlrules.SetAllPropertiesRule;

/**
 * @author zosatapo
 *
 * To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Generation - Code and Comments
 */
public class Willow extends Embedded
{
	private static Log log = LogFactory.getLog(Willow.class);
	
	// ----------------------------------------------------- Instance Variables
	/**
	 * Pathname to the server configuration file.
	 */
	protected String configFile = "conf/server.xml";

	/**
	 * The server component we are starting or stopping
	 */
	protected Server server = null;

	/**
	 * Are we starting a new server?
	 */
	protected boolean starting = false;

	/**
	 * Are we stopping an existing server?
	 */
	protected boolean stopping = false;

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

	public void setConfig(String file)
	{
		configFile = file;
	}

	public void setConfigFile(String file)
	{
		configFile = file;
	}

	public String getConfigFile()
	{
		return configFile;
	}


	/**
	 * Set the server instance we are configuring.
	 *
	 * @param server The new server
	 */
	public void setServer(Server server)
	{

		this.server = server;

	}
	
	// ----------------------------------------------------------- Main Program

	/**
	 * The application main program.
	 *
	 * @param args Command line arguments
	 */
	public static void main(String args[])
	{
		(new Willow()).process(args);
	}

	/**
	 * The instance main program.
	 *
	 * @param args Command line arguments
	 */
	public void process(String args[])
	{

		setAwait(true);
		setHome();
		try
		{
			if (arguments(args))
			{
				if (starting)
				{
					load(args);
					start();
				}
				else if (stopping)
				{
					stopServer();
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace(System.out);
		}
	}

	// ------------------------------------------------------ Protected Methods

	/**
	 * Process the specified command line arguments, and return
	 * <code>true</code> if we should continue processing; otherwise
	 * return <code>false</code>.
	 *
	 * @param args Command line arguments to process
	 */
	protected boolean arguments(String args[])
	{

		boolean isConfig = false;

		if (args.length < 1)
		{
			usage();
			return (false);
		}

		for (int i = 0; i < args.length; i++)
		{
			if (isConfig)
			{
				configFile = args[i];
				isConfig = false;
			}
			else if (args[i].equals("-config"))
			{
				isConfig = true;
			}
			else if (args[i].equals("-help"))
			{
				usage();
				return (false);
			}
			else if (args[i].equals("start"))
			{
				starting = true;
				stopping = false;
			}
			else if (args[i].equals("stop"))
			{
				starting = false;
				stopping = true;
			}
			else
			{
				usage();
				return (false);
			}
		}

		return (true);

	}

	/**
	 * Return a File object representing our configuration file.
	 */
	protected File configFile()
	{

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

	}

	/**
	 * Create and configure the Digester we will be using for startup.
	 */
	protected Digester createStartDigester()
	{
		long t1 = System.currentTimeMillis();
		// Initialize the digester
		Digester digester = new WillowDigester();
		if (debug > 0)
		{
			digester.setDebug(debug);
		}
		digester.setValidating(false);
		digester.setClassLoader(StandardServer.class.getClassLoader());

		// Configure the actions we will be using
		digester.addObjectCreate("Server", "org.huihoo.willow.core.StandardServer", "className");
		digester.addSetProperties("Server");
		digester.addSetNext("Server", "setServer", "org.huihoo.willow.Server");

		digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element
		"className");
		digester.addSetProperties("Server/Listener");
		digester.addSetNext(
			"Server/Listener",
			"addLifecycleListener",
			"org.huihoo.willow.LifecycleListener");

		digester.addObjectCreate(
			"Server/Service",
			"org.huihoo.willow.core.StandardService",
			"className");
		digester.addSetProperties("Server/Service");
		digester.addSetNext("Server/Service", "addService", "org.huihoo.willow.Service");

		digester.addObjectCreate("Server/Service/Listener", null, // MUST be specified in the element
		"className");
		digester.addSetProperties("Server/Service/Listener");
		digester.addSetNext(
			"Server/Service/Listener",
			"addLifecycleListener",
			"org.huihoo.willow.LifecycleListener");

		// Add RuleSets for nested elements
		digester.addRuleSet(new EngineRuleSet("Server/Service/"));
		digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/"));

		// When the 'engine' is found, set the parentClassLoader.
		digester.addRule(
			"Server/Service/Engine",
			new SetParentClassLoaderRule(digester, parentClassLoader));

		digester.addObjectCreate(
			"Server/Service/NamingProvider",
			"org.huihoo.willow.core.StandardNamingProvider",
			"className");
		digester.addRule("Server/Service/NamingProvider", new SetAllPropertiesRule());
		digester.addSetNext(
			"Server/Service/NamingProvider",
			"addProvider",
			"org.huihoo.willow.Provider");

		digester.addObjectCreate("Server/Service/NamingProvider/Listener", null,
		// MUST be specified in the element
		"className");
		digester.addSetProperties("Server/Service/NamingProvider/Listener");
		digester.addSetNext(
			"Server/Service/NamingProvider/Listener",
			"addLifecycleListener",
			"org.huihoo.willow.LifecycleListener");
			
		long t2 = System.currentTimeMillis();
		log.debug("Digester for server.xml created " + (t2 - t1));
		return (digester);

	}

	/**
	 * Create and configure the Digester we will be using for shutdown.
	 */
	protected Digester createStopDigester()
	{

		// Initialize the digester
		Digester digester = new Digester();
		if (debug > 0)
		{
			digester.setDebug(debug);
		}

		// Configure the rules we need for shutting down
		digester.addObjectCreate("Server", "org.huihoo.willow.core.StandardServer", "className");
		digester.addSetProperties("Server");
		digester.addSetNext("Server", "setServer", "org.huihoo.willow.Server");

		return (digester);

	}

	public void stopServer()
	{

		if (server == null)
		{
			// Create and execute our Digester
			Digester digester = createStopDigester();
			digester.setClassLoader(Thread.currentThread().getContextClassLoader());
			File file = configFile();
			try
			{
				InputSource is = new InputSource("file://" + file.getAbsolutePath());
				FileInputStream fis = new FileInputStream(file);
				is.setByteStream(fis);
				digester.push(this);
				digester.parse(is);
				fis.close();	
			}
			catch (Exception e)
			{
				System.out.println("Willow.stop: " + e);
				e.printStackTrace(System.out);
				System.exit(1);
			}
		}

		// Stop the existing server
		try
		{
			Socket socket = new Socket("127.0.0.1", server.getPort());
			OutputStream stream = socket.getOutputStream();
			String shutdown = server.getShutdown();
			for (int i = 0; i < shutdown.length(); i++)
				stream.write(shutdown.charAt(i));
			stream.flush();
			stream.close();
			socket.close();
		}
		catch (IOException e)
		{
			System.out.println("Willow.stop: " + e);
			e.printStackTrace(System.out);
			System.exit(1);
		}

	}

	/**
	 * Set the <code>catalina.home</code> System property to the current
	 * working directory if it has not been set.
	 * @deprecated Use initDirs()
	 */
	public void setHome()
	{
		initDirs();
	}

	/**
	 * Start a new server instance.
	 */
	public void load()
	{
		initDirs();

		// Create and execute our Digester
		Digester digester = createStartDigester();
		long t1 = System.currentTimeMillis();

		InputSource inputSource = null;
		InputStream inputStream = null;
		try
		{
			File file = configFile();
			inputStream = new FileInputStream(file);
			inputSource = new InputSource("file://" + file.getAbsolutePath());
			log.info("Server configuration :"+file.getAbsolutePath());
		}
		catch (Exception e)
		{
			;
		}
		if (inputStream == null)
		{
			try
			{
				inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
				inputSource =
					new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
			}
			catch (Exception e)
			{
				;
			}
		}

		if (inputStream == null)
		{
			System.out.println("Can't load server.xml");
			return;
		}

		try
		{
			inputSource.setByteStream(inputStream);
			digester.push(this);
			digester.parse(inputSource);
			inputStream.close();
		}
		catch (Exception e)
		{
			System.out.println("Willow.start: " + e);
			e.printStackTrace(System.out);
			return;
		}

		// Replace System.out and System.err with a custom PrintStream
		SystemLogHandler systemlog = new SystemLogHandler(System.out);
		System.setOut(systemlog);
		System.setErr(systemlog);
		
		DatabaseLoader.loadOnStartup(Constants.DatabaseXml);
		long t2 = System.currentTimeMillis();
		log.info("Initialization processed in " + (t2 - t1) + " ms");

	}

	/* 
	 * Load using arguments
	 */
	public void load(String args[])
	{

		setHome();
		try
		{
			if (arguments(args))
			{
				load();
			}
		}
		catch (Exception e)
		{
			e.printStackTrace(System.out);
		}
	}

	/**
	 * Start a new server instance.
	 */
	public void start()
	{
		if (server == null)
		{
			load();
		}

		long t1 = System.currentTimeMillis();

		// Start the new server
		if (server instanceof Lifecycle)
		{
			try
			{
				((Lifecycle) server).start();
			}
			catch (LifecycleException e)
			{
				log.error("Willow.start: ", e);
			}
		}
//			
		long t2 = System.currentTimeMillis();
		log.info("Server startup in " + (t2 - t1) + " ms");
		
		if (await)
		{
			await();
			stop();
		}
	}

	/**
	 * Stop an existing server instance.
	 */
	public void stop()
	{
		// Shut down the server
		if (server instanceof Lifecycle)
		{
			try
			{
				((Lifecycle) server).stop();
			}
			catch (LifecycleException e)
			{
				log.error("Willow.stop", e);
			}
		}
		
		System.exit(-1);
	}

	/**
	 * Await and shutdown.
	 */
	public void await()
	{
		server.await();
	}

	/**
	 * Print usage information for this application.
	 */
	protected void usage()
	{

		System.out.println(
			"usage: java org.huihoo.willow.startup.Willow"
				+ " [ -config {pathname} ] [ -debug ]"
				+ " { start | stop }");

	}

// ------------------------------------------------------------ Private Classes
/**
 * Rule that sets the parent class loader for the top object on the stack,
 * which must be a <code>Container</code>.
 */

final class SetParentClassLoaderRule extends Rule
{

	public SetParentClassLoaderRule(Digester digester, ClassLoader parentClassLoader)
	{

		super(digester);
		this.parentClassLoader = parentClassLoader;

	}

	ClassLoader parentClassLoader = null;

	public void begin(Attributes attributes) throws Exception
	{

		if (digester.getDebug() >= 1)
		{
			digester.log("Setting parent class loader");
		}

		Container top = (Container) digester.peek();
		top.setParentClassLoader(parentClassLoader);

	}
}
}
