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

   <title>Decorators</title>
  
   <para>
      Interceptors are a powerful way to capture and separate concerns which are <emphasis>orthogonal</emphasis> to the
      application (and type system). Any interceptor is able to intercept invocations of any Java type. This makes them
      perfect for solving technical concerns such as transaction management, security and call logging. However, by
      nature, interceptors are unaware of the actual semantics of the events they intercept. Thus, interceptors aren't
      an appropriate tool for separating business-related concerns.
   </para>
  
   <para>
      The reverse is true of <emphasis>decorators</emphasis>. A decorator intercepts invocations only for a certain Java
      interface, and is therefore aware of all the semantics attached to that interface. Since decorators directly
      implement operations with business semantics, it makes them the perfect tool for modeling some kinds of business
      concerns. It also means that a decorator doesn't have the generality of an interceptor. Decorators aren't able to
      solve technical concerns that cut across many disparate types. Interceptors and decorators, though similar in many
      ways, are complementary. Let's look at some cases where decorators fit the bill.
   </para>
  
   <para>Suppose we have an interface that represents accounts:</para>
  
   <programlisting role="JAVA"><![CDATA[public interface Account {
   public BigDecimal getBalance();
   public User getOwner();
   public void withdraw(BigDecimal amount);
   public void deposit(BigDecimal amount);
}]]></programlisting>

   <para>
      Several different beans in our system implement the <literal>Account</literal> interface. However, we have a
      common legal requirement that; for any kind of account, large transactions must be recorded by the system in a
      special log. This is a perfect job for a decorator.
   </para>
  
   <para>
      A decorator is a bean (possibly even an abstract class) that implements the type it decorates and is annotated
      <literal>@Decorator</literal>.
   </para>
  
   <programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   ...
}]]></programlisting>
    
   <para>
      The decorator implements the methods of the decorated type that it wants to intercept.
   </para>
    
   <programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   @Inject @Delegate @Any Account account;

   @PersistenceContext EntityManager em;
    
   public void withdraw(BigDecimal amount) {
      ...
   }
    
   public void deposit(BigDecimal amount);
      ...
   }
}]]></programlisting>

   <para>
      Unlike other beans, a decorator may be an abstract class. Therefore, if there's nothing special the decorator
      needs to do for a particular method of the decorated interface, you don't need to implement that method.
   </para>
  
   <para>
      Interceptors for a method are called before decorators that apply to the method.
   </para>

   <section>
      <title>Delegate object</title>
  
   <para>
      Decorators have a special injection point, called the <emphasis>delegate injection point</emphasis>, with the same 
      type as the beans they decorate, and the annotation <literal>@Delegate</literal>. There must be exactly one delegate 
      injection point, which can be a constructor parameter, initializer method parameter or injected field.
   </para>

   <programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   @Inject @Delegate @Any Account account;
   ...
}]]></programlisting>

      <para>A decorator is bound to any bean which:</para>
    
      <itemizedlist>
         <listitem>
            <para>has the type of the delegate injection point as a bean type, and</para>
         </listitem>
         <listitem>
            <para>has all qualifiers that are declared at the delegate injection point.</para>
         </listitem>
      </itemizedlist>
    
      <para>
         This delegate injection point specifies that the decorator is bound to all beans that implement
         <literal>Account</literal>:
      </para>
    
      <programlisting role="JAVA"><![CDATA[@Inject @Delegate @Any Account account;]]></programlisting>

      <para>
         A delegate injection point may specify any number of qualifier annotations. The decorator will only be
         bound to beans with the same qualifiers.
      </para>
    
      <programlisting role="JAVA"><![CDATA[@Inject @Delegate @Foreign Account account;]]></programlisting>
    
      <para>
         The decorator may invoke the delegate object, which has much the same effect as calling
         <literal>InvocationContext.proceed()</literal> from an interceptor. The main difference is that the decorator
         can invoke <emphasis>any</emphasis> business method on the delegate object.
      </para>
  
   <programlisting role="JAVA"><![CDATA[@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   @Inject @Delegate @Any Account account;

   @PersistenceContext EntityManager em;
    
   public void withdraw(BigDecimal amount) {
      account.withdraw(amount);
      if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
         em.persist( new LoggedWithdrawl(amount) );
      }
   }
    
   public void deposit(BigDecimal amount);
      account.deposit(amount);
      if ( amount.compareTo(LARGE_AMOUNT)>0 ) {
         em.persist( new LoggedDeposit(amount) );
      }
   }
}]]></programlisting>

   </section>
  
   <section>
      <title>Enabling decorators</title>

      <para>
         By default, all decorators are disabled. We need to <emphasis>enable</emphasis> our decorator 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">
   <decorators>
         <class>org.mycompany.myapp.LargeTransactionDecorator</class>
   </decorators>
</beans>]]></programlisting>

      <para>
         This declaration serves the same purpose for decorators that the <literal>&lt;interceptors&gt;</literal>
         declaration serves for interceptors:
      </para>

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

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