1 package org.codehaus.plexus.classworlds.launcher;
2
3 /*
4 * Copyright 2001-2006 Codehaus Foundation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.net.MalformedURLException;
25 import java.net.URL;
26 import java.nio.file.Files;
27 import java.nio.file.Paths;
28
29 import org.codehaus.plexus.classworlds.ClassWorld;
30 import org.codehaus.plexus.classworlds.realm.ClassRealm;
31 import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
32 import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
33
34 /**
35 * <p>Command-line invokable application launcher.</p>
36 *
37 * <p>This launcher class assists in the creation of classloaders and <code>ClassRealm</code>s
38 * from a configuration file and the launching of the application's <code>main</code>
39 * method from the correct class loaded through the correct classloader.</p>
40 *
41 * <p> The path to the configuration file is specified using the <code>classworlds.conf</code>
42 * system property, typically specified using the <code>-D</code> switch to
43 * <code>java</code>.</p>
44 *
45 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
46 */
47 public class Launcher {
48 protected static final String CLASSWORLDS_CONF = "classworlds.conf";
49
50 protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/";
51
52 protected ClassLoader systemClassLoader;
53
54 protected String mainClassName;
55
56 protected String mainRealmName;
57
58 protected ClassWorld world;
59
60 private int exitCode = 0;
61
62 public Launcher() {
63 this.systemClassLoader = Thread.currentThread().getContextClassLoader();
64 }
65
66 public void setSystemClassLoader(ClassLoader loader) {
67 this.systemClassLoader = loader;
68 }
69
70 public ClassLoader getSystemClassLoader() {
71 return this.systemClassLoader;
72 }
73
74 public int getExitCode() {
75 return exitCode;
76 }
77
78 public void setAppMain(String mainClassName, String mainRealmName) {
79 this.mainClassName = mainClassName;
80
81 this.mainRealmName = mainRealmName;
82 }
83
84 public String getMainRealmName() {
85 return this.mainRealmName;
86 }
87
88 public String getMainClassName() {
89 return this.mainClassName;
90 }
91
92 public void setWorld(ClassWorld world) {
93 this.world = world;
94 }
95
96 public ClassWorld getWorld() {
97 return this.world;
98 }
99
100 /**
101 * Configure from a file.
102 *
103 * @param is The config input stream.
104 * @throws IOException If an error occurs reading the config file.
105 * @throws MalformedURLException If the config file contains invalid URLs.
106 * @throws ConfigurationException If the config file is corrupt.
107 * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms
108 * with the same id.
109 * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry
110 * point in a non-existent realm.
111 */
112 public void configure(InputStream is)
113 throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException {
114 Configurator configurator = new Configurator(this);
115
116 configurator.configure(is);
117 }
118
119 /**
120 * Retrieve the main entry class.
121 *
122 * @return The main entry class.
123 * @throws ClassNotFoundException If the class cannot be found.
124 * @throws NoSuchRealmException If the specified main entry realm does not exist.
125 */
126 public Class<?> getMainClass() throws ClassNotFoundException, NoSuchRealmException {
127 return getMainRealm().loadClass(getMainClassName());
128 }
129
130 /**
131 * Retrieve the main entry realm.
132 *
133 * @return The main entry realm.
134 * @throws NoSuchRealmException If the specified main entry realm does not exist.
135 */
136 public ClassRealm getMainRealm() throws NoSuchRealmException {
137 return getWorld().getRealm(getMainRealmName());
138 }
139
140 /**
141 * Retrieve the enhanced main entry method.
142 *
143 * @return The enhanced main entry method.
144 * @throws ClassNotFoundException If the main entry class cannot be found.
145 * @throws NoSuchMethodException If the main entry method cannot be found.
146 * @throws NoSuchRealmException If the main entry realm cannot be found.
147 */
148 protected Method getEnhancedMainMethod()
149 throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
150 Class<?> cwClass = getMainRealm().loadClass(ClassWorld.class.getName());
151
152 Method m = getMainClass().getMethod("main", String[].class, cwClass);
153
154 int modifiers = m.getModifiers();
155
156 if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
157 if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
158 return m;
159 }
160 }
161
162 throw new NoSuchMethodException("public static void main(String[] args, ClassWorld world)");
163 }
164
165 /**
166 * Retrieve the main entry method.
167 *
168 * @return The main entry method.
169 * @throws ClassNotFoundException If the main entry class cannot be found.
170 * @throws NoSuchMethodException If the main entry method cannot be found.
171 * @throws NoSuchRealmException If the main entry realm cannot be found.
172 */
173 protected Method getMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException {
174 Method m = getMainClass().getMethod("main", String[].class);
175
176 int modifiers = m.getModifiers();
177
178 if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
179 if (m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE) {
180 return m;
181 }
182 }
183
184 throw new NoSuchMethodException("public static void main(String[] args) in " + getMainClass());
185 }
186
187 /**
188 * Launch the application.
189 *
190 * @param args The application args.
191 * @throws ClassNotFoundException If the main entry class cannot be found.
192 * @throws IllegalAccessException If the method cannot be accessed.
193 * @throws InvocationTargetException If the target of the invokation is invalid.
194 * @throws NoSuchMethodException If the main entry method cannot be found.
195 * @throws NoSuchRealmException If the main entry realm cannot be found.
196 */
197 public void launch(String[] args)
198 throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
199 NoSuchRealmException {
200 try {
201 launchEnhanced(args);
202
203 return;
204 } catch (NoSuchMethodException e) {
205 // ignore
206 }
207
208 launchStandard(args);
209 }
210
211 /**
212 * <p>Attempt to launch the application through the enhanced main method.</p>
213 *
214 * <p>This will seek a method with the exact signature of:</p>
215 * <pre>
216 * public static void main(String[] args, ClassWorld world)
217 * </pre>
218 *
219 * @param args The application args.
220 * @throws ClassNotFoundException If the main entry class cannot be found.
221 * @throws IllegalAccessException If the method cannot be accessed.
222 * @throws InvocationTargetException If the target of the invokation is
223 * invalid.
224 * @throws NoSuchMethodException If the main entry method cannot be found.
225 * @throws NoSuchRealmException If the main entry realm cannot be found.
226 */
227 protected void launchEnhanced(String[] args)
228 throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
229 NoSuchRealmException {
230 ClassRealm mainRealm = getMainRealm();
231
232 Class<?> mainClass = getMainClass();
233
234 Method mainMethod = getEnhancedMainMethod();
235
236 ClassLoader cl = mainRealm;
237
238 // ----------------------------------------------------------------------
239 // This is what the classloader for the main realm looks like when we
240 // boot from the command line:
241 // ----------------------------------------------------------------------
242 // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar
243 // ^
244 // |
245 // |
246 // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar
247 // ^
248 // |
249 // |
250 // [ Strategy ]
251 // ----------------------------------------------------------------------
252
253 Thread.currentThread().setContextClassLoader(cl);
254
255 Object ret = mainMethod.invoke(mainClass, args, getWorld());
256
257 if (ret instanceof Integer) {
258 exitCode = (Integer) ret;
259 }
260
261 Thread.currentThread().setContextClassLoader(systemClassLoader);
262 }
263
264 /**
265 * <p>Attempt to launch the application through the standard main method.</p>
266 *
267 * <p>This will seek a method with the exact signature of:</p>
268 *
269 * <pre>
270 * public static void main(String[] args)
271 * </pre>
272 *
273 * @param args The application args.
274 * @throws ClassNotFoundException If the main entry class cannot be found.
275 * @throws IllegalAccessException If the method cannot be accessed.
276 * @throws InvocationTargetException If the target of the invokation is
277 * invalid.
278 * @throws NoSuchMethodException If the main entry method cannot be found.
279 * @throws NoSuchRealmException If the main entry realm cannot be found.
280 */
281 protected void launchStandard(String[] args)
282 throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
283 NoSuchRealmException {
284 ClassRealm mainRealm = getMainRealm();
285
286 Class<?> mainClass = getMainClass();
287
288 Method mainMethod = getMainMethod();
289
290 Thread.currentThread().setContextClassLoader(mainRealm);
291
292 Object ret = mainMethod.invoke(mainClass, new Object[] {args});
293
294 if (ret instanceof Integer) {
295 exitCode = (Integer) ret;
296 }
297
298 Thread.currentThread().setContextClassLoader(systemClassLoader);
299 }
300
301 // ------------------------------------------------------------
302 // Class methods
303 // ------------------------------------------------------------
304
305 /**
306 * Launch the launcher from the command line.
307 * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception,
308 * or some other code for an application error.
309 *
310 * @param args The application command-line arguments.
311 */
312 public static void main(String[] args) {
313 try {
314 int exitCode = mainWithExitCode(args);
315
316 System.exit(exitCode);
317 } catch (Exception e) {
318 e.printStackTrace();
319
320 System.exit(100);
321 }
322 }
323
324 /**
325 * Launch the launcher.
326 *
327 * @param args The application command-line arguments.
328 * @return an integer exit code
329 * @throws Exception If an error occurs.
330 */
331 public static int mainWithExitCode(String[] args) throws Exception {
332 String classworldsConf = System.getProperty(CLASSWORLDS_CONF);
333
334 InputStream is;
335
336 Launcher launcher = new Launcher();
337
338 ClassLoader cl = Thread.currentThread().getContextClassLoader();
339
340 launcher.setSystemClassLoader(cl);
341
342 if (classworldsConf != null) {
343 is = Files.newInputStream(Paths.get(classworldsConf));
344 } else {
345 if ("true".equals(System.getProperty("classworlds.bootstrapped"))) {
346 is = cl.getResourceAsStream(UBERJAR_CONF_DIR + CLASSWORLDS_CONF);
347 } else {
348 is = cl.getResourceAsStream(CLASSWORLDS_CONF);
349 }
350 }
351
352 if (is == null) {
353 throw new Exception("classworlds configuration not specified nor found in the classpath");
354 }
355
356 launcher.configure(is);
357
358 is.close();
359
360 try {
361 launcher.launch(args);
362 } catch (InvocationTargetException e) {
363 ClassRealm realm = launcher.getWorld().getRealm(launcher.getMainRealmName());
364
365 URL[] constituents = realm.getURLs();
366
367 System.out.println("---------------------------------------------------");
368
369 for (int i = 0; i < constituents.length; i++) {
370 System.out.println("constituent[" + i + "]: " + constituents[i]);
371 }
372
373 System.out.println("---------------------------------------------------");
374
375 // Decode ITE (if we can)
376 Throwable t = e.getTargetException();
377
378 if (t instanceof Exception) {
379 throw (Exception) t;
380 }
381 if (t instanceof Error) {
382 throw (Error) t;
383 }
384
385 // Else just toss the ITE
386 throw e;
387 }
388
389 return launcher.getExitCode();
390 }
391 }