source: trunk/quest-jfcmonitor/src/main/java/de/ugoe/cs/quest/jfcmonitor/JarLauncher.java @ 825

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