/**********************************************************************************
 * $URL: https://source.sakaiproject.org/svn/mailarchive/tags/sakai_2-3-1/mailarchive-james/james/src/java/org/sakaiproject/james/PhoenixLauncherMain.java $
 * $Id: PhoenixLauncherMain.java 8186 2006-04-24 01:42:56Z ggolden@umich.edu $
 ***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
 * 
 * Licensed under the Educational Community License, Version 1.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * 
 *      http://www.opensource.org/licenses/ecl1.php
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 *
 **********************************************************************************/

package org.sakaiproject.james;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/** Taken from the phoenix 4.0.4 org.apache.avalon.phoenix.launcher.Main.java */
/*
 * Copyright (C) The Apache Software Foundation. All rights reserved. This software is published under the terms of the Apache Software License version 1.1, a copy of which has been included with this distribution in the LICENSE.txt file.
 */

/**
 * PhoenixLoader is the class that bootstraps and sets up engine ClassLoader. It also sets up a default policy that gives full permissions to engine code.
 * 
 * @author <a href="mailto:peter at apache.org">Peter Donald</a>
 */
public final class PhoenixLauncherMain
{
	/** Our logger. */
	private static Log M_log = LogFactory.getLog(PhoenixLauncherMain.class);

	private static final String MAIN_CLASS = "org.apache.avalon.phoenix.frontends.CLIMain";

	private static final String LOADER_JAR = "phoenix-loader.jar";

	private static Object c_frontend;

	/**
	 * Main entry point for Phoenix.
	 * 
	 * @param args
	 *        the command line arguments
	 * @throws Exception
	 *         if an error occurs
	 */
	public static final void main(final String[] args) throws Exception
	{
		final int exitCode = startup(args, new HashMap(), true);
		System.exit(exitCode);
	}

	/**
	 * Method to call to startup Phoenix from an external (calling) application. Protected to allow access from DaemonLauncher.
	 * 
	 * @param args
	 *        the command line arg array
	 * @param data
	 *        a set of extra parameters to pass to embeddor
	 * @param blocking
	 *        false if the current thread is expected to return.
	 * @return the exit code which should be used to exit the JVM
	 * @throws Exception
	 *         if an error occurs
	 */
	protected static final int startup(final String[] args, final Map data, final boolean blocking) throws Exception
	{
		int exitCode;
		try
		{
			// setup new Policy manager
			// TODO: (done) removed
			// Policy.setPolicy( new FreeNEasyPolicy() );

			// Create engine ClassLoader
			// TODO: (done) removed
			// final URL[] urls = getEngineClassPath();
			// final URLClassLoader classLoader = new URLClassLoader( urls );

			// TODO: add these extra paths to the existing classloader
			final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

			// TODO: (done) use the one classloader
			// data.put( "common.classloader", ClassLoader.getSystemClassLoader() );
			data.put("common.classloader", classLoader);

			data.put("container.classloader", classLoader);
			data.put("phoenix.home", new File(findPhoenixHome()));

			// Setup context classloader
			// TODO: (done) removed
			// Thread.currentThread().setContextClassLoader( classLoader );

			// Create main launcher
			final Class clazz = classLoader.loadClass(MAIN_CLASS);
			final Class[] paramTypes = new Class[] { args.getClass(), Map.class, Boolean.TYPE };
			final Method method = clazz.getMethod("main", paramTypes);
			c_frontend = clazz.newInstance();

			// kick the tires and light the fires....
			final Integer integer = (Integer) method.invoke(c_frontend, new Object[] { args, data, new Boolean(blocking) });
			exitCode = integer.intValue();
		}
		catch (final Exception e)
		{
			// TODO: (done) changed to a log
			// e.printStackTrace();
			M_log.warn("PhoenixLauncherMain.startup: " + e);
			exitCode = 1;
		}
		return exitCode;
	}

	/**
	 * Method to call to shutdown Phoenix from an external (calling) application. Protected to allow access from DaemonLauncher.
	 */
	protected static final void shutdown()
	{
		if (null == c_frontend)
		{
			return;
		}

		try
		{
			final Class clazz = c_frontend.getClass();
			final Method method = clazz.getMethod("shutdown", new Class[0]);

			// Lets put this sucker to sleep
			method.invoke(c_frontend, new Object[0]);
		}
		catch (final Exception e)
		{
			// TODO: (done) changed to a log
			// e.printStackTrace();
			M_log.warn("PhoenixLauncherMain.shutdown: " + e);
		}
		finally
		{
			c_frontend = null;
		}
	}

	/**
	 * Create a ClassPath for the engine.
	 * 
	 * @return the set of URLs that engine uses to load
	 * @throws Exception
	 *         if unable to aquire classpath
	 */
	private static URL[] getEngineClassPath() throws Exception
	{
		final ArrayList urls = new ArrayList();

		final File dir = findEngineLibDir();
		final File[] files = dir.listFiles();
		for (int i = 0; i < files.length; i++)
		{
			final File file = files[i];
			if (file.getName().endsWith(".jar"))
			{
				urls.add(file.toURL());
			}
		}

		// TODO: (done) add also the phoenix libs
		final File dir_p = findPhoenixLibDir();
		final File[] files_p = dir_p.listFiles();
		for (int i = 0; i < files_p.length; i++)
		{
			final File file = files_p[i];
			if (file.getName().endsWith(".jar"))
			{
				urls.add(file.toURL());
			}
		}

		return (URL[]) urls.toArray(new URL[urls.size()]);
	}

	/**
	 * Find directory to load engine specific libraries from.
	 * 
	 * @return the lib dir
	 * @throws Exception
	 *         if unable to aquire directory
	 */
	private static File findEngineLibDir() throws Exception
	{
		final String phoenixHome = findPhoenixHome();
		final String engineLibDir = phoenixHome + File.separator + "bin" + File.separator + "lib";
		final File dir = new File(engineLibDir).getCanonicalFile();
		if (!dir.exists())
		{
			throw new Exception("Unable to locate engine lib directory at " + engineLibDir);
		}
		return dir;
	}

	/**
	 * TODO: (done) added this method Find directory to load phoenix libraries from.
	 * 
	 * @return the lib dir
	 * @throws Exception
	 *         if unable to aquire directory
	 */
	private static File findPhoenixLibDir() throws Exception
	{
		final String phoenixHome = findPhoenixHome();
		final String engineLibDir = phoenixHome + File.separator + "lib";
		final File dir = new File(engineLibDir).getCanonicalFile();
		if (!dir.exists())
		{
			throw new Exception("Unable to locate engine lib directory at " + engineLibDir);
		}
		return dir;
	}

	/**
	 * Utility method to find the home directory of Phoenix and make sure system property is set to it.
	 * 
	 * @return the location of phoenix directory
	 * @throws Exception
	 *         if unable to locate directory
	 */
	private static String findPhoenixHome() throws Exception
	{
		String phoenixHome = System.getProperty("phoenix.home", null);
		if (null == phoenixHome)
		{
			final File loaderDir = findLoaderDir();
			phoenixHome = loaderDir.getAbsoluteFile().getParentFile() + File.separator;
		}

		phoenixHome = (new File(phoenixHome)).getCanonicalFile().toString();
		System.setProperty("phoenix.home", phoenixHome);
		return phoenixHome;
	}

	/**
	 * Finds the LOADER_JAR file in the classpath.
	 */
	private static final File findLoaderDir() throws Exception
	{
		final String classpath = System.getProperty("java.class.path");
		final String pathSeparator = System.getProperty("path.separator");
		final StringTokenizer tokenizer = new StringTokenizer(classpath, pathSeparator);

		while (tokenizer.hasMoreTokens())
		{
			final String element = tokenizer.nextToken();

			if (element.endsWith(LOADER_JAR))
			{
				File file = (new File(element)).getCanonicalFile();
				file = file.getParentFile();
				return file;
			}
		}

		throw new Exception("Unable to locate " + LOADER_JAR + " in classpath");
	}

	/**
	 * Default polic class to give every code base all permssions. Will be replaced once the kernel loads.
	 */
	private static class FreeNEasyPolicy extends Policy
	{
		public PermissionCollection getPermissions(final CodeSource codeSource)
		{
			final Permissions permissions = new Permissions();
			permissions.add(new java.security.AllPermission());
			return permissions;
		}

		public void refresh()
		{
		}
	}
}
