//----------------------------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.bootstrap;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;

import org.huihoo.willow.Globals;

/**
 * Boostrap loader for Willow.  This application constructs a class loader
 * for use in loading the Willow internal classes (by accumulating all of the
 * JAR files found in the "server" directory under XPDLGlobals.PROPS_WILLOW_HOME), and
 * starts the regular execution of the container.  The purpose of this
 * roundabout approach is to keep the Willow internal classes (and any
 * other classes they depend on, such as an XML parser) out of the system
 * class path and therefore not visible to application level classes.
 *
 */

public final class Bootstrap
{

	// -------------------------------------------------------------- Constants

	protected static final String WILLOW_HOME_TOKEN = "${willow.home}";
	protected static final String WILLOW_BASE_TOKEN = "${willow.base}";

	public final static String COMMON_REPOSITORY = "common";
	public final static String SERVER_REPOSITORY = "server";
	public final static String SHARED_REPOSITORY = "shared";
	
	// ------------------------------------------------------- Static Variables

	/**
	 * Daemon object used by main.
	 */
	private static Bootstrap daemon = null;

	// -------------------------------------------------------------- Variables

	/**
	 * Debugging detail level for processing the startup.
	 */
	protected int debug = 0;

	/**
	 * Daemon reference.
	 */
	private Object willowDaemon = null;

	protected ClassLoader commonLoader = null;
	protected ClassLoader willowLoader = null;
	protected ClassLoader sharedLoader = null;
	
	// -------------------------------------------------------- Private Methods
	
	private void initClassLoaders()
	{
		try
		{
			ClassLoaderFactory.setDebug(debug);
			
			commonLoader = createClassLoader(COMMON_REPOSITORY, null);
			willowLoader = createClassLoader(SERVER_REPOSITORY,commonLoader);
			sharedLoader = createClassLoader(SHARED_REPOSITORY,commonLoader);
		}
		catch (Throwable t)
		{
			log("Class loader creation threw exception", t);
			System.exit(1);
		}
	}

	private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception
	{

		String value = WillowProperties.getProperty(name + ".loader");
		
		if ((value == null) || (value.equals("")))
		{
			return parent;
		}

		ArrayList unpackedList = new ArrayList();
		ArrayList packedList = new ArrayList();
		ArrayList urlList = new ArrayList();

		StringTokenizer tokenizer = new StringTokenizer(value, ",");
		while (tokenizer.hasMoreElements())
		{
			String repository = tokenizer.nextToken();
			// Check for a JAR URL repository
			try
			{
				urlList.add(new URL(repository));
				continue;
			}
			catch (MalformedURLException e)
			{
				// Ignore
			}
			// Local repository
			boolean packed = false;
			
			if (repository.startsWith(WILLOW_HOME_TOKEN))
			{
				repository = getHome() + repository.substring(WILLOW_HOME_TOKEN.length());
			}

			if (repository.endsWith("*.jar"))
			{
				packed = true;
				repository = repository.substring(0, repository.length() - "*.jar".length());
			}
			if (packed)
			{
				packedList.add(new File(repository));
			}
			else
			{
				unpackedList.add(new File(repository));
			}
		}
		
		File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
		File[] packed = (File[]) packedList.toArray(new File[0]);
				
		return ClassLoaderFactory.createClassLoader(unpacked, packed, parent);

	}
	
	/**
	 * Initialize daemon.
	 */
	public void init() throws Exception
	{

		// Set Willow path
		setHome();
		
		File homeFile=new File(getHome());
		this.setHome(homeFile.getCanonicalPath());			
		
		initClassLoaders();

		Thread.currentThread().setContextClassLoader(willowLoader);
		SecurityClassLoad.securityClassLoad(willowLoader);
		
		// Load our startup class and call its process() method
		if (debug >= 1)
		{	
			log("Loading startup class");		
		}
		
		Class startupClass = ((StandardClassLoader)willowLoader).loadClass("org.huihoo.willow.startup.Willow");
		
		Object startupInstance = startupClass.newInstance();

		// Set the shared extensions class loader
		if (debug >= 1)
		{	
			log("Setting startup class properties");			
		}
		
		String methodName = "setParentClassLoader";
		Class paramTypes[] = new Class[1];
		paramTypes[0] = Class.forName("java.lang.ClassLoader");
		Object paramValues[] = new Object[1];
		paramValues[0] = sharedLoader;
		Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
		method.invoke(startupInstance, paramValues);			
		willowDaemon = startupInstance;	
	}

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

	/**
	 * Start the Willow daemon.
	 */
	public void start() throws Exception
	{
		if (willowDaemon == null)
		{
			init();
		}

		Method method = willowDaemon.getClass().getMethod("start", null);
		method.invoke(willowDaemon, null);	
	}

	/**
	 * Stop the Willow Daemon.
	 */
	public void stop() throws Exception
	{

		Method method = willowDaemon.getClass().getMethod("stop", null);
		method.invoke(willowDaemon, null);

	}

	/**
	 * Stop the standlone server.
	 */
	public void stopServer() throws Exception
	{

		Method method = willowDaemon.getClass().getMethod("stopServer", null);
		method.invoke(willowDaemon, null);

	}

	/**
	 * Set flag.
	 */
	public void setAwait(boolean await) throws Exception
	{

		Class paramTypes[] = new Class[1];
		paramTypes[0] = Boolean.TYPE;
		Object paramValues[] = new Object[1];
		paramValues[0] = new Boolean(await);
		Method method = willowDaemon.getClass().getMethod("setAwait", paramTypes);
		method.invoke(willowDaemon, paramValues);

	}

	public boolean getAwait() throws Exception
	{
		Class paramTypes[] = new Class[0];
		Object paramValues[] = new Object[0];
		Method method = willowDaemon.getClass().getMethod("getAwait", paramTypes);
		Boolean b = (Boolean) method.invoke(willowDaemon, paramValues);
		return b.booleanValue();
	}

	/**
	 * Destroy the Willow Daemon.
	 */
	public void destroy()
	{

		// FIXME

	}
	/**
	 * Load daemon.
	 */
	private void load(String[] arguments) throws Exception
	{

		// Call the load() method
		String methodName = "load";
		Object param[];
		Class paramTypes[];
		if (arguments == null || arguments.length == 0)
		{
			paramTypes = null;
			param = null;
		}
		else
		{
			paramTypes = new Class[1];
			paramTypes[0] = arguments.getClass();
			param = new Object[1];
			param[0] = arguments;
		}
		Method method = willowDaemon.getClass().getMethod(methodName, paramTypes);
		if (debug >= 1)
			log("Calling startup class " + method);
		method.invoke(willowDaemon, param);

	}
	/**
	 * Main method, used for testing only.
	 *
	 * @param args Command line arguments to be processed
	 */
	public static void main(String args[])
	{

		if (daemon == null)
		{
			daemon = new Bootstrap();
			try
			{
				daemon.init();
			}
			catch (Throwable t)
			{
				t.printStackTrace();
				return;
			}
		}

		try
		{
			String command = "start";
			if (args.length > 0)
			{
				command = args[args.length - 1];
			}

			if (command.equals("startd"))
			{
				args[0] = "start";
				daemon.load(args);
				daemon.start();
			}
			else if (command.equals("stopd"))
			{
				args[0] = "stop";
				daemon.stop();
			}
			else if (command.equals("start"))
			{
				daemon.setAwait(true);
				daemon.load(args);
				daemon.start();
			}
			else if (command.equals("stop"))
			{
				daemon.stopServer();
			}
		}
		catch (Throwable t)
		{
			t.printStackTrace();
		}

	}

	public void setHome(String s)
	{
		System.setProperty(Globals.PROPS_WILLOW_HOME, s);
	}

	/**
	 * Set the <code>willow.home</code> System property to the current
	 * working directory if it has not been set.
	 */
	private void setHome()
	{

		if (System.getProperty(Globals.PROPS_WILLOW_HOME) != null)
		{
			return;
		}
		File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar");
		if (bootstrapJar.exists())
		{
			try
			{
				System.setProperty(
					Globals.PROPS_WILLOW_HOME,
					(new File(System.getProperty("user.dir"), "..")).getCanonicalPath());
			}
			catch (Exception e)
			{
				// Ignore
				System.setProperty(Globals.PROPS_WILLOW_HOME, System.getProperty("user.dir"));
			}
		}
		else
		{
			System.setProperty(Globals.PROPS_WILLOW_HOME, System.getProperty("user.dir"));
		}

	}

	/**
	 * Get the value of the willow.home environment variable.
	 */
	public static String getHome()
	{
		return System.getProperty(Globals.PROPS_WILLOW_HOME, System.getProperty("user.dir"));
	}

	/**
	 * Log a debugging detail message.
	 *
	 * @param message The message to be logged
	 */
	protected static void log(String message)
	{

		System.out.print("Bootstrap: ");
		System.out.println(message);

	}

	/**
	 * Log a debugging detail message with an exception.
	 *
	 * @param message The message to be logged
	 * @param exception The exception to be logged
	 */
	protected static void log(String message, Throwable exception)
	{

		log(message);
		exception.printStackTrace(System.out);

	}

}
