Seam Module Spotlight: Seam Catch

Posted by    |       Seam

This is my first in.relation.to entry! Hello everyone, my name is Jason Porter, author of Seam Catch. You'll see more Seam 3 / CDI posts from me in the future, for now, let's get to our first Seam Module spotlight:

For our first entry in the Seam Module spotlight we're placing Seam Catch front and center! Catch is the go to module for handling exceptions in your application. You no longer need to worry about what to do in those catch blocks, or how exceptions will be handled in a uniform manner. As long as you're using Catch (either through an integration via another module such as Servlet, Faces, Rest, etc. or via manual integration it is the same) all of those concerns are taken care of, all you have to do is create the handlers, which are very easy and follow a very deterministic order. The following examples assume you have a basic working knowledge of Catch, to understand the basics please see the reference documentation.

In this example we want three different actions to happen based on the type of exception:

  1. Log each exception
  2. Rollback the transaction on all PersistenceExceptions
  3. End the current long running conversation for all of our application exceptions

This is done by creating three very simple exception handlers:

    @HandlesExceptions
    public class StandardHandlers {
        public void rollbackTransaction(@Handles CaughtException<PersistenceException> event, UserTransaction tx) {
            try {
                tx.rollback();
            }
            // catch any jta exceptions
            event.handled();
        }

        public void logExceptions(@Handles(during = TraversalMode.BREADTH_FIRST, precedence = Precedence.HIGH) CaughtException<Throwable> evt, Log log) {
            log.error("Exception caught: " + evt.getException().getMessage());
        }

        public void endConversation(@Handles CaughtException<MyBaseApplicationException> evt, Conversation conv) {
            if (!conv.isTransient())
                conv.end();
        }
    }

The first handler (method rollbackTransaction(...)) is notified when any javax.persistence.PersistenceException is passed to Catch, it also has the current transaction as an injection point. At this point, all we want to is rollback the transaction that was injected. We're also marking the exception as handled, meaning we don't need any further handlers to be notified about this exception.

The next exception handler, logExceptions(...) is invoked for every exception that Catch receives. I know you're wondering about that statement in the above paragraph about marking the exception as handled and no other exception handlers will be notified. Catch traverses the exception hierarchy for each exception (and each exception wrapped) looking first for handlers that match the exception hierarchy that are to be notified during the BREADTH_FIRST cycle, then handlers that are marked as DEPTH_FIRST (the default). You'll notice our logging handler is BREADTH_FIRST and also pertains to all instances of Throwable, it also has a high precedence, meaning any other handlers of the same type (Throwable) will be notified after this handler executes. We're simply going to log this exception and continue on with the rest of the handlers (by default a handler that does not invoke any of the handler chain status methods will be treated as markAsHandled() and continue with the rest of the handlers). Any handler that has already been called for a particular exception passed to Catch will never be called more than once.

The last handler in our example is endConversation(...). Again, not very much to do here, ideally your handlers should be fairly simple. We're simply checking to see if the user has a long running conversation and ending that conversation when Catch receives an application exception, meaning an exception specific to your application; in other words, an exception you have created. This handler will be be notified after the logging handler.

That about wraps it up for this edition of Seam Module spot... what, how do you pass an exception to Catch? This question has come up a few times on the Seam Forums, so I'll answer it here. If you are using another Seam Module that has Catch integration (at the time of writing those are Servlet, Faces and Rest): nothing, it's already done for you! Any exception that is not explicitly handled via a try/catch block will be passed to Catch. In Faces it uses the JSF2 exception handling APIs to pass all exceptions to Catch, with Rest it uses the JAX-RS exception handling APIs and with Servlet, a servlet filter that is around each request passes the exception to Catch. If you are not using one of these other modules, or you want Catch to be used in your own try/catch blocks you need to do a manual integration by using the CDI Event class:

    public MyClass {
        ...
        @Inject Event<ExceptionToCatch> catchEvt 

        try {
            ...
        }
        catch (...) {
            catchEvt.fire(new ExceptionToCatch(Exception));
        }

        ....
    }

That's it, two lines of CDI code and Catch can be used for all your exception handling needs! With that, thank you for reading our first Seam Module spotlight! Stay tuned for spotlights on other Seam Modules. Other Catch Spotlights may include StackTrace Filtering: the ability to modify the stack trace of an exception, and exception mapping: the very handy feature of changing an exception for another exception such as mapping SQLException instances to actual meaningful exceptions, similar to what's available in the Spring Framework, but can be easily customized to your own application.


Back to top