/*
 * @(#)VersionHelper.java	1.8 03/01/23
 *
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package workflow.client.factory;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.naming.NamingEnumeration;

import workflow.client.MatrixClient;


/**
 * VersionHelper was used by JNDI to accommodate differences between
 * JDK 1.1.x and the Java 2 platform. Since this is no longer necessary
 * due to JNDI's presence in the Java 2 Platform v1.3 and higher, this
 * class currently serves as a set of utilities for performing
 * system-level things, such as class-loading and reading system properties.
 * 
 * @author Rosanna Lee
 * @author Scott Seligman
 * @version 1.8 03/01/23
 */

public class VersionHelper
{
	private static VersionHelper helper = null;
	private boolean getSystemPropsFailed = false;

	final static String[] PROPS =
		new String[] { MatrixClient.INITIAL_CLIENT_FACTORY, MatrixClient.PROVIDER_URL, };

	public final static int INITIAL_CLIENT_FACTORY = 0;
	public final static int PROVIDER_URL = 1;

	VersionHelper()
	{
	} // Disallow anyone from creating one of these.

	static {
		helper = new VersionHelper();
	}

	public static VersionHelper getVersionHelper()
	{
		return helper;
	}

	static protected URL[] getUrlArray(String codebase) throws MalformedURLException
	{
		// Parse codebase into separate URLs
		StringTokenizer parser = new StringTokenizer(codebase);
		Vector vec = new Vector(10);
		while (parser.hasMoreTokens())
		{
			vec.addElement(parser.nextToken());
		}
		String[] url = new String[vec.size()];
		for (int i = 0; i < url.length; i++)
		{
			url[i] = (String) vec.elementAt(i);
		}

		URL[] urlArray = new URL[url.length];
		for (int i = 0; i < urlArray.length; i++)
		{
			urlArray[i] = new URL(url[i]);
		}
		return urlArray;
	}

	public Class loadClass(String className) throws ClassNotFoundException
	{
		ClassLoader cl = getContextClassLoader();
		return loadClass(className, cl);
	}

	/**
		* Package private.
		*/
	Class loadClass(String className, ClassLoader cl) throws ClassNotFoundException
	{
		return cl.loadClass(className);
	}

	/**
	 * @param className A non-null fully qualified class name.
	 * @param codebase  A non-null, space-separated list of URL strings.
	 */
	public Class loadClass(String className, String codebase)
		throws ClassNotFoundException, MalformedURLException
	{
		ClassLoader cl;

		ClassLoader parent = getContextClassLoader();
		cl = URLClassLoader.newInstance(getUrlArray(codebase), parent);

		return Class.forName(className, true, cl);
	}

	String getWorkflowNamingProperty(final int i)
	{
		return (String) AccessController.doPrivileged(new PrivilegedAction()
		{
			public Object run()
			{
				try
				{
					return System.getProperty(PROPS[i]);
				}
				catch (SecurityException e)
				{
					return null;
				}
			}
		});
	}

	String[] getWorkflowNamingProperties()
	{
		if (getSystemPropsFailed)
		{
			return null; // after one failure, don't bother trying again
		}
		Properties sysProps = (Properties) AccessController.doPrivileged(new PrivilegedAction()
		{
			public Object run()
			{
				try
				{
					return System.getProperties();
				}
				catch (SecurityException e)
				{
					getSystemPropsFailed = true;
					return null;
				}
			}
		});
		if (sysProps == null)
		{
			return null;
		}
		String[] wfnProps = new String[PROPS.length];
		for (int i = 0; i < PROPS.length; i++)
		{
			wfnProps[i] = sysProps.getProperty(PROPS[i]);
		}
		return wfnProps;
	}

	InputStream getResourceAsStream(final Class c, final String name)
	{
		return (InputStream) AccessController.doPrivileged(new PrivilegedAction()
		{
			public Object run()
			{
				return c.getResourceAsStream(name);
			}
		});
	}

	NamingEnumeration getResources(final ClassLoader cl, final String name) throws IOException
	{
		Enumeration urls;
		try
		{
			urls = (Enumeration) AccessController.doPrivileged(new PrivilegedExceptionAction()
			{
				public Object run() throws IOException
				{
					return (cl == null) ? ClassLoader.getSystemResources(name) : cl.getResources(name);
				}
			});
		}
		catch (PrivilegedActionException e)
		{
			throw (IOException) e.getException();
		}
		return new InputStreamEnumeration(urls);
	}

	ClassLoader getContextClassLoader()
	{
		//return VersionHelper.class.getClassLoader();
		return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction()
		{
			public Object run()
			{
				return Thread.currentThread().getContextClassLoader();
			}
		});
	}

	/**
	 * Given an enumeration of URLs, an instance of this class represents
	 * an enumeration of their InputStreams.  Each operation on the URL
	 * enumeration is performed within a doPrivileged block.
	 * This is used to enumerate the resources under a foreign codebase.
	 * This class is not MT-safe.
	 */
	class InputStreamEnumeration implements NamingEnumeration
	{

		private final Enumeration urls;

		private Object nextElement = null;

		InputStreamEnumeration(Enumeration urls)
		{
			this.urls = urls;
		}

		/*
		 * Returns the next InputStream, or null if there are no more.
		 * An InputStream that cannot be opened is skipped.
		 */
		private Object getNextElement()
		{
			return AccessController.doPrivileged(new PrivilegedAction()
			{
				public Object run()
				{
					while (urls.hasMoreElements())
					{
						try
						{
							return ((URL) urls.nextElement()).openStream();
						}
						catch (IOException e)
						{
							// skip this URL
						}
					}
					return null;
				}
			});
		}

		public boolean hasMore()
		{
			if (nextElement != null)
			{
				return true;
			}
			nextElement = getNextElement();
			return (nextElement != null);
		}

		public boolean hasMoreElements()
		{
			return hasMore();
		}

		public Object next()
		{
			if (hasMore())
			{
				Object res = nextElement;
				nextElement = null;
				return res;
			}
			else
			{
				throw new NoSuchElementException();
			}
		}

		public Object nextElement()
		{
			return next();
		}

		public void close()
		{
		}
	}
}
