View Javadoc

1   package org.codehaus.classworlds;
2   
3   /*
4    $Id: Launcher.java,v 1.5 2004/09/19 17:41:44 dandiep Exp $
5   
6    Copyright 2002 (C) The Werken Company. All Rights Reserved.
7    
8    Redistribution and use of this software and associated documentation
9    ("Software"), with or without modification, are permitted provided
10   that the following conditions are met:
11  
12   1. Redistributions of source code must retain copyright
13      statements and notices.  Redistributions must also contain a
14      copy of this document.
15   
16   2. Redistributions in binary form must reproduce the
17      above copyright notice, this list of conditions and the
18      following disclaimer in the documentation and/or other
19      materials provided with the distribution.
20   
21   3. The name "classworlds" must not be used to endorse or promote
22      products derived from this Software without prior written
23      permission of The Werken Company.  For written permission,
24      please contact bob@werken.com.
25   
26   4. Products derived from this Software may not be called "classworlds"
27      nor may "classworlds" appear in their names without prior written
28      permission of The Werken Company. "classworlds" is a registered
29      trademark of The Werken Company.
30   
31   5. Due credit should be given to The Werken Company.
32      (http://classworlds.werken.com/).
33   
34   THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
35   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
36   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
37   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
38   THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
45   OF THE POSSIBILITY OF SUCH DAMAGE.
46   
47   */
48  
49  import java.io.IOException;
50  import java.io.InputStream;
51  import java.io.FileInputStream;
52  import java.lang.reflect.Method;
53  import java.lang.reflect.Modifier;
54  import java.lang.reflect.InvocationTargetException;
55  import java.net.MalformedURLException;
56  import java.net.URL;
57  
58  /***
59   * Command-line invokable application launcher.
60   * <p/>
61   * <p/>
62   * This launcher class assists in the creation of classloaders and <code>ClassRealm</code>s
63   * from a configuration file and the launching of the application's <code>main</code>
64   * method from the correct class loaded through the correct classloader.
65   * </p>
66   * <p/>
67   * <p/>
68   * The path to the configuration file is specified using the <code>classworlds.conf</code>
69   * system property, typically specified using the <code>-D</code> switch to
70   * <code>java</code>.
71   * </p>
72   *
73   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
74   * @version $Id: Launcher.java,v 1.5 2004/09/19 17:41:44 dandiep Exp $
75   */
76  public class Launcher
77  {
78      protected static final String CLASSWORLDS_CONF = "classworlds.conf";
79  
80      protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/";
81  
82      protected String mainClassName;
83  
84      protected String mainRealmName;
85  
86      protected ClassWorld world;
87  
88      private int exitCode = 0;
89  
90      public Launcher()
91      {
92      }
93  
94      public int getExitCode()
95      {
96          return exitCode;
97      }
98  
99      public void setAppMain( String mainClassName, String mainRealmName )
100     {
101         this.mainClassName = mainClassName;
102 
103         this.mainRealmName = mainRealmName;
104     }
105 
106     public String getMainRealmName()
107     {
108         return this.mainRealmName;
109     }
110 
111     public String getMainClassName()
112     {
113         return this.mainClassName;
114     }
115 
116     public void setWorld( ClassWorld world )
117     {
118         this.world = world;
119     }
120 
121     public ClassWorld getWorld()
122     {
123         return this.world;
124     }
125 
126     /***
127      * Configure from a file.
128      *
129      * @param is The config input stream.
130      * @throws IOException             If an error occurs reading the config file.
131      * @throws MalformedURLException   If the config file contains invalid URLs.
132      * @throws ConfigurationException  If the config file is corrupt.
133      * @throws DuplicateRealmException If the config file defines two realms
134      *                                 with the same id.
135      * @throws NoSuchRealmException    If the config file defines a main entry
136      *                                 point in a non-existent realm.
137      */
138     public void configure( InputStream is )
139         throws IOException, MalformedURLException, ConfigurationException,
140         DuplicateRealmException, NoSuchRealmException
141     {
142         Configurator configurator = new Configurator( this );
143 
144         configurator.configure( is );
145     }
146 
147     /***
148      * Retrieve the main entry class.
149      *
150      * @return The main entry class.
151      * @throws ClassNotFoundException If the class cannot be found.
152      * @throws NoSuchRealmException   If the specified main entry realm does not exist.
153      */
154     public Class getMainClass()
155         throws ClassNotFoundException, NoSuchRealmException
156     {
157         return getMainRealm().loadClass( getMainClassName() );
158     }
159 
160     /***
161      * Retrieve the main entry realm.
162      *
163      * @return The main entry realm.
164      * @throws NoSuchRealmException If the specified main entry realm does not exist.
165      */
166     public ClassRealm getMainRealm()
167         throws NoSuchRealmException
168     {
169         return getWorld().getRealm( getMainRealmName() );
170     }
171 
172     /***
173      * Retrieve the enhanced main entry method.
174      *
175      * @return The enhanced main entry method.
176      * @throws ClassNotFoundException If the main entry class cannot be found.
177      * @throws NoSuchMethodException  If the main entry method cannot be found.
178      * @throws NoSuchRealmException   If the main entry realm cannot be found.
179      */
180     protected Method getEnhancedMainMethod()
181         throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException
182     {
183         Method[] methods = getMainClass().getMethods();
184         Class cwClass = getMainRealm().loadClass( ClassWorld.class.getName() );
185 
186         Method m = getMainClass().getMethod( "main", new Class[] { String[].class, cwClass } );
187 
188         int modifiers = m.getModifiers();
189 
190         if ( Modifier.isStatic( modifiers ) && Modifier.isPublic( modifiers ) )
191         {
192             if ( m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE )
193             {
194                 return m;
195             }
196         }
197 
198         throw new NoSuchMethodException( "public static void main(String[] args, ClassWorld world)" );
199     }
200 
201     /***
202      * Retrieve the main entry method.
203      *
204      * @return The main entry method.
205      * @throws ClassNotFoundException If the main entry class cannot be found.
206      * @throws NoSuchMethodException  If the main entry method cannot be found.
207      * @throws NoSuchRealmException   If the main entry realm cannot be found.
208      */
209     protected Method getMainMethod()
210         throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException
211     {
212         Method m = getMainClass().getMethod( "main", new Class[] { String[].class } );
213 
214         int modifiers = m.getModifiers();
215 
216         if ( Modifier.isStatic( modifiers ) && Modifier.isPublic( modifiers ) )
217         {
218             if ( m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE )
219             {
220                 return m;
221             }
222         }
223 
224         throw new NoSuchMethodException( "public static void main(String[] args) in " + getMainClass() );
225     }
226 
227     /***
228      * Launch the application.
229      *
230      * @param args The application args.
231      * @throws ClassNotFoundException    If the main entry class cannot be found.
232      * @throws IllegalAccessException    If the method cannot be accessed.
233      * @throws InvocationTargetException If the target of the invokation is invalid.
234      * @throws NoSuchMethodException     If the main entry method cannot be found.
235      * @throws NoSuchRealmException      If the main entry realm cannot be found.
236      */
237     public void launch( String[] args )
238         throws ClassNotFoundException, IllegalAccessException,
239         InvocationTargetException, NoSuchMethodException, NoSuchRealmException
240     {
241         try
242         {
243             launchEnhanced( args );
244 
245             return;
246         }
247         catch ( NoSuchMethodException e )
248         {
249             // ignore
250         }
251 
252         launchStandard( args );
253     }
254 
255     /***
256      * Attempt to launch the application through the enhanced main method.
257      * <p/>
258      * <p/>
259      * This will seek a method with the exact signature of:
260      * </p>
261      * <p/>
262      * <pre>
263      *  public static void main(String[] args, ClassWorld world)
264      *  </pre>
265      *
266      * @param args The application args.
267      * @throws ClassNotFoundException    If the main entry class cannot be found.
268      * @throws IllegalAccessException    If the method cannot be accessed.
269      * @throws InvocationTargetException If the target of the invokation is
270      *                                   invalid.
271      * @throws NoSuchMethodException     If the main entry method cannot be found.
272      * @throws NoSuchRealmException      If the main entry realm cannot be found.
273      */
274     protected void launchEnhanced( String[] args )
275         throws ClassNotFoundException, IllegalAccessException,
276         InvocationTargetException, NoSuchMethodException, NoSuchRealmException
277     {
278         ClassRealm mainRealm = getMainRealm();
279 
280         Class mainClass = getMainClass();
281 
282         Method mainMethod = getEnhancedMainMethod();
283 
284         ClassLoader cl = mainRealm.getClassLoader();
285 
286         // ----------------------------------------------------------------------
287         // This is what the classloader for the main realm looks like when we
288         // boot from the command line:
289         // ----------------------------------------------------------------------
290         // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar
291         //           ^
292         //           |
293         //           |
294         // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar
295         //           ^
296         //           |
297         //           |
298         // [ RealmClassLoader ]
299         // ----------------------------------------------------------------------
300 
301         Thread.currentThread().setContextClassLoader( cl );
302 
303         Object ret = mainMethod.invoke( mainClass, new Object[]{args, getWorld()} );
304         if ( ret instanceof Integer )
305         {
306             exitCode = ( ( Integer ) ret ).intValue();
307         }
308     }
309 
310     /**
311      * Attempt to launch the application through the standard main method.
312      * <p/>
313      * <p/>
314      * This will seek a method with the exact signature of:
315      * </p>
316      * <p/>
317      * <pre>
318      *  public static void main(String[] args)
319      *  </pre>
320      *
321      * @param args The application args.
322      * @throws ClassNotFoundException    If the main entry class cannot be found.
323      * @throws IllegalAccessException    If the method cannot be accessed.
324      * @throws InvocationTargetException If the target of the invokation is
325      *                                   invalid.
326      * @throws NoSuchMethodException     If the main entry method cannot be found.
327      * @throws NoSuchRealmException      If the main entry realm cannot be found.
328      */
329     protected void launchStandard( String[] args )
330         throws ClassNotFoundException, IllegalAccessException,
331         InvocationTargetException, NoSuchMethodException, NoSuchRealmException
332     {
333         ClassRealm mainRealm = getMainRealm();
334 
335         Class mainClass = getMainClass();
336 
337         Method mainMethod = getMainMethod();
338 
339         Thread.currentThread().setContextClassLoader( mainRealm.getClassLoader() );
340 
341         Object ret = mainMethod.invoke( mainClass, new Object[]{args} );
342         if ( ret instanceof Integer )
343         {
344             exitCode = ( ( Integer ) ret ).intValue();
345         }
346     }
347 
348     // ------------------------------------------------------------
349     //     Class methods
350     // ------------------------------------------------------------
351 
352     /***
353      * Launch the launcher from the command line.
354      * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception,
355      * or some other code for an application error.
356      *
357      * @param args The application command-line arguments.
358      */
359     public static void main( String[] args )
360     {
361         try
362         {
363             int exitCode = mainWithExitCode( args );
364             System.exit( exitCode );
365         }
366         catch ( Exception e )
367         {
368             e.printStackTrace();
369             System.exit( 100 );
370         }
371     }
372 
373     /***
374      * Launch the launcher.
375      *
376      * @param args The application command-line arguments.
377      * @return an integer exit code
378      * @throws Exception If an error occurs.
379      */
380     public static int mainWithExitCode( String[] args )
381         throws Exception
382     {
383         String classworldsConf = System.getProperty( CLASSWORLDS_CONF );
384 
385         InputStream is = null;
386 
387         Launcher launcher = new Launcher();
388 
389         if ( classworldsConf != null )
390         {
391             is = new FileInputStream( classworldsConf );
392         }
393         else
394         {
395             ClassLoader cl = Thread.currentThread().getContextClassLoader();
396 
397             if ( "true".equals( System.getProperty( "classworlds.bootstrapped" ) ) )
398             {
399                 is = cl.getResourceAsStream( UBERJAR_CONF_DIR + CLASSWORLDS_CONF );
400             }
401             else
402             {
403                 is = cl.getResourceAsStream( CLASSWORLDS_CONF );
404             }
405         }
406 
407         if ( is == null )
408         {
409             throw new Exception( "classworlds configuration not specified nor found in the classpath" );
410         }
411 
412         launcher.configure( is );
413 
414         try
415         {
416             launcher.launch( args );
417         }
418         catch ( InvocationTargetException e )
419         {
420             ClassRealm realm = launcher.getWorld().getRealm( launcher.getMainRealmName() );
421 
422             URL[] constituents = realm.getConstituents();
423 
424             System.out.println( "---------------------------------------------------" );
425 
426             for ( int i = 0; i < constituents.length; i++ )
427             {
428                 System.out.println( "constituent[" + i + "]: " + constituents[i] );
429             }
430 
431             System.out.println( "---------------------------------------------------" );
432 
433             // Decode ITE (if we can)
434             Throwable t = e.getTargetException();
435 
436             if ( t instanceof Exception )
437             {
438                 throw (Exception) t;
439             }
440             if ( t instanceof Error )
441             {
442                 throw (Error) t;
443             }
444 
445             // Else just toss the ITE
446             throw e;
447         }
448 
449         return launcher.getExitCode();
450     }
451 }