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!