SeamFramework.orgCommunity Documentation

Seam Catch


1. Seam Catch - Introduction
2. Seam Catch - Installation
2.1. Maven dependency configuration
3. Seam Catch - Usage
3.1. Exception handlers
3.2. Exception handler annotations
3.2.1. @HandlesExceptions
3.2.2. @Handles
3.3. Exception stack trace processing
3.4. Exception handler ordering
3.4.1. Traversal of exception type hierarchy
3.4.2. Handler precendence
3.5. APIs for exception information and flow control
3.5.1. CaughtException
3.5.2. ExceptionStack
4. Seam Catch - Filtering Stack Traces
4.1. Introduction
4.2. ExceptionStackOutput
4.3. StackFrameFilter
4.4. StackFrameFilterResult
4.5. StackFrame
5. Seam Catch - Framework Integration
5.1. Creating and Firing an ExceptionToCatch event
5.2. Default Handlers and Qualifiers
5.2.1. Default Handlers
5.2.2. Qualifiers
5.3. Supporting ServiceHandlers
Seam Catch - Glossary

Exceptions are a fact of life. As developers, we need to be prepared to deal with them in the most graceful manner possible. Seam Catch provides a simple, yet robust foundation for modules and/or applications to establish a customized exception handling process. By employing a delegation model, Catch allows exceptions to be addressed in a centralized, extensible and uniform manner.

Catch is first notified of an exception to be handled via a CDI event. This event is fired either by the application or a Catch integration. Catch then hands the exception off to a chain of registered handlers, which deal with the exception appropriately. The use of CDI events to connect exceptions to handlers makes this strategy of exception handling non-invasive and minimally coupled to Catch's infrastructure.

The exception handling process remains mostly transparent to the developer. In some cases, you register an exception handler simply by annotating a handler method. Alternatively, you can handle an exception programmatically, just as you would observe an event in CDI.

In this guide, we'll explore the various options you have for handling exceptions using Catch, as well as how framework authors can offer Catch integration.

To use the Seam Catch module, you need to add the Seam Catch API to your project as a compile-time dependency. At runtime, you'll also need the Seam Catch implementation, which you either specify explicitly or through a transitive dependency of another module that depends on it (as part of exposing its own Catch integration).

First, check your application's library dependencies to see whether Seam Catch is already being included by another module (such as Seam Servlet). If not, you'll need to setup the dependencies as described below.

If you are using Maven as your build tool, you can add the following single dependency to your pom.xml file to include Seam Catch:


<dependency>
   <groupId>org.jboss.seam.catch</groupId>
   <artifactId>seam-catch</artifactId>
   <version>${seam.catch.version}</version>
</dependency>

Tip

Substitute the expression ${seam.catch.version} with the most recent or appropriate version of Seam Catch. Alternatively, you can create a Maven user-defined property to satisfy this substitution so you can centrally manage the version.

Alternatively, you can use the API at compile time and only include the implementation at runtime. This protects you from inadvertantly depending on an implementation class.


<dependency>
   <groupId>org.jboss.seam.catch</groupId>
   <artifactId>seam-catch-api</artifactId>
   <version>${seam.catch.version}</version>
   <scope>compile</scope>
</dependency>

<dependency>
   <groupId>org.jboss.seam.catch</groupId>
   <artifactId>seam-catch-impl</artifactId>
   <version>${seam.catch.version}</version>
   <scope>runtime</scope>
</dependency>

Now you're ready to start catching exceptions!

Exception handlers are contained within exception handler beans, which are CDI beans annotated with @HandlesExceptions. Exception handlers are methods which have a parameter which is an instance of CaughtException<T extends Throwable> annotated with the @Handles annotation.

@Handles is a method parameter annotation that designates a method as an exception handler. Exception handler methods are registered on beans annotated with @HandlesExceptions. Catch will discover all such methods at deployment time.

Let's look at an example. The following method is invoked for every exception that Catch processes and prints the exception message to stout. (Throwable is the base exception type in Java and thus represents all exceptions).

(1)@HandlesExceptions

public class MyHandlers
{
(2)   void printExceptions(@Handles CaughtException<Throwable> evt)
   {
      System.out.println("Something bad happened: " +
(3)            evt.getException().getMessage());
(4)      evt.markHandled();
   }
}
            

1

The @HandlesExceptions annotation signals that this bean contains exception handler methods.

2

The @Handles annotation on the first parameter designates this method as an exception handler (though it is not required to be the first parameter). This parameter must be of type CaughtException<T extends Throwable>, otherwise it's detected as a definition error. The type parameter designates which exception the method should handle. This method is notified of all exceptions (requested by the base exception type Throwable).

3

The CaughtException instance provides access to information about the exception and can be used to control exception handling flow. In this case, it's used to read the current exception being handled in the exception stack trace, as returned by getException().

4

This handler does not modify the invocation of subsequent handlers, as designated by invoking markHandled() on CaughtException. As this is the default behavior, this line could be omitted.

The @Handles annotation must be placed on a parameter of the method, which must be of type CaughtException<T extends Throwable>. Handler methods are similar to CDI observers and, as such, follow the same principles and guidelines as observers (such as invocation, injection of parameters, qualifiers, etc) with the following exceptions:

In addition to designating a method as exception handler, the @Handles annotation specifies two pieces of information about when the method should be invoked relative to other handler methods:

Let's take a look at more sophisticated example that uses all the features of handlers to log all exceptions.

(1)@HandlesExceptions

public class MyHandlers
{
(2)   void logExceptions(@Handles(during = TraversalMode.BREADTH_FIRST)
(3)         @WebRequest CaughtException<Throwable> evt,
(4)         Logger log)
   {
      log.warn("Something bad happened: " + evt.getException().getMessage());
   }
}
            

1

The @HandlesExceptions annotation signals that this bean contains exception handler methods.

2

This handler has a default precedence of 0 (the default value of the precedence attribute on @Handles). It's invoked during the breadth first traversal mode. For more information on traversal, see the section Section 3.4.1, “Traversal of exception type hierarchy”.

3

This handler is qualified with @WebRequest. When Catch calculates the handler chain, it filters handlers based on the exception type and qualifiers. This handler will only be invoked for exceptions passed to Catch that carry the @WebRequest qualifier. We'll assume this qualifier distinguishes a web page request from a REST request.

4

Any additional parameters of a handler method are treated as injection points. These parameters are injected into the handler when it is invoked by Catch. In this case, we are injecting a Logger bean that must be defined within the application (or by an extension).

A handler is guaranteed to only be invoked once per exception (automatically muted), unless it reenables itself by invoking the unmute() method on the CaughtException instance.

Handlers must not throw checked exceptions, and should avoid throwing unchecked exceptions. Should a handler throw an unchecked exception it will propegate up the stack and all handling done via Catch will cease. Any exception that was being handled will be lost.

When an exception is thrown, chances are it's nested (wrapped) inside other exceptions. (If you've ever examined a server log, you'll appreciate this fact). The collection of exceptions in its entirety is termed an exception stack trace.

The outermost exception of an exception stack trace (e.g., EJBException, ServletException, etc) is probably of little use to exception handlers. That's why Catch doesn't simply pass the exception stack trace directly to the exception handlers. Instead, it intelligently unwraps the stack trace and treats the root exception cause as the primary exception.

The first exception handlers to be invoked by Catch are those that match the type of root cause. Thus, instead of seeing a vague EJBException, your handlers will instead see an meaningful exception such as ConstraintViolationException. This feature, alone, makes Catch a worthwhile tool.

Catch continues to work through the exception stack trace, notifying handlers of each exception in the stack, until a handler flags the exception as handled. Once an exception is marked as handled, Catch stops processing the exception. If a handler instructed Catch to rethrow the exception (by invoking CaughtException#rethrow(), Catch will rethrow the exception outside the Catch infrastructure. Otherwise, it simply returns flow control to the caller.

Consider a stack trace containing the following nested causes (from outer cause to root cause):

Catch will unwrap this exception and notify handlers in the following order:

If there's a handler for PersistenceException, it will likely prevent the handlers for EJBException from being invoked, which is a good thing since what useful information can really be obtained from EJBException?

While processing one of the causes in the exception stack trace, Catch has a specific order it uses to invoke the handlers, operating on two axes:

We'll first address the traversal of the exception type hierarchy, then cover relative handler precedence.

Catch doesn't simply invoke handlers that match the exact type of the exception. Instead, it walks up and down the type hierarchy of the exception. It first notifies least specific handler in breadth first traversal mode, then gradually works down the type hiearchy towards handlers for the actual exception type, still in breadth first traversal. Once all breadth first traversal handlers have been invoked, the process is reversed for depth first traversal, meaning the most specific handlers are notified first and Catch continues walking up the hierarchy tree.

There are two modes of this traversal:

By default, handlers are registered into the DEPTH_FIRST traversal path. That means in most cases, Catch starts with handlers of the actual exception type and works up towards the handler for the least specific type.

However, when a handler is registered to be notified during the BREADTH_FIRST traversal, as in the example above, Catch will notify that exception handler before the exception handler for the actual type is notified.

Let's consider an example. Assume that Catch is handling the SocketException. It will notify handlers in the following order:

The same type traversal occurs for each exception processed in the stack trace.

In order for a handler to be notified of the IOException before the SocketException, it would have to specify the BREADTH_FIRST traversal path explicitly:



void handleIOException(@Handles(during = TraversalMode.BREADTH_FIRST)
      CaughtException<IOException> evt)
{
   System.out.println("An I/O exception occurred, but not sure what type yet");
}
         

BREADTH_FIRST handlers are typically used for logging exceptions because they are not likely to be short-circuited (and thus always get invoked).

To summarize, here's how Catch determines the order of handlers to invoke (until a handler marks exception as handled):

There are two APIs provided by Catch that should be familiar to application developers:

ExceptionStack contains information about the exception causes relative to the current exception cause. It is also the source of the exception types the invoked handlers are matched against. It is accessed in handlers by calling the method getExceptionStack() on the CaughtException object. Please see API docs for more information, all methods are fairly self-explanatory.

Tip

This object is mutable and can be modified before any handlers are invoked by an observer:

public void modifyStack(@Observes ExceptionStack stack) {

  ...
}
            

Modifying the ExceptionStack may be useful to remove exception types that are effectively meaningless sucsh as EJBException, changing the exception type to something more meaningful such as cases like SQLException, or wrapping exceptions as custom application exception types.





Integration of Seam Catch with other frameworks consists of one main step, and two other optional (but highly encouraged) steps:

  • creating and firing an ExceptionToCatch
  • adding any default handlers and qualifiers with annotation literals (optional)
  • supporting ServiceHandlers for creating exception handlers

ServiceHandlers make for a very easy and concise way to define exception handlers. The following example comes from the jaxrs example in the distribution:

@HandlesExceptions

@ExceptionResponseService
public interface DeclarativeRestExceptionHandlers
{
               
   @SendHttpResponse(status = 403, message = "Access to resource denied (Annotation-configured response)")
   void onNoAccess(@Handles @RestRequest CaughtException<AccessControlException> e);
   @SendHttpResponse(status = 400, message = "Invalid identifier (Annotation-configured response)")
   void onInvalidIdentifier(@Handles @RestRequest CaughtException<IllegalArgumentException> e);
}
      

All the vital information that would normally be done in the handler method is actually contained in the @SendHttpResponse annotation. The only thing left is some boiler plate code to setup the Response. In a jax-rs application (or even in any web application) this approach helps developers cut down on the amount of boiler plate code they have to write in their own handlers and should be implemented in any Catch integration, however, there may be situtations where ServiceHandlers simply do not make sense.

Note

If ServiceHandlers are implemented make sure to document if any of the methods are called from CaughtException, specifically abort(), handled() or rethrow(). These methods affect invocation of other handlers (or rethrowing the exception in the case of rethrow()).