How to handle Exceptions
Running your own analysis can lead to errors or unwanted behavior. In those cases, which need to throw an exception, TeeTime provides a reliable mechanism to handle such. Following article explains how to make use of this mechanisms and how to extend it with own handlers.
Exception Throwing
If you need to throw an exception from your own stage, you can simply throw a RuntimeException or one of its subclasses. This exception will be catched by the framework forwarded to a listener, which decides on further procedures.
The Exception Listener
Once thrown, a listener can examine the exception and decide whether or not the analysis should continue to execute.
To explain how this works, we take a look at TeeTime’s LoggingExceptionListener:
class LoggingExceptionListener extends AbstractExceptionListener { @Override public FurtherExecution onStageException(final Exception e, final Stage throwingStage) { logger.warn("Exception occurred in " + throwingStage.getId(), e); // Send warning return FurtherExecution.CONTINUE; // Continue with execution } }
This listener extends from AbstractExceptionListener, which already provides a logger and a enum type to indicate the further execution strategy. Every ExceptionListener needs to implement the onStageException method, which will be called, if an exception is thrown by a stage. The original exception and the stage instance which has thrown it are passed on to this method, to enable a further examination of the whole case.
In our example, we take the provided logger and send a warning message to it. Furthermore, we decide to let the analysis continue with its execution. To do so, all we need is to return FurtherExecution.CONTINUE. If the execution should be terminated, we can tell the framework this by returning FurtherExecution.TERMINATE.
Selecting the desired Listener
TeeTime already comes with 3 different listeners:
- IgnoringExceptionListener (default in 1.x): Ignores the exception and does not abort the execution
- LoggingExceptionListener: Logs the exception and does not abort the execution
- TerminatingExceptionListener (default in 2.x): Terminates the analysis after the first thrown exception
You can either use one of this 3 listeners or implement your own by extending AbstractExceptionListener. TeeTime creates a new instance of the desired listener for every thread within the analysis, by using a factory. You can tell TeeTime to use a particular listener by passing over the corresponding factory to the constructor of the Configuration class.
Your own listener also needs a factory which creates new instances. For example:
public class LoggingExceptionListenerFactory implements IExceptionListenerFactory { @Override public AbstractExceptionListener createInstance() { return new LoggingExceptionListener(); } }
This is a simple implementation of the IExceptionListenerFactory interface. It just creates new instances of the LoggingExceptionListener and returns them to the framework.
We can now use it and pass it to the framework, like:
public class ExampleConfiguration extends Configuration { public ExceptionPassingTestConfig() { super(new LoggingExceptionHandler()); ... } }
With these 3 simple steps, you can define your own behavior upon thrown exceptions and use it with TeeTime.
Execution termination
If the Listener decides to terminate the execution, an ExecutionException is thrown which can be caught in Execution.executeBlocking() and Execution.waitForTermination().