[263] | 1 | package de.ugoe.cs.eventbench.jfcmonitor;
|
---|
| 2 |
|
---|
| 3 | import java.io.FileInputStream;
|
---|
| 4 | import java.io.FileNotFoundException;
|
---|
| 5 | import java.io.IOException;
|
---|
| 6 | import java.lang.reflect.InvocationTargetException;
|
---|
| 7 | import java.lang.reflect.Method;
|
---|
| 8 | import java.net.MalformedURLException;
|
---|
| 9 | import java.net.URL;
|
---|
| 10 | import java.net.URLClassLoader;
|
---|
[286] | 11 | import java.util.Arrays;
|
---|
[263] | 12 | import java.util.jar.JarInputStream;
|
---|
| 13 | import java.util.jar.Manifest;
|
---|
| 14 |
|
---|
[265] | 15 | /**
|
---|
| 16 | * <p>
|
---|
| 17 | * Class that launches an executable Jar-file in the same thread and VM where
|
---|
| 18 | * the JarLauncher instance is created. The requirements on the Jar file are:
|
---|
| 19 | * <li>Must contain a MANIFEST.</li>
|
---|
| 20 | * <li>The MANIFEST must define the main-class of the application ("Main-Class"
|
---|
| 21 | * entry).</li>
|
---|
| 22 | * <li>The MANIFEST must define the classpath of the application ("Class-Path"
|
---|
| 23 | * entry).</li>
|
---|
| 24 | * </p>
|
---|
| 25 | *
|
---|
| 26 | * @author Steffen Herbold
|
---|
| 27 | * @version 1.0
|
---|
| 28 | */
|
---|
[263] | 29 | public class JarLauncher {
|
---|
[264] | 30 |
|
---|
[265] | 31 | /**
|
---|
| 32 | * <p>
|
---|
| 33 | * Name of the Jar file to be executed.
|
---|
| 34 | * </p>
|
---|
| 35 | */
|
---|
| 36 | private String jarfile;
|
---|
[264] | 37 |
|
---|
[265] | 38 | /**
|
---|
| 39 | * <p>
|
---|
| 40 | * Arguments for launching the Jar file.
|
---|
| 41 | * </p>
|
---|
| 42 | */
|
---|
| 43 | private String[] args;
|
---|
[264] | 44 |
|
---|
[265] | 45 | /**
|
---|
| 46 | * <p>
|
---|
| 47 | * Helper variable with the path to the working directory.
|
---|
| 48 | * </p>
|
---|
| 49 | */
|
---|
| 50 | final String workingDir = System.getProperty("user.dir") + "/";
|
---|
[264] | 51 |
|
---|
[265] | 52 | /**
|
---|
| 53 | * <p>
|
---|
| 54 | * Internal variable used to store the classpath extracted from the Jar
|
---|
| 55 | * file's MANIFEST.
|
---|
| 56 | * </p>
|
---|
| 57 | */
|
---|
| 58 | private String[] classPath = new String[] {};
|
---|
| 59 |
|
---|
| 60 | /**
|
---|
| 61 | * <p>
|
---|
| 62 | * Internal variable used to store the name (including package information)
|
---|
| 63 | * of the Jar file's main function extracted from the Jar file's MANIFEST.
|
---|
| 64 | * </p>
|
---|
| 65 | */
|
---|
| 66 | private String mainClassName = "";
|
---|
| 67 |
|
---|
| 68 | /**
|
---|
| 69 | * <p>
|
---|
| 70 | * Inner class that defines an exception that is thrown if launching the
|
---|
| 71 | * application in the Jar file fails.
|
---|
| 72 | * </p>
|
---|
| 73 | *
|
---|
| 74 | * @author Steffen Herbold
|
---|
| 75 | * @version 1.0
|
---|
| 76 | */
|
---|
[283] | 77 | private static class JarLaunchException extends Exception {
|
---|
[265] | 78 |
|
---|
| 79 | /**
|
---|
| 80 | * <p>
|
---|
| 81 | * Id for object serialization.
|
---|
| 82 | * </p>
|
---|
| 83 | */
|
---|
[264] | 84 | private static final long serialVersionUID = 1L;
|
---|
| 85 |
|
---|
[265] | 86 | /**
|
---|
| 87 | * <p>
|
---|
| 88 | * Constructor. Creates a new JarLaunchException.
|
---|
| 89 | * </p>
|
---|
| 90 | *
|
---|
| 91 | * @param string
|
---|
| 92 | * error message of the exception
|
---|
| 93 | */
|
---|
[264] | 94 | public JarLaunchException(String string) {
|
---|
| 95 | super(string);
|
---|
| 96 | }
|
---|
| 97 |
|
---|
[265] | 98 | /**
|
---|
| 99 | * <p>
|
---|
| 100 | * Constructor. Creates a new JarLaunchException as a copy of an
|
---|
| 101 | * existing exception.
|
---|
| 102 | * </p>
|
---|
| 103 | *
|
---|
| 104 | * @param e
|
---|
| 105 | * exception that is copied
|
---|
| 106 | */
|
---|
[264] | 107 | public JarLaunchException(Exception e) {
|
---|
| 108 | super(e);
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | }
|
---|
| 112 |
|
---|
[265] | 113 | /**
|
---|
| 114 | * <p>
|
---|
| 115 | * Constructor. Creates a new JarLauncher.
|
---|
| 116 | * </p>
|
---|
| 117 | *
|
---|
[267] | 118 | * @param jarfile
|
---|
[265] | 119 | * file to be launched; must not be complete path but in relation
|
---|
| 120 | * to the current working directory
|
---|
| 121 | * @param args
|
---|
| 122 | * arguments with which the main function of the Jar file is
|
---|
| 123 | * called
|
---|
| 124 | */
|
---|
[263] | 125 | public JarLauncher(String jarfile, String[] args) {
|
---|
| 126 | this.jarfile = jarfile;
|
---|
[286] | 127 | this.args = Arrays.copyOf(args, args.length);
|
---|
[263] | 128 | }
|
---|
| 129 |
|
---|
[265] | 130 | /**
|
---|
| 131 | * <p>
|
---|
| 132 | * Executes the main function of the Jar file associated with this launcher.
|
---|
| 133 | * </p>
|
---|
| 134 | */
|
---|
[264] | 135 | public void exec() {
|
---|
| 136 | try {
|
---|
| 137 | getInfoFromJar();
|
---|
| 138 | initClassLoader();
|
---|
[265] | 139 | runMain();
|
---|
[264] | 140 | } catch (JarLaunchException e) {
|
---|
| 141 | System.err.println("Failure to launch application.");
|
---|
| 142 | System.err.println(e.getMessage());
|
---|
| 143 | }
|
---|
[263] | 144 | }
|
---|
[264] | 145 |
|
---|
[265] | 146 | /**
|
---|
| 147 | * <p>
|
---|
| 148 | * Retrieves the classpath and main function from the Jar file's MANIFEST.
|
---|
| 149 | * </p>
|
---|
| 150 | *
|
---|
| 151 | * @throws JarLaunchException
|
---|
| 152 | * thrown if reading of Jar file or MANIFEST fails
|
---|
| 153 | */
|
---|
[264] | 154 | private void getInfoFromJar() throws JarLaunchException {
|
---|
| 155 | JarInputStream jarInputStream;
|
---|
| 156 | try {
|
---|
| 157 | jarInputStream = new JarInputStream(new FileInputStream(workingDir
|
---|
| 158 | + jarfile));
|
---|
| 159 | } catch (FileNotFoundException e) {
|
---|
| 160 | throw new JarLaunchException(e);
|
---|
| 161 | } catch (IOException e) {
|
---|
| 162 | throw new JarLaunchException(e);
|
---|
| 163 | }
|
---|
[263] | 164 | Manifest manifest = jarInputStream.getManifest();
|
---|
| 165 | mainClassName = manifest.getMainAttributes().getValue("Main-Class");
|
---|
[264] | 166 | String jarClassPath = manifest.getMainAttributes().getValue(
|
---|
| 167 | "Class-Path");
|
---|
[263] | 168 | String[] jarClassPathElements = jarClassPath.split(" ");
|
---|
| 169 | classPath = new String[jarClassPathElements.length];
|
---|
[264] | 170 | for (int i = 0; i < jarClassPathElements.length; i++) {
|
---|
| 171 | classPath[i] = "file:" + workingDir + jarClassPathElements[i];
|
---|
[263] | 172 | }
|
---|
[283] | 173 | try {
|
---|
| 174 | jarInputStream.close();
|
---|
| 175 | } catch (IOException e) {
|
---|
| 176 | e.printStackTrace();
|
---|
| 177 | }
|
---|
[263] | 178 | }
|
---|
[264] | 179 |
|
---|
[265] | 180 | /**
|
---|
| 181 | * <p>
|
---|
| 182 | * Modifies the {@link ClassLoader} of the current VM such that it includes
|
---|
| 183 | * the class path defined in the Jar file's MANIFEST.
|
---|
| 184 | * </p>
|
---|
| 185 | *
|
---|
| 186 | * @throws JarLaunchException
|
---|
| 187 | * thrown if modification of {@link ClassLoader} fails.
|
---|
| 188 | */
|
---|
[264] | 189 | private void initClassLoader() throws JarLaunchException {
|
---|
[263] | 190 | URLClassLoader classLoader = (URLClassLoader) ClassLoader
|
---|
| 191 | .getSystemClassLoader();
|
---|
[264] | 192 | Method method;
|
---|
| 193 | try {
|
---|
| 194 | method = URLClassLoader.class.getDeclaredMethod("addURL",
|
---|
| 195 | new Class[] { URL.class });
|
---|
| 196 | } catch (SecurityException e) {
|
---|
| 197 | throw new JarLaunchException(
|
---|
| 198 | "addURL method of URLClassLoader not accessible via reflection.");
|
---|
| 199 | } catch (NoSuchMethodException e) {
|
---|
| 200 | throw new JarLaunchException(
|
---|
| 201 | "URLClassLoader does not have addURL method. Should be impossible!!");
|
---|
| 202 | }
|
---|
[263] | 203 | method.setAccessible(true);
|
---|
[264] | 204 |
|
---|
| 205 | try {
|
---|
| 206 | method.invoke(classLoader, new Object[] { new URL("file:"
|
---|
| 207 | + workingDir + jarfile) });
|
---|
| 208 | for (String element : classPath) {
|
---|
| 209 | method.invoke(classLoader, new Object[] { new URL(element) });
|
---|
| 210 | }
|
---|
| 211 | } catch (IllegalArgumentException e) {
|
---|
| 212 | throw new JarLaunchException(
|
---|
| 213 | "Illegal arguments for addURL method. Should be impossible!!");
|
---|
| 214 | } catch (MalformedURLException e) {
|
---|
| 215 | throw new JarLaunchException(e);
|
---|
| 216 | } catch (IllegalAccessException e) {
|
---|
| 217 | throw new JarLaunchException(
|
---|
| 218 | "addURL method of URLClassLoader not accessible via reflection.");
|
---|
| 219 | } catch (InvocationTargetException e) {
|
---|
| 220 | e.printStackTrace();
|
---|
[263] | 221 | }
|
---|
| 222 | }
|
---|
[264] | 223 |
|
---|
[265] | 224 | /**
|
---|
| 225 | * <p>
|
---|
| 226 | * Executes the main function.
|
---|
| 227 | * </p>
|
---|
| 228 | *
|
---|
| 229 | * @throws JarLaunchException
|
---|
| 230 | * thrown if execution of main function fails or the main
|
---|
| 231 | * function itself throws an exception
|
---|
| 232 | */
|
---|
| 233 | private void runMain() throws JarLaunchException {
|
---|
[264] | 234 | Class<?> mainClass;
|
---|
| 235 | try {
|
---|
| 236 | mainClass = Class.forName(mainClassName);
|
---|
| 237 | } catch (ClassNotFoundException e) {
|
---|
| 238 | throw new JarLaunchException("Main class not found: "
|
---|
| 239 | + mainClassName);
|
---|
| 240 | }
|
---|
| 241 | Method mainMethod;
|
---|
| 242 | try {
|
---|
| 243 | mainMethod = mainClass.getMethod("main",
|
---|
| 244 | new Class[] { String[].class });
|
---|
| 245 | } catch (SecurityException e) {
|
---|
| 246 | throw new JarLaunchException("Main method not accessible.");
|
---|
| 247 | } catch (NoSuchMethodException e) {
|
---|
| 248 | throw new JarLaunchException("Main method not found.");
|
---|
| 249 | }
|
---|
| 250 | try {
|
---|
| 251 | mainMethod.invoke(null, new Object[] { args });
|
---|
| 252 | } catch (IllegalArgumentException e) {
|
---|
| 253 | throw new JarLaunchException(
|
---|
| 254 | "Illegal arguments for main method. Should be impossible!!");
|
---|
| 255 | } catch (IllegalAccessException e) {
|
---|
| 256 | throw new JarLaunchException("Main method not accessible.");
|
---|
| 257 | } catch (InvocationTargetException e) {
|
---|
| 258 | throw new JarLaunchException(e);
|
---|
| 259 | }
|
---|
[263] | 260 | }
|
---|
| 261 | }
|
---|