SeamFramework.orgCommunity Documentation

Chapter 8. Decorators

8.1. Delegate attributes
8.2. Enabling decorators

Interceptors are a powerful way to capture and separate concerns which are orthogonal to the 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 and security. 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.

The reverse is true of decorators. A decorator intercepts invocations only for a certain Java interface, and is therefore aware of all the semantics attached to that interface. This makes decorators a 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.

Suppose we have an interface that represents accounts:

public interface Account {

    public BigDecimal getBalance();
    public User getOwner();
    public void withdraw(BigDecimal amount);
    public void deposit(BigDecimal amount);
}

Several different Web Beans in our system implement the Account 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.

A decorator is a simple Web Bean that implements the type it decorates and is annotated @Decorator.

@Decorator

public abstract class LargeTransactionDecorator 
        implements Account {
    
    @Decorates 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) );
        }
    }
    
}

Unlike other simple Web Beans, a decorator may be an abstract class. 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.

All decorators have a delegate attribute. The type and binding types of the delegate attribute determine which Web Beans the decorator is bound to. The delegate attribute type must implement or extend all interfaces implemented by the decorator.

This delegate attribute specifies that the decorator is bound to all Web Beans that implement Account:

@Decorates Account account;

A delegate attribute may specify a binding annotation. Then the decorator will only be bound to Web Beans with the same binding.

@Decorates @Foreign Account account;

A decorator is bound to any Web Bean which:

The decorator may invoke the delegate attribute, which has much the same effect as calling InvocationContext.proceed() from an interceptor.

We need to enable our decorator in web-beans.xml.


<Decorators>
    <myapp:LargeTransactionDecorator/>
</Decorators>

This declaration serves the same purpose for decorators that the <Interceptors> declaration serves for interceptors:

Interceptors for a method are called before decorators that apply to that method.