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