bb.util
Class Execute

java.lang.Object
  extended by bb.util.Execute

public class Execute
extends Object

Executes a "task", defined here as arbitrary Java code that is contained inside either a Callable or Runnable instance.

The following always occurs:

  1. assigns a new UncaughtThrowableLogger to Thread, which ensures that every uncaught Throwable is properly logged. Note that this assignment will remain for the life of the JVM, unless subsequently changed by other code.
  2. logs the task's start and stop dates, as well as the total execution time
  3. ensures that any Throwable raised by the task is either logged or rethrown as a RuntimeException
Additionally, the following may/may not occur, depending on what method of this class is called (see each method's javadocs for details):
  1. executes the task on EventQueue's dispatch thread instead of the calling thread (this is required by Java GUI components)
  2. plays an appropriate sound, depending on whether task finishes normally or abnormally
  3. forces the JVM to exit with an appropriate exit code, depending on whether task finishes normally or abnormally

This functionality is exposed via static methods like thenContinue, thenExitIfEntryPoint, usingEdt.

The original motivation of this class was to simplify and standardize the implementation of main methods, since they often require the same boilerplate code (the functionality described above). One way to write a (non-GUI) main method using this class is:


        /**
 ...
 

If this method is this Java process's entry point (i.e. first main method), then its final action is a call to System.exit, which means that this method never returns; its exit code is 0 if it executes normally, 1 if it throws a Throwable (which will be caught and logged). Otherwise, this method returns and leaves the JVM running. / public static void main(final String[] args) { Execute.thenExitIfEntryPoint( new Callable() { public Void call() throws Exception { // insert here whatever specific code your main is supposed to do return null; } } ); }

If the purpose is to launch a GUI, then this form almost certainly should be used:

        /**
 Creates a GUI and then returns.
/
        public static void main(final String[] args) {
                Execute.usingEdt( new Runnable() { public void run() {
                        // insert here whatever specific code your main is supposed to do
                } } );
        }
 

Concerning the execution time that is always logged by this class: it can serve as a quick and dirty benchmark only if a) task runs long enough that a single execution yields an accurate execution time and b) you do not care about execution statistics (i.e. means, standard deviations, confidence intervals). For all serious benchmarking needs, use Benchmark. Here is an example of how some quick benchamrks may be obtained:


        /**
 Benchmarks a series of tasks; see the logs (files and/or console) for results.
/
        public static void main(final String[] args) {
                Execute.thenContinue( new Runnable() { public void run() {
                        // insert here code for task #1
                } } );

                Execute.thenContinue( new Runnable() { public void run() {
                        // insert here code for task #2
                } } );

                // etc...
        }
 

This class is multithread safe: its public api is static methods which use no shared state. (Its instance state is completely encapsulated, and is mostly immutable or thread safe).

Author:
Brent Boyer

Nested Class Summary
private static class Execute.Caller
          Records information about the class that is calling Execute: its class name, method name, and whether or not it is the actual program entry point of the current Java process.
private static class Execute.TestGui
           
static class Execute.UnitTest
          See the Overview page of the project's javadocs for a general description of this unit test class.
 
Field Summary
private  String classCalling
           
private  boolean exitWhenDone
           
private  Level levelEvents
           
private  Logger logger
           
private  String methodCalling
           
private  boolean soundOnTaskSuccess
           
private  Object task
           
 
Constructor Summary
private Execute(String classCalling, String methodCalling, Object task, Level levelEvents, boolean soundOnTaskSuccess, boolean exitWhenDone, Logger logger)
          Constructor.
 
Method Summary
private  void ensureUncaughtThrowablesHandled()
           
private  Object executeTask()
           
private  void log(Level level, String msg, Throwable t)
           
private  void logProblem(Throwable t)
          Contract: should never throw any Throwable, since may be implicitly used in a finally clause.
private  void logStart()
           
private  void logStop(long t1)
          Contract: should never throw any Throwable, since will be used in a finally clause.
private  Object perform()
          Fundamental API method that implements the essential functionality of this class.
private  void playSound(int exitCode)
          Contract: should never throw any Throwable, since will be used in a finally clause.
private  void seeIfShouldExit(int exitCode)
          Contract: should never throw any Throwable, since will be used in a finally clause.
static
<T> T
thenContinue(Callable<T> task)
          Returns thenContinue(task, LogUtil.getLogger2()).
static
<T> T
thenContinue(Callable<T> task, Logger logger)
          Executes task, returns its result (or rethrows any Throwable task threw as a RuntimeException), and leaves the JVM running.
static void thenContinue(Runnable task)
          Simply calls thenContinue(task, LogUtil.getLogger2()).
static void thenContinue(Runnable task, Logger logger)
          Same as thenContinue, except that task is a Runnable, and so has no result.
private static Object thenContinueImpl(Object task, Logger logger)
           
static
<T> T
thenExitIfEntryPoint(Callable<T> task)
          Returns thenExitIfEntryPoint(task, LogUtil.getLogger2()).
static
<T> T
thenExitIfEntryPoint(Callable<T> task, Logger logger)
          Executes task and plays a distinct sound depending on if task returned normally or threw a Throwable.
static void thenExitIfEntryPoint(Runnable task)
          Simply calls thenExitIfEntryPoint(task, LogUtil.getLogger2()).
static void thenExitIfEntryPoint(Runnable task, Logger logger)
          Same as thenExitIfEntryPoint, except that task is a Runnable, and so has no result.
private static Object thenExitIfEntryPointImpl(Object task, Logger logger)
           
static
<T> void
usingEdt(Callable<T> task)
          Simply calls usingEdt(task, LogUtil.getLogger2()).
static
<T> void
usingEdt(Callable<T> task, Logger logger)
          Schedules task for asynchronous execution on EventQueue's dispatch thread (aka the EDT), so this method soon returns, and leaves the JVM running.
static void usingEdt(Runnable task)
          Simply calls usingEdt(task, LogUtil.getLogger2()).
static void usingEdt(Runnable task, Logger logger)
          Same as usingEdt, except that task is a Runnable, and so has no result.
private static void usingEdtImpl(Object task, Logger logger)
           
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

classCalling

private final String classCalling

methodCalling

private final String methodCalling

task

private final Object task

levelEvents

private final Level levelEvents

soundOnTaskSuccess

private final boolean soundOnTaskSuccess

exitWhenDone

private final boolean exitWhenDone

logger

private final Logger logger
Constructor Detail

Execute

private Execute(String classCalling,
                String methodCalling,
                Object task,
                Level levelEvents,
                boolean soundOnTaskSuccess,
                boolean exitWhenDone,
                Logger logger)
         throws IllegalArgumentException
Constructor.

Parameters:
classCalling - name of the class which is calling Execute; this is the class name that will be used in the log records
methodCalling - name of the method which is calling Execute; this is the method name that will be used in the log records
task - wraps arbitrary code to be executed
levelEvents - the logging Level to use for normal (non-Throwable) events
soundOnTaskSuccess - if true, causes a sound to be played if task completes normally (a sound is always played if task throws a Throwable)
exitWhenDone - if true, then a) ensures that System.exit is called at the end of this method (with an exit code of 0 if task.call returned normally, and an exit code of 1 if task.call threw some Throwable), which means that this method never actually returns to the caller, b) a sound is played when task returns normally (a sound is always played if task returns abnormally), and c) task's return value or Throwable thrown are logged; if false, then control should return to the caller (unless task or some other code calls System.exit)
logger - used to log all events to (start/stop dates and execution times, uncaught Throwables, etc)
Throws:
IllegalArgumentException - if if classCalling or methodCalling is blank; task is null; task is not an instanceof Callable or Runnable; levelEvents is null; logger is null
Method Detail

thenContinue

public static <T> T thenContinue(Callable<T> task)
                      throws IllegalArgumentException,
                             RuntimeException
Returns thenContinue(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

thenContinue

public static <T> T thenContinue(Callable<T> task,
                                 Logger logger)
                      throws IllegalArgumentException,
                             RuntimeException
Executes task, returns its result (or rethrows any Throwable task threw as a RuntimeException), and leaves the JVM running. A sound is played only if task throws a Throwable (nothing is played if it returns normally). All normal events (e.g. task's start/stop dates and execution time) are logged at Level.INFO (Throwables are always logged at Level.SEVERE).

Throws:
IllegalArgumentException - if the constructor objects to one of this method's params, or some other method on the call stack throws this
RuntimeException - (or some subclass) if any other Throwable is thrown while executing task; this may merely wrap the underlying Throwable

thenContinue

public static void thenContinue(Runnable task)
                         throws IllegalArgumentException,
                                RuntimeException
Simply calls thenContinue(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

thenContinue

public static void thenContinue(Runnable task,
                                Logger logger)
                         throws IllegalArgumentException,
                                RuntimeException
Same as thenContinue, except that task is a Runnable, and so has no result.

Throws:
IllegalArgumentException
RuntimeException

thenContinueImpl

private static Object thenContinueImpl(Object task,
                                       Logger logger)
                                throws IllegalArgumentException,
                                       RuntimeException
Throws:
IllegalArgumentException
RuntimeException

thenExitIfEntryPoint

public static <T> T thenExitIfEntryPoint(Callable<T> task)
                              throws IllegalArgumentException,
                                     RuntimeException
Returns thenExitIfEntryPoint(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

thenExitIfEntryPoint

public static <T> T thenExitIfEntryPoint(Callable<T> task,
                                         Logger logger)
                              throws IllegalArgumentException,
                                     RuntimeException
Executes task and plays a distinct sound depending on if task returned normally or threw a Throwable. If the class calling this method is the current Java process's entry point (i.e. first main method) then logs task's result (or any Throwable it threw) and calls System.exit with an appropriate exit code (0 if task returned normally, 1 if it threw a Throwable), which means that this method never returns. Otherwise, this method returns task's result (or rethrows any Throwable it threw as a RuntimeException) and leaves the JVM running. All normal events (e.g. task's start/stop dates and execution time) are logged at Level.INFO (Throwables are always logged at Level.SEVERE).

Altho many Java style guides warn against programs explicitly forcing JVM exit, this behavior is necessary if the program that main belongs to is actually is part of a sequence of programs that rely on exit codes to stop processing in the event of failure.

Throws:
IllegalArgumentException - if the constructor objects to one of this method's params, or some other method on the call stack throws this
RuntimeException - (or some subclass) if any other Throwable is thrown while executing task; this may merely wrap the underlying Throwable

thenExitIfEntryPoint

public static void thenExitIfEntryPoint(Runnable task)
                                 throws IllegalArgumentException,
                                        RuntimeException
Simply calls thenExitIfEntryPoint(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

thenExitIfEntryPoint

public static void thenExitIfEntryPoint(Runnable task,
                                        Logger logger)
                                 throws IllegalArgumentException,
                                        RuntimeException
Same as thenExitIfEntryPoint, except that task is a Runnable, and so has no result.

Throws:
IllegalArgumentException
RuntimeException

thenExitIfEntryPointImpl

private static Object thenExitIfEntryPointImpl(Object task,
                                               Logger logger)
                                        throws IllegalArgumentException,
                                               RuntimeException
Throws:
IllegalArgumentException
RuntimeException

usingEdt

public static <T> void usingEdt(Callable<T> task)
                     throws IllegalArgumentException,
                            RuntimeException
Simply calls usingEdt(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

usingEdt

public static <T> void usingEdt(Callable<T> task,
                                Logger logger)
                     throws IllegalArgumentException,
                            RuntimeException
Schedules task for asynchronous execution on EventQueue's dispatch thread (aka the EDT), so this method soon returns, and leaves the JVM running. When it is eventually executed on the EDT, task's result (or any Throwable it throws) is always logged to logger. A sound is played only if task throws a Throwable (nothing is played if it returns normally). All normal events (e.g. task's start/stop dates and execution time) are logged at Level.INFO (Throwables are always logged at Level.SEVERE).

Throws:
IllegalArgumentException - if the constructor objects to one of this method's params, or some other method on the call stack throws this
RuntimeException - (or some subclass) if any other Throwable is thrown while executing task; this may merely wrap the underlying Throwable

usingEdt

public static void usingEdt(Runnable task)
                     throws IllegalArgumentException,
                            RuntimeException
Simply calls usingEdt(task, LogUtil.getLogger2()).

Throws:
IllegalArgumentException
RuntimeException

usingEdt

public static void usingEdt(Runnable task,
                            Logger logger)
                     throws IllegalArgumentException,
                            RuntimeException
Same as usingEdt, except that task is a Runnable, and so has no result.

Throws:
IllegalArgumentException
RuntimeException

usingEdtImpl

private static void usingEdtImpl(Object task,
                                 Logger logger)
                          throws IllegalArgumentException,
                                 RuntimeException
Throws:
IllegalArgumentException
RuntimeException

perform

private Object perform()
                throws RuntimeException
Fundamental API method that implements the essential functionality of this class.

Returns:
the result of task.call
Throws:
RuntimeException - (or some subclass) if task.call throws it; this may wrap the underlying Throwable

ensureUncaughtThrowablesHandled

private void ensureUncaughtThrowablesHandled()

logStart

private void logStart()

logStop

private void logStop(long t1)
Contract: should never throw any Throwable, since will be used in a finally clause.


logProblem

private void logProblem(Throwable t)
Contract: should never throw any Throwable, since may be implicitly used in a finally clause.


log

private void log(Level level,
                 String msg,
                 Throwable t)

executeTask

private Object executeTask()
                    throws Exception
Throws:
Exception

playSound

private void playSound(int exitCode)
Contract: should never throw any Throwable, since will be used in a finally clause.


seeIfShouldExit

private void seeIfShouldExit(int exitCode)
Contract: should never throw any Throwable, since will be used in a finally clause.