Random bits of information by a developer

08 December 2010

Seam Catch Alpha2

Over the weekend Seam Catch Alpha2 was released! I'm trying to do a two week timebox release cycle for Seam Catch. Alpha 2 contains some minor API changes (some additions, and a couple of renames), a new qualifier and a couple of new objects for better framework integration. The largest of these enhancements is the ability to support ServiceHandlers.

Qualifier changes

The @HandlesExceptions is no longer a qualifier, this was needed to support ServiceHandlers, and is simply a marker anyway. This shouldn't affect users.

We've added a new qualifier @CatchResource to help distinguish resources injected into handlers that are only for Catch use. Here's an example:

public void logToSpecialCatchLog(@Handles CaughtException<Throwable> e, @CatchResource Logger log)
{
    log.warn("Unexpected exception caught: " + e.getException().getMessage());
}
      
It doesn't do much, but this shows that the Logger instance being injected is a special log configured only for Catch. This qualifier can be applied to anything and helps the developer visually see what resources are really being used.

ServiceHandlers

This feature is a great feature that really makes exception handling easy. So far we've seen examples of creating handlers with full method implementations, if you're doing something specialized, but repetitive, such as a JAX-RS error response you end up writing the boiler plate code over and over again just to change a few things (the message and the status code). Now you can do this sort of thing with only writing the boiler plate part once, the rest is all declarative, take a look!
@HandlesExceptions
@ExceptionResponseService
public interface DeclarativeRestExceptionHandlers
{           
   @SendHttpResponse(status = 404, message = "Requested resource does not exist")
   void onNoResult(@Handles @RestRequest CaughtException<NoResultException> e);

   @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);
}
      
There are three handlers right there, nothing else (save the [ServiceHandler][] implemenation) is needed! These handlers all send an error response for a JAX-RS request. They all change the status code returned and the message. What does it take to implement this? Aside from the new annotation (@SendHttpResponse), there's two other classes that need to be written:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ServiceHandler(ExceptionResponseServiceHandler.class)
public @interface ExceptionResponseService
{               
} 

public class ExceptionResponseServiceHandler
{
   @Inject @CatchResource
   private Instance<ResponseBuilder> builderProvider;

   @AroundInvoke
   public Object processException(InvocationContext ctx)
   {
      Method m = ctx.getMethod();
      if (ctx.getParameters().length > 0 && ctx.getParameters()[0] instanceof CaughtException)
      {
         ResponseBuilder builder = builderProvider.get();
         CaughtException<?> c = (CaughtException<?>) ctx.getParameters()[0];
         if (m.isAnnotationPresent(SendHttpResponse.class))
         {
            SendHttpResponse r = m.getAnnotation(SendHttpResponse.class);
            String message = r.message();
            if (r.message().length() == 0 && r.passthru())
            {
               message = c.getException().getMessage();
            }

            builder.status(r.status());
            if (message != null && message.length() > 0)
            {
               builder.entity(new ErrorMessageWrapper(message));
            }
         }
         else
         {
            builder.entity(new ErrorMessageWrapper("Unknown error"));
         }
      }
      return Void.TYPE;
   }
}
Most of this is building up the error from the annotation, but it's only 26 LoC, and you quite a bit of reuse from them. That's all there is to it! Framework integrations should implement some of these for basic things, but you can further create them for yourself if you have some repetitive action that you can extract in your exception handling practices. Look for another release (Alpha3) in a couple of weeks and a Beta out by the end of the year!

No comments: