Interceptors and Decorators tutorial

In this tutorial we will show two advanced features of CDI programming: Interceptors and Decorators. We will introduce them by using the Kitchesink example which will be upgraded to use these features.

An Interceptor is a class that is used to interpose in method invocations or lifecycle events that occur in an associated target class. The interceptor performs a separation of concern activity by doing tasks, such as logging or auditing, that are not related with the business logic of the application and that are repeated often within an application.

CDI Interceptors are pretty much the same as EJB interceptors, however since they are applied using a qualifier, you will at first define your interceptor qualifier, for example, here is a Log qualifier (which can be triggered by adding the @Log annotation on your Beans):

@InterceptorBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE, METHOD, FIELD})
@Qualifier

public @interface Log { }
}


Notice the @InterceptorBinding annotation which differentiates this specifies that this qualifier will be used as an Interceptor.

And now will code the Interceptor definition, which bears the qualifier annotation (@Log) at the top of it:
package org.jboss.as.quickstarts.kitchensink.interceptor;

import java.io.Serializable;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

import org.jboss.logging.Logger;

@Interceptor
@Log
public class LoggingInterceptor implements Serializable {

    private static final Logger logger =
            Logger.getLogger(LoggingInterceptor.class);
    @AroundInvoke
     public Object logMethodEntry(InvocationContext ctx) throws Exception {
           logger.info("Before entering method:" + ctx.getMethod().getName());
           return ctx.proceed();
     }
}

As you can see, this Interceptor barely traces method calling writing this information to the application logger. The ctx.proceed() just as for EJBs interceptors, will continue in the list of other Interceptors or finally land on the Bean method.

Now we can apply this annotation either at class level (and will intercept all method calls) or at method level, so it will intercept just that method call. Let's apply it to the MemberController bean so that it will trace the main application lifecycle:

@Model
@Log
public class MemberController {

    @Inject
    private FacesContext facesContext;

    @Inject
    private MemberRegistrationInt memberRegistration;

    @Produces
    @Named
    private Member newMember;

    @PostConstruct
    public void initNewMember() {
        newMember = new Member();
    }

    public void register() throws Exception {
        try {
            memberRegistration.register(newMember);
            FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO, "Registered!", "Registration successful");
            facesContext.addMessage(null, m);
            initNewMember();
        } catch (Exception e) {
            String errorMessage = getRootErrorMessage(e);
            FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR, errorMessage, "Registration unsuccessful");
            facesContext.addMessage(null, m);
        }
    }

    private String getRootErrorMessage(Exception e) {
        // Default to general error message that registration failed.
        String errorMessage = "Registration failed. See server log for more information";
        if (e == null) {
            // This shouldn't happen, but return the default messages
            return errorMessage;
        }

        // Start with the exception and recurse to find the root cause
        Throwable t = e;
        while (t != null) {
            // Get the message from the Throwable class instance
            errorMessage = t.getLocalizedMessage();
            t = t.getCause();
        }
        // This is the root cause message
        return errorMessage;
    }

}

In order to let your Interceptor kick-in, you need to declare it into your beans.xml file:

<interceptors>
   <class>org.jboss.as.quickstarts.kitchensink.interceptor.LoggingInterceptor</class>
</interceptors>

What about the order of interceptors ? in case you have more than one interceptor that will be used in your Bean, the order will be determined by your beans.xml, so for example, supposing that you have the following interceptors (bound respectively to LoggingInterceptor1 and LoggingInterceptor2):

@Log1
@Log2
public void register() throws Exception {

. . . . .
}

Here the LoggingInterceptor2 will be invoked at first since we have configured it as first in beans.xml
<interceptors>
   <class>org.jboss.as.quickstarts.kitchensink.interceptor.LoggingInterceptor2</class>
   <class>org.jboss.as.quickstarts.kitchensink.interceptor.LoggingInterceptor1</class>
</interceptors>

Additionally, if you have got several Interceptors in your chain, you can allow your interceptor inherit from another one, so with a single annotation you can describe a complex chain of interceptors.

This can be done by adding the @Inherited annotation and stating the qualifiers we are inheriting inside the qualifier. Here the @ComplexLog qualifier inherits from our @Log qualifier;

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Log
public @interface ComplexLog {
}

thus, once you have coded your ComplexLog Interceptor class, you can trigger both interceptors by simply adding to your Bean:


@ComplexLog
public void register() throws Exception {
}

Follow us on Twitter