/* ******************************************************************* * Copyright (c) 1999-2001 Xerox Corporation, * 2002 Palo Alto Research Center, Incorporated (PARC). * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: * Xerox/PARC initial implementation * ******************************************************************/ package org.aspectj.internal.tools.ant.taskdefs; import java.io.File; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.aspectj.testing.util.LangUtil; /** * Wrapper to invoke class identified by setting VM argument. * Caller must set a system property "MainWrapper.classname" * to the fully-qualified name of the target class to invoke, * and the target class must be resolvable from the defining * class loader of this class. * VM argument name is available as PROP_NAME, but * is set by adding the following to the command line: * -DMainWrapper.classname="fully.qualified.Classname". * This returns -1 if unable to load the main method, * 1 if the invoked method throws an exception, and 0 otherwise. */ public class MainWrapper { /** MUST set the fully-qualified name of class to invoke using * a VM property of this name * tracked in Ajctest.java */ public static final String PROP_NAME = "MainWrapper.classname"; /** May set the path to a classes diretory, * to interpret class names and load classes. * Tracked in Ajctest.java */ public static final String CLASSDIR_NAME = "MainWrapper.classdir"; /** to disable returning int via System.exit, set to boolean true value (todo: ignored) */ public static final String SIGNAL_EXCEPTION_NAME = "MainWrapper.signalException"; /** to disable returning via System.exit on first Throwable, set to boolean true value (todo: ignored) */ public static final String FAIL_ON_EXCEPTION_NAME = "MainWrapper.failOnException"; /** quit on first exception */ // todo public class controls - yuck public static boolean FAIL_ON_EXCEPTION = true; /** signal number of exceptions with int return value */ public static boolean SIGNAL_EXCEPTION = true; /** redirect messages for exceptions; if null, none printed */ public static PrintStream OUT_STREAM = System.err; /** result accumulated, possibly from multiple threads */ private static int result; /** * Run target class's main(args), doing a System.exit() with * a value > 0 for the number of Throwable that * the target class threw that * makes it through to a top-level ThreadGroup. (This is * strictly speaking not correct since applications can live * after their exceptions stop a thread.) * Exit with a value < 0 if there were exceptions in loading * the target class. Messages are printed to OUT_STREAM. */ public static void main(String[] args) { String classname = "no property : " + PROP_NAME; Method main = null; // setup: this try block is for loading main method - return -1 if fail try { // access classname from jvm arg classname = System.getProperty(PROP_NAME); // this will fail if the class is not available from this classloader Class cl = Class.forName(classname); final Class[] argTypes = new Class[] {String[].class}; // will fail if no main method main = cl.getMethod("main", argTypes); if (!Modifier.isStatic(main.getModifiers())) { PrintStream outStream = OUT_STREAM; if (null != outStream) outStream.println("main is not static"); result = -1; } // if user also request loading of all classes... String classesDir = System.getProperty(CLASSDIR_NAME); if ((null != classesDir) && (0 < classesDir.length())) { MainWrapper.loadAllClasses(new File(classesDir)); } } catch (Throwable t) { if (1 != result) result--; reportException("setup Throwable invoking class " + classname, t); } // run: this try block is for running things - get Throwable from our thread here if ((null != main) && (0 == result)) { try { runInOurThreadGroup(main, args); } catch (Throwable t) { if (result > -1) { result++; } reportException("run Throwable invoking class " + classname, t); } } if ((0 != result) && (SIGNAL_EXCEPTION)) { System.exit(result); } } static void runInOurThreadGroup(final Method main, final String[] args) { final String classname = main.getDeclaringClass().getName(); ThreadGroup ourGroup = new ThreadGroup("MainWrapper ThreadGroup") { public void uncaughtException(Thread t, Throwable e) { reportException("uncaughtException invoking " + classname, e); result++; if (FAIL_ON_EXCEPTION) { System.exit((SIGNAL_EXCEPTION ? result : 0)); } } }; Runnable runner = new Runnable() { public void run() { try { main.invoke(null, new Object[] {args}); } catch (InvocationTargetException e) { result = -1; reportException("InvocationTargetException invoking " + classname, e); } catch (IllegalAccessException e) { result = -1; reportException("IllegalAccessException invoking " + classname, e); } } }; Thread newMain = new Thread(ourGroup, runner, "pseudo-main"); newMain.start(); try { newMain.join(); } catch (InterruptedException e) { result = -1; // todo: InterruptedException might be benign - retry? reportException("Interrupted while waiting for to join " + newMain, e); } } /** * Try to load all classes in a directory. * @throws Error if any failed */ static protected void loadAllClasses(File classesDir) { if (null != classesDir) { String[] names = LangUtil.classesIn(classesDir); StringBuffer err = new StringBuffer(); LangUtil.loadClasses(names, null, err); if (0 < err.length()) { throw new Error("MainWrapper Errors loading classes: " + err.toString()); } } } static void reportException(String context, Throwable t) { PrintStream outStream = OUT_STREAM; if (null != outStream) { while ((null != t) && (InvocationTargetException.class.isAssignableFrom(t.getClass()))) { t = ((InvocationTargetException) t).getTargetException(); } outStream.println(" context: " + context); outStream.println(" message: " + t.getMessage()); t.printStackTrace(outStream); } } } // MainWrapper