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

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.huihoo.willow.Service;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Logger;
import org.huihoo.willow.core.StandardService;
import org.huihoo.willow.util.StringManager;

public class ExpandWar
{

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

	/**
	 * Expand the WAR file found at the specified URL into an unpacked
	 * directory structure, and return the absolute name to the expanded
	 * directory.
	 *
	 * @param service Service war is being installed for
	 * @param war URL of the workflow application archive to be expanded
	 *  (must start with "jar:")
	 *
	 * @exception IllegalArgumentException if this is not a "jar:" URL
	 * @exception IOException if an input/output error was encountered
	 *  during expansion
	 */
	public static String expand(Service service, URL war) throws IOException
	{

		int debug = 0;
		Logger logger = service.getLogger();

		if (service instanceof StandardService)
		{
			debug = ((StandardService) service).getDebug();
		}

		// Calculate the directory name of the expanded directory
		if (debug >= 1)
		{
			logger.log("expand(" + war.toString() + ")");
		}
		String name = war.toString().replace('\\', '/');
		if (name.endsWith("!/"))
		{
			name = name.substring(0, name.length() - 2);
		}
		int period = name.lastIndexOf('.');
		if (period >= name.length() - 4)
			name = name.substring(0, period);
		int slash = name.lastIndexOf('/');
		if (slash >= 0)
		{
			name = name.substring(slash + 1);
		}
		if (debug >= 1)
		{
			logger.log("  Proposed directory name: " + name);
		}
		return expand(service, war, name);

	}

	/**
	 * Expand the WAR file found at the specified URL into an unpacked
	 * directory structure, and return the absolute name to the expanded
	 * directory.
	 *
	 * @param service Service war is being installed for
	 * @param war URL of the workflow application archive to be expanded
	 *  (must start with "jar:")
	 * @param name Context path name for workflow application
	 *
	 * @exception IllegalArgumentException if this is not a "jar:" URL
	 * @exception IOException if an input/output error was encountered
	 *  during expansion
	 */
	public static String expand(Service service, URL war, String name) throws IOException
	{

		int debug = 0;
		Logger logger = service.getLogger();

		if (service instanceof StandardService)
		{
			debug = ((StandardService) service).getDebug();
		}

		// Make sure that there is no such directory already existing
		File appBase = new File(service.getAppBase());
		if (!appBase.isAbsolute())
		{
			appBase = new File(System.getProperty(Globals.PROPS_WILLOW_HOME), service.getAppBase());
		}
		if (!appBase.exists() || !appBase.isDirectory())
		{
			throw new IOException(sm.getString("serviceConfig.appBase", appBase.getAbsolutePath()));
		}
		File docBase = new File(appBase, name);
		if (docBase.exists())
		{
			// War file is already installed
			return (docBase.getAbsolutePath());
		}

		// Create the new document base directory
		docBase.mkdir();
		if (debug >= 2)
		{
			logger.log("  Have created expansion directory " + docBase.getAbsolutePath());
		}

		// Expand the WAR into the new document base directory
		JarURLConnection juc = (JarURLConnection) war.openConnection();
		juc.setUseCaches(false);
		JarFile jarFile = null;
		InputStream input = null;
		try
		{
			jarFile = juc.getJarFile();
			if (debug >= 2)
			{
				logger.log("  Have opened JAR file successfully");
			}
			Enumeration jarEntries = jarFile.entries();
			if (debug >= 2)
			{
				logger.log("  Have retrieved entries enumeration");
			}
			while (jarEntries.hasMoreElements())
			{
				JarEntry jarEntry = (JarEntry) jarEntries.nextElement();
				String jar_name = jarEntry.getName();
				if (debug >= 2)
				{
					logger.log("  Am processing entry " + jar_name);
				}
				int last = jar_name.lastIndexOf('/');
				if (last >= 0)
				{
					File parent = new File(docBase, jar_name.substring(0, last));
					if (debug >= 2)
					{
						logger.log("  Creating parent directory " + parent);
					}
					parent.mkdirs();
				}
				if (jar_name.endsWith("/"))
				{
					continue;
				}
				if (debug >= 2)
				{
					logger.log("  Creating expanded file " + jar_name);
				}
				input = jarFile.getInputStream(jarEntry);
				expand(input, docBase, jar_name);
				input.close();
				input = null;
			}
		}
		catch (IOException e)
		{
			// If something went wrong, delete expanded dir to keep things 
			// clean
			deleteDir(docBase);
			throw e;
		}
		finally
		{
			if (input != null)
			{
				try
				{
					input.close();
				}
				catch (Throwable t)
				{
					;
				}
				input = null;
			}
			if (jarFile != null)
			{
				try
				{
					jarFile.close();
				}
				catch (Throwable t)
				{
					;
				}
				jarFile = null;
			}
		}

		// Return the absolute path to our new document base directory
		return (docBase.getAbsolutePath());

	}

	/**
	 * Delete the specified directory, including all of its contents and
	 * subdirectories recursively.
	 *
	 * @param dir File object representing the directory to be deleted
	 */
	public static void deleteDir(File dir)
	{

		String files[] = dir.list();
		if (files == null)
		{
			files = new String[0];
		}
		for (int i = 0; i < files.length; i++)
		{
			File file = new File(dir, files[i]);
			if (file.isDirectory())
			{
				deleteDir(file);
			}
			else
			{
				file.delete();
			}
		}
		dir.delete();

	}

	/**
	 * Expand the specified input stream into the specified directory, creating
	 * a file named from the specified relative path.
	 *
	 * @param input InputStream to be copied
	 * @param docBase Document base directory into which we are expanding
	 * @param name Relative name of the file to be created
	 *
	 * @exception IOException if an input/output error occurs
	 */
	protected static void expand(InputStream input, File docBase, String name) throws IOException
	{

		File file = new File(docBase, name);
		BufferedOutputStream output = null;
		try
		{
			output = new BufferedOutputStream(new FileOutputStream(file));
			byte buffer[] = new byte[2048];
			while (true)
			{
				int n = input.read(buffer);
				if (n <= 0)
					break;
				output.write(buffer, 0, n);
			}
		}
		finally
		{
			if (output != null)
			{
				try
				{
					output.close();
				}
				catch (IOException e)
				{
					// Ignore
				}
			}
		}

	}

}
