The Seam Catch module creates a simple, yet robust base for other modules and users to create a custom and complete exception handling process. Exception handling is done using CDI events, keeping exception handling noninvasive and also helping the program or module to stay minimally coupled to the exception handling framework.
The Seam Catch API is the only compile time dependency a project needs, and an implementation must also be included, either explicitly or via some other module depending on it (and exposing their own specialized extensions) is all that is needed during runtime. If you are using Maven as your build tool, you can add the following dependency to your pom.xml file:
<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>
The runtime dependency is only needed if another Seam 3 module being used doesn't already use it. Typically this will only be for Java SE development.
Replace ${seam-catch-version} with the most recent or appropriate version of Seam Catch.
An end user of the Seam Catch Framework is typically only concerned with Exception Handlers (methods in Handler
Beans, which are similar to CDI Observers). Handler Beans are CDI beans with the
@HandlesExceptions
annotation. There may be other resources made available by other modules
which can be injected into handler methods on an as needed basis. For further information, please check the API
docs, or examples.
The
@HandlesException
annotation is simply a marker annotation instructing the Seam
Catch CDI extension to scan the bean for handler methods.
Example
@HandlesExceptions
public class MyHandlers
{
public void catchAllHandler(@Handles(during = TraversalPath.DESCENDING) @MyFramework CaughtException<Throwable> event, Logger log)
{
log.warn("Exception occured: " + event.getException().getMessage());
}
}
This is a complete and valid handler showing all the current features of handlers. The Handler Bean is
defined by the class level annotation
@HandlesExceptions
and the actual handler is
defined by the method that takes a
CaughtException
of type
Throwable
which
is annotated using the
@Handles
annotation. Also notice the handler is qualified using
the
@MyFramework
qualifier. This works the same as qualifers in CDI Observers,
it will only be invoked for exceptions (it catches typeThrowable
) where the
initial
ExceptionToCatchEvent
was created with the
@MyFramework
annotation passed to the constructor. The Logger instance is also injected into the handler when it is
invoked. The handler has a default precedence of 0 and a
TraversalPath
of
DESCENDING
. It does not modify flow control of other handlers however and simply uses
the default proceed.
@Handles
is a parameter annotation that specifies a method as an exception handler. It
acts similar to the
@Observes
annotation from CDI. In addition to promoting a normal
method to an exception handler it also carries data about the handler:
TraversalPath.ASCENDING
being default)
The
@Handles
annotation must be placed on the first parameter of the method, which must
be of type CaughtException. Handler methods are similar to CDI Obeservers and follow the same principals and
guidelines (such as invocation, injection of parameters, qualifiers, etc). They differ from Observer
methods in that:
A handler is guaranteed to only be invoked once per exception (unless it is unmuted via the CaughtException
object by callingunMute()
). Handlers must not throw checked exceptions, and should
avoid throwing unchecked exceptions.
Adding a handler is simply creating a class and a method the follows the above rules (class annotated with
@HandlesExceptions
and a method with the first parameter being a CaughtException and annotated
with@Handles
). Catch will discover all handler methods at deploy time. See the example
above for a simple, but complete handler.
The ordering of handlers is multifaceted. Based on the traversal path of the causing container handlers are
ordered according to the hiearchy of the excption type (most specific first if
TraversalPath.ASCENDING
, least specific first if
TraversalPath.DESCENDING
traversal), and the precedence when two handlers are for the
same exception type.
The
precedence
of a handler helps determine the order of the handler relative to other
handlers of the same exception type. It follows a high-to-low integer schema (the higher the precedence, the
sooner the handler is invoked during traversal of the causing chain).
When an exception is handled with Catch the causing container is unwrapped to get at each exception. The
first pass (TraversalPath.DESCENDING
) starts with the outer most exception working it's
way to the root exception. The traversal is then reversed and traversed from root cause up. This allows
handlers to take part in various stages of the causing container. At each entry in the container, handlers
are invoked based on the exception type (either an exact match or a super type) of the entry. For example
if the exception type isSocketException
, handlers for types
SocketException
,
IOException,
Exception
and
Throwable
would all invoked (in that order), however, a handler for
BindException
would not be invoked.
There are other objects used in Catch that should be familiar to handler writers namely
CaughtException
CauseContainer
CaughtException
contains methods to interact with the handling procces, allowing a level of flow
control to be available to handler (such as re-throwing the exception, or aborting), and allowing a handler
to
be unmuted. Once a handler is invoked it is muted, meaning it will not be run again for that causing
container,
unless it is explicitly marked as unmuted via the
CaughtException.unMute()
object.
Five methods exist on the
CaughtException
object to give flow control to the handler
abort()
- terminate all handling immediately after this handler, does not mark the
exception as handled, does not re-throw the exception.
rethrow()
- continues through all handlers, but once all handlers have been called
(assuming another handler does not call abort() or handled()) the initial exception passed to Catch is
rethrown. Does not mark the exception as handled.
handled()
- marks the exception as handled and terminates further handling.
proceed()
- default. Marks the exception as handled and proceeds with the rest of the
handlers.
proceedToCause()
- marks the exception as handled, but proceeds to the next cause in
the cause container, without calling other handlers for the current cause.
Integration of Seam Catch with other frameworks consists of one main step, and one other optional (but highly encouraged) step:
ExceptionToCatchEvent
An
ExceptionToCatchEvent
is constructed by passing a
Throwable
and
optionally qualifiers for handlers. Firing the event is done via CDI events (either straight from the
BeanManager
or injecting a
Event<ExceptionToCatchEvent>
and calling fire).
To ease the burden on the application developers, the integration should tie into the exception handling
mechanism of the integrating framework, if any exist. By tying into the framework's exception handling,
any uncaught exceptions should be routed through the Seam Catch system and allow handlers to be invoked.
This is the typical way of using the Seam Catch framework. Of course, it doesn't stop the application
developer from firing their own
ExceptionToCatchEvent
within a catch block.
An integration with Catch can define it's own handlers to always be used. It's recommended that any built-in handler from an integration have a very low precedence, be a handler for as generic an exception as is suitable (i.e. Seam Persistence could have a built-in handler for PersistenceExceptions to rollback a transaction, etc), and make use of qualifiers specific for the integration. This helps limit any collisions with handlers the application developer may create.
Catch supports qualifiers for theCaughtException
. To add a qualifier to be used when
firing
handlers they must be add to the
ExceptionToCatchEvent
via the constructor (please see
API docs for more info). Qualifiers for integrations should be used to avoid collisions in the application
error handling both when defining handlers and when firing events from the integration.