package org.codehaus.plexus.classworlds.launcher;

/*
 * Copyright 2001-2006 Codehaus Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;

import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;

Command-line invokable application launcher.

This launcher class assists in the creation of classloaders and ClassRealms from a configuration file and the launching of the application's main method from the correct class loaded through the correct classloader.

The path to the configuration file is specified using the classworlds.conf system property, typically specified using the -D switch to java.

Author:bob mcwhirter
/** * <p>Command-line invokable application launcher.</p> * * <p>This launcher class assists in the creation of classloaders and <code>ClassRealm</code>s * from a configuration file and the launching of the application's <code>main</code> * method from the correct class loaded through the correct classloader.</p> * * <p> The path to the configuration file is specified using the <code>classworlds.conf</code> * system property, typically specified using the <code>-D</code> switch to * <code>java</code>.</p> * * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a> */
public class Launcher { protected static final String CLASSWORLDS_CONF = "classworlds.conf"; protected static final String UBERJAR_CONF_DIR = "WORLDS-INF/conf/"; protected ClassLoader systemClassLoader; protected String mainClassName; protected String mainRealmName; protected ClassWorld world; private int exitCode = 0; public Launcher() { this.systemClassLoader = Thread.currentThread().getContextClassLoader(); } public void setSystemClassLoader( ClassLoader loader ) { this.systemClassLoader = loader; } public ClassLoader getSystemClassLoader() { return this.systemClassLoader; } public int getExitCode() { return exitCode; } public void setAppMain( String mainClassName, String mainRealmName ) { this.mainClassName = mainClassName; this.mainRealmName = mainRealmName; } public String getMainRealmName() { return this.mainRealmName; } public String getMainClassName() { return this.mainClassName; } public void setWorld( ClassWorld world ) { this.world = world; } public ClassWorld getWorld() { return this.world; }
Configure from a file.
Params:
  • is – The config input stream.
Throws:
/** * Configure from a file. * * @param is The config input stream. * @throws IOException If an error occurs reading the config file. * @throws MalformedURLException If the config file contains invalid URLs. * @throws ConfigurationException If the config file is corrupt. * @throws org.codehaus.plexus.classworlds.realm.DuplicateRealmException If the config file defines two realms * with the same id. * @throws org.codehaus.plexus.classworlds.realm.NoSuchRealmException If the config file defines a main entry * point in a non-existent realm. */
public void configure( InputStream is ) throws IOException, ConfigurationException, DuplicateRealmException, NoSuchRealmException { Configurator configurator = new Configurator( this ); configurator.configure( is ); }
Retrieve the main entry class.
Throws:
Returns:The main entry class.
/** * Retrieve the main entry class. * * @return The main entry class. * @throws ClassNotFoundException If the class cannot be found. * @throws NoSuchRealmException If the specified main entry realm does not exist. */
public Class<?> getMainClass() throws ClassNotFoundException, NoSuchRealmException { return getMainRealm().loadClass( getMainClassName() ); }
Retrieve the main entry realm.
Throws:
Returns:The main entry realm.
/** * Retrieve the main entry realm. * * @return The main entry realm. * @throws NoSuchRealmException If the specified main entry realm does not exist. */
public ClassRealm getMainRealm() throws NoSuchRealmException { return getWorld().getRealm( getMainRealmName() ); }
Retrieve the enhanced main entry method.
Throws:
Returns:The enhanced main entry method.
/** * Retrieve the enhanced main entry method. * * @return The enhanced main entry method. * @throws ClassNotFoundException If the main entry class cannot be found. * @throws NoSuchMethodException If the main entry method cannot be found. * @throws NoSuchRealmException If the main entry realm cannot be found. */
protected Method getEnhancedMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException { Class<?> cwClass = getMainRealm().loadClass( ClassWorld.class.getName() ); Method m = getMainClass().getMethod( "main", new Class[]{String[].class, cwClass} ); int modifiers = m.getModifiers(); if ( Modifier.isStatic( modifiers ) && Modifier.isPublic( modifiers ) ) { if ( m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE ) { return m; } } throw new NoSuchMethodException( "public static void main(String[] args, ClassWorld world)" ); }
Retrieve the main entry method.
Throws:
Returns:The main entry method.
/** * Retrieve the main entry method. * * @return The main entry method. * @throws ClassNotFoundException If the main entry class cannot be found. * @throws NoSuchMethodException If the main entry method cannot be found. * @throws NoSuchRealmException If the main entry realm cannot be found. */
protected Method getMainMethod() throws ClassNotFoundException, NoSuchMethodException, NoSuchRealmException { Method m = getMainClass().getMethod( "main", new Class[]{String[].class} ); int modifiers = m.getModifiers(); if ( Modifier.isStatic( modifiers ) && Modifier.isPublic( modifiers ) ) { if ( m.getReturnType() == Integer.TYPE || m.getReturnType() == Void.TYPE ) { return m; } } throw new NoSuchMethodException( "public static void main(String[] args) in " + getMainClass() ); }
Launch the application.
Params:
  • args – The application args.
Throws:
/** * Launch the application. * * @param args The application args. * @throws ClassNotFoundException If the main entry class cannot be found. * @throws IllegalAccessException If the method cannot be accessed. * @throws InvocationTargetException If the target of the invokation is invalid. * @throws NoSuchMethodException If the main entry method cannot be found. * @throws NoSuchRealmException If the main entry realm cannot be found. */
public void launch( String[] args ) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchRealmException { try { launchEnhanced( args ); return; } catch ( NoSuchMethodException e ) { // ignore } launchStandard( args ); }

Attempt to launch the application through the enhanced main method.

This will seek a method with the exact signature of:

 public static void main(String[] args, ClassWorld world)
 
Params:
  • args – The application args.
Throws:
/** * <p>Attempt to launch the application through the enhanced main method.</p> * * <p>This will seek a method with the exact signature of:</p> * <pre> * public static void main(String[] args, ClassWorld world) * </pre> * * @param args The application args. * @throws ClassNotFoundException If the main entry class cannot be found. * @throws IllegalAccessException If the method cannot be accessed. * @throws InvocationTargetException If the target of the invokation is * invalid. * @throws NoSuchMethodException If the main entry method cannot be found. * @throws NoSuchRealmException If the main entry realm cannot be found. */
protected void launchEnhanced( String[] args ) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchRealmException { ClassRealm mainRealm = getMainRealm(); Class<?> mainClass = getMainClass(); Method mainMethod = getEnhancedMainMethod(); ClassLoader cl = mainRealm; // ---------------------------------------------------------------------- // This is what the classloader for the main realm looks like when we // boot from the command line: // ---------------------------------------------------------------------- // [ AppLauncher$AppClassLoader ] : $CLASSPATH envar // ^ // | // | // [ AppLauncher$ExtClassLoader ] : ${java.home}/jre/lib/ext/*.jar // ^ // | // | // [ Strategy ] // ---------------------------------------------------------------------- Thread.currentThread().setContextClassLoader( cl ); Object ret = mainMethod.invoke( mainClass, new Object[]{args, getWorld()} ); if ( ret instanceof Integer ) { exitCode = ( (Integer) ret ).intValue(); } Thread.currentThread().setContextClassLoader( systemClassLoader ); }

Attempt to launch the application through the standard main method.

This will seek a method with the exact signature of:

 public static void main(String[] args)
 
Params:
  • args – The application args.
Throws:
/** * <p>Attempt to launch the application through the standard main method.</p> * * <p>This will seek a method with the exact signature of:</p> * <pre> * public static void main(String[] args) * </pre> * * @param args The application args. * @throws ClassNotFoundException If the main entry class cannot be found. * @throws IllegalAccessException If the method cannot be accessed. * @throws InvocationTargetException If the target of the invokation is * invalid. * @throws NoSuchMethodException If the main entry method cannot be found. * @throws NoSuchRealmException If the main entry realm cannot be found. */
protected void launchStandard( String[] args ) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchRealmException { ClassRealm mainRealm = getMainRealm(); Class<?> mainClass = getMainClass(); Method mainMethod = getMainMethod(); Thread.currentThread().setContextClassLoader( mainRealm ); Object ret = mainMethod.invoke( mainClass, new Object[]{args} ); if ( ret instanceof Integer ) { exitCode = ( (Integer) ret ).intValue(); } Thread.currentThread().setContextClassLoader( systemClassLoader ); } // ------------------------------------------------------------ // Class methods // ------------------------------------------------------------
Launch the launcher from the command line. Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception, or some other code for an application error.
Params:
  • args – The application command-line arguments.
/** * Launch the launcher from the command line. * Will exit using System.exit with an exit code of 0 for success, 100 if there was an unknown exception, * or some other code for an application error. * * @param args The application command-line arguments. */
public static void main( String[] args ) { try { int exitCode = mainWithExitCode( args ); System.exit( exitCode ); } catch ( Exception e ) { e.printStackTrace(); System.exit( 100 ); } }
Launch the launcher.
Params:
  • args – The application command-line arguments.
Throws:
Returns:an integer exit code
/** * Launch the launcher. * * @param args The application command-line arguments. * @return an integer exit code * @throws Exception If an error occurs. */
public static int mainWithExitCode( String[] args ) throws Exception { String classworldsConf = System.getProperty( CLASSWORLDS_CONF ); InputStream is; Launcher launcher = new Launcher(); ClassLoader cl = Thread.currentThread().getContextClassLoader(); launcher.setSystemClassLoader( cl ); if ( classworldsConf != null ) { is = new FileInputStream( classworldsConf ); } else { if ( "true".equals( System.getProperty( "classworlds.bootstrapped" ) ) ) { is = cl.getResourceAsStream( UBERJAR_CONF_DIR + CLASSWORLDS_CONF ); } else { is = cl.getResourceAsStream( CLASSWORLDS_CONF ); } } if ( is == null ) { throw new Exception( "classworlds configuration not specified nor found in the classpath" ); } launcher.configure( is ); is.close(); try { launcher.launch( args ); } catch ( InvocationTargetException e ) { ClassRealm realm = launcher.getWorld().getRealm( launcher.getMainRealmName() ); URL[] constituents = realm.getURLs(); System.out.println( "---------------------------------------------------" ); for ( int i = 0; i < constituents.length; i++ ) { System.out.println( "constituent[" + i + "]: " + constituents[i] ); } System.out.println( "---------------------------------------------------" ); // Decode ITE (if we can) Throwable t = e.getTargetException(); if ( t instanceof Exception ) { throw (Exception) t; } if ( t instanceof Error ) { throw (Error) t; } // Else just toss the ITE throw e; } return launcher.getExitCode(); } }