//----------------------------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.workflow.script.selector;

import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

import org.huihoo.workflow.script.Constants;


public class ScriptLoader extends URLClassLoader
{
	private PermissionCollection permissionCollection;
	private CodeSource codeSource;
	private String className;
	private ClassLoader parent;
	private SecurityManager securityManager;
	private PrivilegedLoadClass privLoadClass;

	public ScriptLoader(
		URL[] urls,
		ClassLoader parent,
		PermissionCollection permissionCollection,
		CodeSource codeSource)
	{
		super(urls, parent);
		this.permissionCollection = permissionCollection;
		this.codeSource = codeSource;
		this.parent = parent;
		this.privLoadClass = new PrivilegedLoadClass();
		this.securityManager = System.getSecurityManager();
	}

	/**
	 * Load the class with the specified name.  This method searches for
	 * classes in the same manner as <code>loadClass(String, boolean)</code>
	 * with <code>false</code> as the second argument.
	 *
	 * @param name Name of the class to be loaded
	 *
	 * @exception ClassNotFoundException if the class was not found
	 */
	public Class loadClass(String name) throws ClassNotFoundException
	{

		return (loadClass(name, false));
	}

	/**
	 * Load the class with the specified name, searching using the following
	 * algorithm until it finds and returns the class.  If the class cannot
	 * be found, returns <code>ClassNotFoundException</code>.
	 * <ul>
	 * <li>Call <code>findLoadedClass(String)</code> to check if the
	 *     class has already been loaded.  If it has, the same
	 *     <code>Class</code> object is returned.</li>
	 * <li>If the <code>delegate</code> property is set to <code>true</code>,
	 *     call the <code>loadClass()</code> method of the parent class
	 *     loader, if any.</li>            
	 * <li>Call <code>findClass()</code> to find this class in our locally
	 *     defined repositories.</li>      
	 * <li>Call the <code>loadClass()</code> method of our parent
	 *     class loader, if any.</li>      
	 * </ul>
	 * If the class was found using the above steps, and the
	 * <code>resolve</code> flag is <code>true</code>, this method will then
	 * call <code>resolveClass(Class)</code> on the resulting Class object.
	 *                                     
	 * @param name Name of the class to be loaded
	 * @param resolve If <code>true</code> then resolve the class
	 *                                     
	 * @exception ClassNotFoundException if the class was not found
	 */
	public Class loadClass(final String name, boolean resolve) throws ClassNotFoundException
	{

		Class clazz = null;

		// (0) Check our previously loaded class cache
		clazz = findLoadedClass(name);
		if (clazz != null)
		{
			if (resolve)
			{
				resolveClass(clazz);
			}
			return (clazz);
		}

		// (.5) Permission to access this class when using a SecurityManager
		if (securityManager != null)
		{
			int dot = name.lastIndexOf('.');
			if (dot >= 0)
			{
				try
				{
					securityManager.checkPackageAccess(name.substring(0, dot));
				}
				catch (SecurityException se)
				{
					String error = "Security Violation, attempt to use " + "Restricted Class: " + name;
					se.printStackTrace();
					throw new ClassNotFoundException(error);
				}
			}
		}

		// Class is in a package, delegate to thread context class loader
		if (!name.startsWith(Constants.SCRIPT_PACKAGE_NAME))
		{
			if (securityManager != null)
			{
				final ClassLoader classLoader = (ClassLoader) AccessController.doPrivileged(privLoadClass);
				try
				{
					clazz = (Class) AccessController.doPrivileged(new PrivilegedExceptionAction()
					{
						public Object run() throws Exception
						{
							return classLoader.loadClass(name);
						}
					});
				}
				catch (PrivilegedActionException ex)
				{
				}
			}
			else
			{
				clazz = parent.loadClass(name);

			} //~end if (securityManager != null)

			if (resolve)
			{
				resolveClass(clazz);
			}
			return clazz;
		}

		try
		{
			clazz = parent.loadClass(name);
			if (clazz != null)
			{
				if (resolve)
					resolveClass(clazz);
				return (clazz);
			}
		}
		catch (ClassNotFoundException e)
		{
			;
		}
		return findClass(name);
	}

	/**
	 * Get the Permissions for a CodeSource.
	 *
	 * Since this ClassLoader is only used for a JSP page in
	 * a web application context, we just return our preset
	 * PermissionCollection for the web app context.
	 *
	 * @param CodeSource where the code was loaded from
	 * @return PermissionCollection for CodeSource
	 */
	public final PermissionCollection getPermissions(CodeSource codeSource)
	{
		return permissionCollection;
	}

	private class PrivilegedLoadClass implements PrivilegedAction
	{

		public Object run()
		{
			return Thread.currentThread().getContextClassLoader();
		}
	}
}