Working with exceptions

Exceptions can be logged via SLF4J Logger, or given to exception handler for centralized, consistent exception handling, or translated into other exceptions. Scout provides some few exceptions/errors, which are used by the framework.

Scout Throwables

All scout throwables are unchecked and typically implementing the IThrowableWithContextInfo interface, which provides functionality for associating context information with the occurred error.

Most scout throwables are runtime exceptions, and typically inherit from PlatformException. See Scout Runtime Exceptions for more information.

Some scout throwables are instances of java.lang.Error by extending PlatformError. Those errors usually provide functionality to interrupt Jobs, for example when a user is canceling a long running operation.
Note: PlatformErrors should never be catched by business logic! See Scout Runtime Errors for more information.

Scout Runtime Exceptions

PlatformException

Base runtime exception of the Scout platform, which allows for message formatting anchors and context information to be associated.

There is a single constructor which accepts the exception’s message, and optionally a variable number of arguments. Typically, a potential cause is given as its argument. The message allows further the use of formatting anchors in the form of {} pairs. The respective formatting arguments are provided via the constructor’s varArg parameter. If the last argument is of the type Throwable and not referenced as formatting anchor in the message, that Throwable is used as the exception’s cause. Internally, SLF4J MessageFormatter is used to provide substitution functionality. Hence, The format is the very same as if using SLF4j Logger.

Further, PlatformException allows to associate context information, which are available in SLF4J diagnostic context map (MDC) upon logging the exception.

Listing 1. PlatformException examples
Exception cause = new Exception();

// Create a PlatformException with a message
new PlatformException("Failed to persist data");

// Create a PlatformException with a message and cause
new PlatformException("Failed to persist data", cause);

// Create a PlatformException with a message with formatting anchors
new PlatformException("Failed to persist data [entity={}, id={}]", "person", 123);

// Create a PlatformException with a message containing formatting anchors and a cause
new PlatformException("Failed to persist data [entity={}, id={}]", "person", 123, cause);

// Create a PlatformException with context information associated
new PlatformException("Failed to persist data", cause)
    .withContextInfo("entity", "person")
    .withContextInfo("id", 123);

ProcessingException

Represents a PlatformException and is thrown in case of a processing failure, and which can be associated with an exception error code and severity.

VetoException

Represents a ProcessingException with VETO character. If thrown server-side, exceptions of this type are transported to the client and typically visualized in the form of a message box.

AssertionException

Represents a PlatformException and indicates an assertion error about the application’s assumptions about expected values.

TransactionRequiredException

Represents a PlatformException and is thrown if a ServerRunContext requires a transaction to be available.

Scout Runtime Errors

Runtime Errors are used to indicate an error, that shouldn’t be catched/treated by business logic and therefore bubble up to the appropriate exception handler in the scout framework. Because those errors are handled by the framework internals, they should never be catched on the server (Services etc.) nor on the client side (Pages, Forms, etc.).

All Scout Runtime Errors extend PlatformError.

PlatformError

Like PlatformException, PlatformErrors implement IThrowableWithContextInfo for associating context information with the occurred error. See PlatformException for usage and example code.

ThreadInterruptedError

Represents a PlatformError and indicates that a thread was interrupted while waiting for some condition to become true, e.g. while waiting for a job to complete. Unlike java.lang.InterruptedException, the thread’s interrupted status is not cleared when catching this exception.

FutureCancelledError

Represents a PlatformError and indicates that the result of a job cannot be retrieved, or the IFuture’s completion not be awaited because the job was cancelled.

TimedOutError

Represents a PlatformError and indicates that the maximal wait time elapsed while waiting for some condition to become true, e.g. while waiting a job to complete.

Exception handling

An exception handler is the central point for exception handling. It provides a single method 'handle' which accepts a Throwable, and which never throws an exception. It is implemented as a bean, meaning managed by the bean manager to allow easy replacement, e.g. to use a different handler when running client or server side. By default, a ProcessingException is logged according to its severity, a VetoException, ThreadInterruptedError or FutureCancelledError logged in DEBUG level, and any other exception logged as an ERROR. If running client side, exceptions are additionally visualized and showed to the user.

Exception translation

Exception translators are used to translate an exception into another exception.

Also, they unwrap the cause of wrapper exceptions, like UndeclaredThrowableException, or InvocationTargetException, or ExecutionException. If the exception is of the type Error, it is normally not translated, but re-thrown instead. That is because an Error indicates a serious problem due to an abnormal condition.

DefaultExceptionTranslator

Use this translator to work with checked exceptions and runtime exceptions, but not with Throwable.

If given an Exception, or a RuntimeException, or if being a subclass thereof, that exception is returned as given. Otherwise, a PlatformException is returned which wraps the given Throwable.

DefaultRuntimeExceptionTranslator

Use this translator to work with runtime exceptions. When working with RunContext or IFuture, some methods optionally accept a translator. If not specified, this translator is used by default.

If given a RuntimeException, it is returned as given. For a checked exception, a PlatformException is returned which wraps the given checked exception.

PlatformExceptionTranslator

Use this translator to work with PlatformExceptions.

If given a PlatformException, it is returned as given. For all other exceptions (checked or unchecked), a PlatformException is returned which wraps the given exception.

Typically, this translator is used if you require to add some context information via IThrowableWithContextInfo.withContextInfo(String, Object, Object).

Listing 2. PlatformException examples
try {
  // do something
}
catch (Exception e) {
  throw BEANS.get(PlatformExceptionTranslator.class).translate(e)
      .withContextInfo("cid", "12345")
      .withContextInfo("user", Subject.getSubject(AccessController.getContext()))
      .withContextInfo("job", IFuture.CURRENT.get());
}

NullExceptionTranslator

Use this translator to work with Throwable as given.

Also, if given a wrapped exception like UndeclaredThrowableException, InvocationTargetException or ExecutionException, that exception is returned as given without unwrapping its cause.

For instance, this translator can be used if working with the Job API, e.g. to distinguish between a FutureCancelledError thrown by the job’s runnable, or because the job was effectively cancelled.

Exception Logging

The Scout framework logs via SLF4J (Simple Logging Facade for Java). It serves as a simple facade or abstraction for various logging frameworks (e.g. java.util.logging or logback) allowing the end user to plug in the desired logging framework at deployment time.

SLF4J allows the use of formatting anchors in the form of {} pairs in the message which will be replaced by the respective argument. If the last argument is of the type Throwable and not referenced as formatting anchor in the message, that Throwable is used as the exception.

Listing 3. Logging examples
Exception e = new Exception();

Logger logger = LoggerFactory.getLogger(getClass());

// Log a message
logger.error("Failed to persist data");

// Log a message with exception
logger.error("Failed to persist data", e);

// Log a message with formatting anchors
logger.error("Failed to persist data [entity={}, id={}]", "person", 123);

// Log a message and exception with a message containing formatting anchors
logger.error("Failed to persist data [entity={}, id={}]", "person", 123, e);