source: trunk/autoquest-jfcmonitor/src/main/java/de/ugoe/cs/autoquest/jfcmonitor/JarLauncher.java @ 927

Last change on this file since 927 was 927, checked in by sherbold, 12 years ago
  • added copyright under the Apache License, Version 2.0
  • Property svn:mime-type set to text/plain
File size: 9.1 KB
Line 
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.
14
15package de.ugoe.cs.autoquest.jfcmonitor;
16
17import java.io.FileInputStream;
18import java.io.FileNotFoundException;
19import java.io.IOException;
20import java.lang.reflect.InvocationTargetException;
21import java.lang.reflect.Method;
22import java.net.MalformedURLException;
23import java.net.URL;
24import java.net.URLClassLoader;
25import java.util.Arrays;
26import java.util.jar.JarInputStream;
27import java.util.jar.Manifest;
28
29/**
30 * <p>
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:
33 * <li>Must contain a MANIFEST.</li>
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>
36 * </p>
37 *
38 * @author Steffen Herbold
39 * @version 1.0
40 */
41public class JarLauncher {
42
43    /**
44     * <p>
45     * Name of the Jar file to be executed.
46     * </p>
47     */
48    private String jarfile;
49
50    /**
51     * <p>
52     * Arguments for launching the Jar file.
53     * </p>
54     */
55    private String[] args;
56
57    /**
58     * <p>
59     * Helper variable with the path to the working directory.
60     * </p>
61     */
62    final String workingDir = System.getProperty("user.dir") + "/";
63
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[] { };
70
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 = "";
78
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 {
89
90        /**
91         * <p>
92         * Id for object serialization.
93         * </p>
94         */
95        private static final long serialVersionUID = 1L;
96
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        }
108
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        }
120
121    }
122
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    }
138
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    }
155
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    }
190
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);
216
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    }
240
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    }
284}
Note: See TracBrowser for help on using the repository browser.