interceptors.xml 12 KB
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"  [ ]>
<chapter id="interceptors">

   <title>Interceptors</title>

   <para>
      Interceptor functionality is defined in the Java Interceptors specification. CDI enhances this functionality with 
      a more sophisticated, semantic, annotation-based approach to binding interceptors to beans.
   </para>

   <para>
      The Interceptors specification defines two kinds of interception points:
   </para>
  
   <itemizedlist>
      <listitem>
         <para>business method interception, and</para>
      </listitem>
      <listitem>
         <para>lifecycle callback interception.</para>
      </listitem>
   </itemizedlist>
  
   <para>
      In addition, the EJB specification defines timeout method interception.
   </para>
  
   <para>
      A <emphasis>business method interceptor</emphasis> applies to invocations of methods of the bean by clients of the
      bean:
   </para>
  
   <programlisting role="JAVA"><![CDATA[public class TransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
  
   <para>
      A <emphasis>lifecycle callback interceptor</emphasis> applies to invocations of lifecycle callbacks by the
      container:
   </para>
  
   <programlisting role="JAVA"><![CDATA[public class DependencyInjectionInterceptor {
   @PostConstruct 
   public void injectDependencies(InvocationContext ctx) { ... }
}]]></programlisting>

   <para>
      An interceptor class may intercept both lifecycle callbacks and business methods.
   </para>
   
   <para>
      A <emphasis>timeout method interceptor</emphasis> applies to invocations of EJB timeout methods by the
      container:
   </para>
  
   <programlisting role="JAVA"><![CDATA[public class TimeoutInterceptor {
   @AroundTimeout 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>
  
   <section>
      <title>Interceptor bindings</title>

      <para>
         Suppose we want to declare that some of our beans are transactional. The first thing we need is an
         <emphasis>interceptor binding type</emphasis> to specify exactly which beans we're interested in:
      </para>

      <programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}]]></programlisting>

      <para>
         Now we can easily specify that our <literal>ShoppingCart</literal> is a transactional object:
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional
public class ShoppingCart { ... }]]></programlisting>

      <para>
         Or, if we prefer, we can specify that just one method is transactional:
      </para>

      <programlisting role="JAVA"><![CDATA[public class ShoppingCart {
   @Transactional public void checkout() { ... }
}]]></programlisting>

   </section>

   <section>
      <title>Implementing interceptors</title>

      <para>
         That's great, but somewhere along the line we're going to have to actually implement the interceptor that
         provides this transaction management aspect. All we need to do is create a standard interceptor, and annotate
         it <literal>@Interceptor</literal> and <literal>@Transactional</literal>.
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional @Interceptor
public class TransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>

      <para>
         Interceptors can take advantage of dependency injection:
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional @Interceptor
public class TransactionInterceptor {

    @Resource UserTransaction transaction;

    @AroundInvoke 
    public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
    
}]]></programlisting>

      <para>
         Multiple interceptors may use the same interceptor binding type.
      </para>

   </section>

   <section>
      <title>Enabling interceptors</title>

      <para>
         By default, all interceptors are disabled. We need to <emphasis>enable</emphasis> our interceptor in the
         <literal>beans.xml</literal> descriptor of a bean archive. This activation only applies to the beans in 
         that archive.
      </para>

<programlisting role="XML"><![CDATA[<beans
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>]]></programlisting>

      <para>Whoah! Why the angle bracket stew?</para> 

      <para>
         Well, having the XML declaration is actually a <emphasis>good thing</emphasis>. It solves two problems:
      </para>

      <itemizedlist>
         <listitem>
            <para>
               it enables us to specify a total ordering for all the interceptors in our system, ensuring deterministic
               behavior, and
            </para>
         </listitem>
         <listitem>
            <para>it lets us enable or disable interceptor classes at deployment time.</para>
         </listitem>
      </itemizedlist> 

      <para>
         For example, we could specify that our security interceptor runs before our transaction interceptor.
      </para>

      <programlisting role="XML"><![CDATA[<beans
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
   <interceptors>
      <class>org.mycompany.myapp.SecurityInterceptor</class>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>]]></programlisting>

      <para>Or we could turn them both off in our test environment by simply not mentioning them in 
      <literal>beans.xml</literal>! Ah, so simple.</para>

   </section>

   <section>
      <title>Interceptor bindings with members</title>

      <para>
         Suppose we want to add some extra information to our <literal>@Transactional</literal> annotation:
      </para>

      <programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
   boolean requiresNew() default false;
}]]></programlisting>

      <para>
         CDI will use the value of <literal>requiresNew</literal> to choose between two different interceptors,
         <literal>TransactionInterceptor</literal> and <literal>RequiresNewTransactionInterceptor</literal>.
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {
   @AroundInvoke 
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}]]></programlisting>

      <para>
         Now we can use <literal>RequiresNewTransactionInterceptor</literal> like this:
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional(requiresNew = true)
public class ShoppingCart { ... }]]></programlisting>

      <para>
         But what if we only have one interceptor and we want the container to ignore the value of
         <literal>requiresNew</literal> when binding interceptors? Perhaps this information is only useful for the
         interceptor implementation. We can use the <literal>@Nonbinding</literal> annotation:
      </para>

      <programlisting role="JAVA"><![CDATA[@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
   @Nonbinding String[] rolesAllowed() default {};
}]]></programlisting>

   </section>

   <section>
      <title>Multiple interceptor binding annotations</title>

      <para>
         Usually we use combinations of interceptor bindings types to bind multiple interceptors to a bean. For example,
         the following declaration would be used to bind <literal>TransactionInterceptor</literal> and
         <literal>SecurityInterceptor</literal> to the same bean:
      </para>

      <programlisting role="JAVA"><![CDATA[@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }]]></programlisting>

      <para>
         However, in very complex cases, an interceptor itself may specify some combination of interceptor binding
         types:
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }]]></programlisting>

      <para>
         Then this interceptor could be bound to the <literal>checkout()</literal> method using any one of the following
         combinations:
      </para>

      <programlisting role="JAVA"><![CDATA[public class ShoppingCart {
   @Transactional @Secure public void checkout() { ... }
}]]></programlisting>

      <programlisting role="JAVA"><![CDATA[@Secure
public class ShoppingCart {
   @Transactional public void checkout() { ... }
}]]></programlisting>

      <programlisting role="JAVA"><![CDATA[@Transactional
public class ShoppingCart {
   @Secure public void checkout() { ... }
}]]></programlisting>

      <programlisting role="JAVA"><![CDATA[@Transactional @Secure
public class ShoppingCart {
   public void checkout() { ... }
}]]></programlisting>

   </section>

   <section>
      <title>Interceptor binding type inheritance</title>
  
      <para>
         One limitation of the Java language support for annotations is the lack of annotation inheritance. Really,
         annotations should have reuse built in, to allow this kind of thing to work:
      </para>

      <programlisting role="JAVA"><![CDATA[public @interface Action extends Transactional, Secure { ... }]]></programlisting>

      <para>
         Well, fortunately, CDI works around this missing feature of Java. We may annotate one interceptor binding type
         with other interceptor binding types (termed a <emphasis>meta-annotation</emphasis>). The interceptor bindings
         are transitive &#8212; any bean with the first interceptor binding inherits the interceptor bindings declared as
         meta-annotations.
      </para>

      <programlisting role="JAVA"><![CDATA[@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }]]></programlisting>

      <para>
         Now, any bean annotated <literal>@Action</literal> will be bound to both
         <literal>TransactionInterceptor</literal> and <literal>SecurityInterceptor</literal>.  (And even
         <literal>TransactionalSecureInterceptor</literal>, if it exists.)
      </para>

   </section>

   <section>
      <title>Use of <literal>@Interceptors</literal></title>
  
      <para>The <literal>@Interceptors</literal> annotation defined by the interceptor specification (and used by the
      managed bean and EJB specifications) is still supported in CDI.</para>
  
      <programlisting role="JAVA"><![CDATA[@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
   public void checkout() { ... }
}]]></programlisting>

      <para>However, this approach suffers the following drawbacks:</para>
  
      <itemizedlist>
         <listitem>
            <para>
               the interceptor implementation is hardcoded in business code,
            </para>
         </listitem>
         <listitem>
            <para>
               interceptors may not be easily disabled at deployment time, and
            </para>
         </listitem>
         <listitem>
            <para>
               the interceptor ordering is non-global &#8212; it is determined by the order in which interceptors are
               listed at the class level.
            </para>
         </listitem>
      </itemizedlist>
  
      <para>Therefore, we recommend the use of CDI-style interceptor bindings.</para>
  
   </section>

<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>