package de.ugoe.cs.eventbench.jfcmonitor;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

public class JarLauncher {

	String workingDir = System.getProperty("user.dir") + "/";
	String jarfile;
	String[] args;

	String[] classPath = new String[] {};
	String mainClassName = "";
	ClassLoader costumClassLoader;

	public class JarLaunchException extends Exception {

		private static final long serialVersionUID = 1L;

		public JarLaunchException() {
			super();
		}

		public JarLaunchException(String string) {
			super(string);
		}

		public JarLaunchException(Exception e) {
			super(e);
		}

	}

	public JarLauncher(String jarfile, String[] args) {
		this.jarfile = jarfile;
		this.args = args;
	}

	public void exec() {
		try {
			getInfoFromJar();
			initClassLoader();
			startSUT();
		} catch (JarLaunchException e) {
			System.err.println("Failure to launch application.");
			System.err.println(e.getMessage());
		}
	}

	private void getInfoFromJar() throws JarLaunchException {
		JarInputStream jarInputStream;
		try {
			jarInputStream = new JarInputStream(new FileInputStream(workingDir
					+ jarfile));
		} catch (FileNotFoundException e) {
			throw new JarLaunchException(e);
		} catch (IOException e) {
			throw new JarLaunchException(e);
		}
		Manifest manifest = jarInputStream.getManifest();
		mainClassName = manifest.getMainAttributes().getValue("Main-Class");
		String jarClassPath = manifest.getMainAttributes().getValue(
				"Class-Path");
		String[] jarClassPathElements = jarClassPath.split(" ");
		classPath = new String[jarClassPathElements.length];
		for (int i = 0; i < jarClassPathElements.length; i++) {
			classPath[i] = "file:" + workingDir + jarClassPathElements[i];
		}
	}

	private void initClassLoader() throws JarLaunchException {
		URLClassLoader classLoader = (URLClassLoader) ClassLoader
				.getSystemClassLoader();
		Method method;
		try {
			method = URLClassLoader.class.getDeclaredMethod("addURL",
					new Class[] { URL.class });
		} catch (SecurityException e) {
			throw new JarLaunchException(
					"addURL method of URLClassLoader not accessible via reflection.");
		} catch (NoSuchMethodException e) {
			throw new JarLaunchException(
					"URLClassLoader does not have addURL method. Should be impossible!!");
		}
		method.setAccessible(true);

		try {
			method.invoke(classLoader, new Object[] { new URL("file:"
					+ workingDir + jarfile) });
			for (String element : classPath) {
				method.invoke(classLoader, new Object[] { new URL(element) });
			}
		} catch (IllegalArgumentException e) {
			throw new JarLaunchException(
					"Illegal arguments for addURL method. Should be impossible!!");
		} catch (MalformedURLException e) {
			throw new JarLaunchException(e);
		} catch (IllegalAccessException e) {
			throw new JarLaunchException(
					"addURL method of URLClassLoader not accessible via reflection.");
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	private void startSUT() throws JarLaunchException {
		Class<?> mainClass;
		try {
			mainClass = Class.forName(mainClassName);
		} catch (ClassNotFoundException e) {
			throw new JarLaunchException("Main class not found: "
					+ mainClassName);
		}
		Method mainMethod;
		try {
			mainMethod = mainClass.getMethod("main",
					new Class[] { String[].class });
		} catch (SecurityException e) {
			throw new JarLaunchException("Main method not accessible.");
		} catch (NoSuchMethodException e) {
			throw new JarLaunchException("Main method not found.");
		}
		try {
			mainMethod.invoke(null, new Object[] { args });
		} catch (IllegalArgumentException e) {
			throw new JarLaunchException(
					"Illegal arguments for main method. Should be impossible!!");
		} catch (IllegalAccessException e) {
			throw new JarLaunchException("Main method not accessible.");
		} catch (InvocationTargetException e) {
			throw new JarLaunchException(e);
		}
	}
}
