/*
 * Created on 2004-6-18
 *
 * 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.net.URL;
import java.util.ArrayList;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.EngineDeployer;
import org.huihoo.willow.Engine;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleEvent;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Logger;
import org.huihoo.willow.core.StandardEngine;
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 final class EngineConfig implements LifecycleListener
{

	// ----------------------------------------------------- Instance Variables
	private static Log log = LogFactory.getLog(EngineConfig.class);

	/**
	 * The Java class name of the Context configuration class we should use.
	 */
	protected String configClass = "org.huihoo.willow.startup.ContextConfig";

	/**
	 * The Java class name of the Context implementation we should use.
	 */
	protected String contextClass = "org.huihoo.willow.core.StandardContext";

	/**
	 * The debugging detail level for this component.
	 */
	private int debug = 0;

	/**
	 * The names of applications that we have auto-deployed (to avoid
	 * double deployment attempts).
	 */
	protected ArrayList deployed = new ArrayList();

	/**
	 * The Engine we are associated with.
	 */
	private Engine engine = null;

	/**
	 * The string resources for this package.
	 */
	private static final StringManager sm = StringManager.getManager(Constants.PACKAGE);

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

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

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

		return (this.debug);

	}

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

		this.debug = debug;

	}

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

	/**
	 * Process the START event for an associated Engine.
	 *
	 * @param event The lifecycle event that has occurred
	 */
	public void lifecycleEvent(LifecycleEvent event)
	{
		if (event.getType().equals("check"))
		{
			check();
		}
		// Identify the engine we are associated with
		try
		{
			engine = (Engine) event.getLifecycle();
			if (engine instanceof StandardEngine)
			{
				int engineDebug = ((StandardEngine) engine).getDebug();
				if (engineDebug > this.debug)
				{
					this.debug = engineDebug;
				}
			}
		}
		catch (ClassCastException e)
		{
			log(sm.getString("engineConfig.cce", event.getLifecycle()), e);
			return;
		}

		// Process the event that has occurred
		if (event.getType().equals(Lifecycle.START_EVENT))
		{
			start();
		}
		else if (event.getType().equals(Lifecycle.STOP_EVENT))
		{
			stop();
		}

	}

	// -------------------------------------------------------- Private Methods

	/**
	 * Log a message on the Logger associated with our Engine (if any)
	 *
	 * @param message Message to be logged
	 */
	private void log(String message)
	{

		Logger logger = null;
		if (engine != null)
			logger = engine.getLogger();
		if (logger != null)
			logger.log("EngineConfig: " + message);
		else
			System.out.println("EngineConfig: " + message);

	}

	/**
	 * Log a message on the Logger associated with our Engine (if any)
	 *
	 * @param message Message to be logged
	 * @param throwable Associated exception
	 */
	private void log(String message, Throwable throwable)
	{

		Logger logger = null;
		if (engine != null)
			logger = engine.getLogger();
		if (logger != null)
			logger.log("EngineConfig: " + message, throwable);
		else
		{
			System.out.println("EngineConfig: " + message);
			System.out.println("" + throwable);
			throwable.printStackTrace(System.out);
		}

	}

	/**
	 * Process a "start" event for this Engine.
	 */
	private void start()
	{
		if (debug > 0)
		{
			log(sm.getString("engineConfig.start"));
		}
		
		if (engine.getDeployOnStartup())
		{
			deployApps();
		}
	}

	/**
	 * Process a "stop" event for this Engine.
	 */
	private void stop()
	{

		if (debug > 0)
		{
			log(sm.getString("engineConfig.stop"));
		}

	}

	/**
	 * Undeploy all deployed applications.
	 */
	protected void undeployApps()
	{

		if (!(engine instanceof EngineDeployer))
		{
			return;
		}
		log.debug(sm.getString("engineConfig.undeploying"));

		String contextPaths[] = ((EngineDeployer) engine).findDeployedApps();
		for (int i = 0; i < contextPaths.length; i++)
		{
			log.debug(sm.getString("engineConfig.undeploy", contextPaths[i]));
			try
			{
				((EngineDeployer) engine).remove(contextPaths[i]);
			}
			catch (Throwable t)
			{
				log.error(sm.getString("engineConfig.undeploy.error", contextPaths[i]), t);
			}
		}
		deployed.clear();

	}

	/**
	 * Deploy webapps.
	 */
	protected void check()
	{
		if (engine.getAutoDeploy())
		{
			// Deploy apps if the Host allows auto deploying
			deployApps();
		}
	}
	/**
	 * Deploy applications for any directories or WAR files that are found
	 * in our "application root" directory.
	 */
	protected void deployApps()
	{

		if (!(engine instanceof EngineDeployer))
		{
			return;
		}

		File engineBase = engine.getAbsoluteEngineBase();

		if (!engineBase.exists() || !engineBase.isDirectory())
		{
			return;
		}

		String files[] = engineBase.list();
		deployDirectories(engineBase, files);

	}

	/**
	 * Deploy directories.
	 */
	protected void deployDirectories(File appBase, String[] files)
	{
		for (int i = 0; i < files.length; i++)
		{
			String contextName = files[i];
			if (deployed.contains(files[i]))
			{
				if (engine.findChild(contextName) != null)
				{
					engine.findChild(contextName).backgroundProcess();
				}
				continue;
			}

			File dir = new File(appBase, files[i]);
			if (dir.isDirectory())
			{

				deployed.add(files[i]);

				// Make sure there is an application configuration directory
				// This is needed if the Context appBase is the same as the
				// web server document root to make sure only web applications
				// are deployed and not directories for web space.
				File xpdlInf = new File(dir, "/WEB-INF");
				if (!xpdlInf.exists() || !xpdlInf.isDirectory() || !xpdlInf.canRead())
				{
					continue;
				}

				// Calculate the context path and make sure it is unique				
				if (engine.findChild(contextName) != null)
				{
					continue;
				}

				// Deploy the application in this directory
				if (log.isDebugEnabled())
				{
					log.debug(sm.getString("engineConfig.deployDir", files[i]));
				}

				long t1 = System.currentTimeMillis();
				try
				{
					URL url = new URL("file", null, dir.getCanonicalPath());
					((EngineDeployer) engine).install(contextName, url);
				}
				catch (Throwable t)
				{
					log.error(sm.getString("engineConfig.deployDir.error", files[i]), t);
				}
				long t2 = System.currentTimeMillis();
				if ((t2 - t1) > 200)
				{
					log.debug("Deployed " + files[i] + " " + (t2 - t1));
				}
			}

		}

	}
}
