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

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.commons.util.IntrospectionUtils;
import org.huihoo.willow.Connector;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.bootstrap.ClassLoaderFactory;
import org.huihoo.willow.bootstrap.StandardClassLoader;
import org.huihoo.willow.core.StandardConnecor;
import org.huihoo.willow.core.StandardServer;



public class CatalinaConnector extends StandardConnecor implements Connector
{
	protected static final String TOKEN_CATALINA_HOME = "${catalina.home}";
	protected static final String TOKEN_CATALINA_BASE = "${catalina.base}";

	protected static Log log = LogFactory.getLog(CatalinaConnector.class);
	protected static final String info = "CatalinaConnnector/1.0";

	/**
	 * Return descriptive information about this CatalinaConnnector implementation.
	 */
	public String getInfo()
	{

		return (info);

	}

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

	private ClassLoader commonLoader = null;
	private ClassLoader serverLoader = null;
	private ClassLoader sharedLoader = null;

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

	private void initClassLoaders()
	{
		try
		{
			ClassLoaderFactory.setDebug(debug);

			serverLoader = StandardServer.class.getClassLoader();
			if (serverLoader != null)
			{
				commonLoader = serverLoader.getParent();
			}

			sharedLoader = getService().getParentClassLoader();
			
			commonLoader=createClassLoader("common",commonLoader);
			sharedLoader=createClassLoader("shared",sharedLoader);
			serverLoader=createClassLoader("server",serverLoader);
		}
		catch (Throwable t)
		{
			log("Class loader creation threw exception", t);

			t.printStackTrace(System.err);
			System.exit(1);
		}
	}

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

		String value = CatalinaProperties.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(TOKEN_CATALINA_HOME))
			{
				repository = getCatalinaHome() + repository.substring(TOKEN_CATALINA_HOME.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]);

		if (parent == null)
		{
			return ClassLoaderFactory.createClassLoader(unpacked, packed, null);
		}
		else
		{
			return changeClassLoader(unpacked, packed, null, parent);
		}
	}

	private ClassLoader changeClassLoader(File unpacked[], File packed[], URL urls[], ClassLoader loader)
		throws Exception
	{
		// Construct the "class path" for this class loader
		ArrayList list = new ArrayList();

		// Add unpacked directories
		if (unpacked != null)
		{
			for (int i = 0; i < unpacked.length; i++)
			{
				File file = unpacked[i];
				if (!file.exists() || !file.canRead())
				{
					continue;
				}

				if (file.isDirectory())
				{
					URL url = new URL("file", null, file.getCanonicalPath() + File.separator);
					list.add(url.toString());
				}
				else if (file.isFile())
				{
					String filename = file.getName().toLowerCase();
					if (!filename.endsWith(".jar"))
					{
						continue;
					}

					URL url = new URL("file", null, file.getCanonicalPath());
					list.add(url.toString());
				}
			}
		}

		// Add packed directory JAR files
		if (packed != null)
		{
			for (int i = 0; i < packed.length; i++)
			{
				File directory = packed[i];

				if (!directory.isDirectory() || !directory.exists() || !directory.canRead())
					continue;
				String filenames[] = directory.list();
				for (int j = 0; j < filenames.length; j++)
				{
					String filename = filenames[j].toLowerCase();
					if (!filename.endsWith(".jar"))
					{
						continue;
					}
					File file = new File(directory, filenames[j]);
					URL url = new URL("file", null, file.getCanonicalPath());
					list.add(url.toString());
				}
			}
		}

		// Add URLs
		if (urls != null)
		{
			for (int i = 0; i < urls.length; i++)
			{
				list.add(urls[i].toString());
			}
		}

		// Construct the class loader itself
		String array[] = (String[]) list.toArray(new String[list.size()]);
		if (loader instanceof StandardClassLoader)
		{
			StandardClassLoader stdLoader = (StandardClassLoader) loader;
			for (int i = 0; i < array.length; ++i)
			{
				stdLoader.addRepository(array[i]);
			}
		}
		return loader;
	}

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

		initClassLoaders();

		Class startupClass = serverLoader.loadClass("org.apache.catalina.startup.Catalina");

		Object startupInstance = startupClass.newInstance();

		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);

		catalinaBootstrap = startupInstance;

	}

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

	public CatalinaConnector()
	{

	}

	/**
	* Get the value of the catalina.home environment variable.
	*/
	public String getCatalinaHome()
	{
		return System.getProperty("catalina.home");
	}

	/**
	 * Get the value of the catalina.base environment variable.
	 */
	public String getCatalinaBase()
	{
		return System.getProperty("catalina.base");
	}

	/**
	 * Set the <code>catalina.base</code> System property to the current
	 * working directory if it has not been set.
	 */
	public void setCatalinaBase(String catalinaBase)
	{
		if (catalinaBase != null)
		{
			System.setProperty("catalina.base", catalinaBase);
		}
		initDirs();
	}

	/**
	 * Set the <code>catalina.home</code> System property to the current
	 * working directory if it has not been set.
	 */
	public void setCatalinaHome(String catalinaHome)
	{
		if (catalinaHome != null)
		{
			System.setProperty("catalina.home", catalinaHome);
		}

		initDirs();
	}

	/**
		* Start the Catalina daemon.
		*/
	public void start() throws LifecycleException
	{
		super.start();

		try
		{
			if (catalinaBootstrap == null)
			{
				init();
			}

			Method method = null;

			//setAwait(false)
			Class paramTypes[] = new Class[1];
			paramTypes[0] = Boolean.TYPE;
			Object paramValues[] = new Object[1];
			paramValues[0] = new Boolean(false);
			method = catalinaBootstrap.getClass().getMethod("setAwait", paramTypes);
			method.invoke(catalinaBootstrap, paramValues);

			File configFile = new File(getConfigFile());

			String configFilePath = configFile.getCanonicalPath();
			paramTypes = new Class[1];
			paramTypes[0] = configFilePath.getClass();
			paramValues = new Object[1];
			paramValues[0] = configFilePath;
			method = catalinaBootstrap.getClass().getMethod("setConfigFile", paramTypes);
			method.invoke(catalinaBootstrap, paramValues);

			method = catalinaBootstrap.getClass().getMethod("start", null);
			method.invoke(catalinaBootstrap, null);
		}
		catch (Exception e)
		{
			this.log("start Catalina server ", e);
		}

	}

	/**
		* Stop the Catalina Daemon.
		*/
	public void stop() throws LifecycleException
	{
		try
		{
			Method method = catalinaBootstrap.getClass().getMethod("stop", null);
			method.invoke(catalinaBootstrap, null);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}

		super.stop();
	}

	/**
		* Initialize daemon.
		*/
	public synchronized void initialize() throws LifecycleException
	{
		super.initialize();
	}

	protected void initDirs()
	{
		String catalinaHome = System.getProperty("catalina.home");
		if (catalinaHome == null)
		{
			// Backwards compatibility patch for J2EE RI 1.3
			String j2eeHome = System.getProperty("com.sun.enterprise.home");
			if (j2eeHome != null)
			{
				catalinaHome = System.getProperty("com.sun.enterprise.home");
			}
			else if (System.getProperty("catalina.base") != null)
			{
				catalinaHome = System.getProperty("catalina.base");
			}
			else
			{
				// Use IntrospectionUtils and guess the dir
				catalinaHome = IntrospectionUtils.guessHome("catalina.home", "catalina.base", "catalina.jar");
				if (catalinaHome == null)
				{
					catalinaHome = IntrospectionUtils.guessHome("tomcat.install", "catalina.home", "tomcat.jar");
				}
			}
		}
		if (catalinaHome != null)
		{
			File home = new File(catalinaHome);
			if (!home.isAbsolute())
			{
				try
				{
					catalinaHome = home.getCanonicalPath();
				}
				catch (IOException e)
				{
					catalinaHome = home.getAbsolutePath();
				}
			}
			System.setProperty("catalina.home", catalinaHome);
		}

		if (System.getProperty("catalina.base") == null)
		{
			System.setProperty("catalina.base", System.getProperty("catalina.home"));
		}
		else
		{
			String catalinaBase = System.getProperty("catalina.base");
			File base = new File(catalinaBase);
			if (!base.isAbsolute())
			{
				try
				{
					catalinaBase = base.getCanonicalPath();
				}
				catch (IOException e)
				{
					catalinaBase = base.getAbsolutePath();
				}
			}
			System.setProperty("catalina.base", catalinaBase);
		}
	}
}
