SeamFramework.orgCommunity Documentation

Capitolo 7. Gli interceptor

7.1. Interceptor bindings
7.2. Implementare gli interceptor
7.3. Abilitare gli interceptor
7.4. Interceptor binding con membri
7.5. Annotazioni per interceptor binding multipli
7.6. Ereditarietà del tipo di interceptor binding
7.7. Uso di @Interceptors

Web Beans riutilizza l'architettura base degli interceptor di EJB3.0, estendendo la funzionalità in due direzioni:

"La specifica Web Bean definisce due tipi di punti di intercettazione:

Un interceptor di un metodo di business si applica alle invocazioni di metodi del Web Bean da parte di client del Web Bean:

public class TransactionInterceptor {

    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

Un interceptor di chiamata del ciclo di vita si applica alle invocazioni delle chiamate del ciclo di vita da parte del container:

public class DependencyInjectionInterceptor {

    @PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}

Una classe interceptor può intercettare entrambi le chiamate del ciclo di vita ed i metodi di business.

Si supponga di voler dichiarare transazionali alcuni Web Beans. La prima cosa necessaria è un'annotazione di interceptor binding per specificare esattamente quali Web Beans sono interessati:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}

Ora è facilmente possibile specificare che ShoppingCart è un oggetto transazionale:

@Transactional

public class ShoppingCart { ... }

O se si preferisce, si può specificare che solo un metodo sia transazionale:

public class ShoppingCart {

    @Transactional public void checkout() { ... }
}

Bene, ma da qualche parte è necessario implementare l'interceptor che fornisce l'aspect di gestione della transazione. Occore quindi creare un interceptor EJB standard e annotarlo con @Interceptor e @Transactional."

@Transactional @Interceptor

public class TransactionInterceptor {
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

Tutti gli interceptor dei Web Beans sono Web Beans semplici e possono sfruttare la dependency injection e la gestione del ciclo di vita contestuale.

@ApplicationScoped @Transactional @Interceptor

public class TransactionInterceptor {
    @Resource Transaction transaction;
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
    
}

Diverso interceptor possono usare lo stesso tipo di interceptor binding.

Infine occorre abilitare l'interceptor in web-beans.xml.


<Interceptors>
    <tx:TransactionInterceptor/>
</Interceptors
>

Ma perché viene usato ancora XML, quando Web Beans non dovrebbe utilizzarlo?

La dichiarazione XML risolve due problemi:

Per esempio è possibile specificare che l'interceptor di sicurezza venga eseguito prima di TransactionInterceptor."


<Interceptors>
    <sx:SecurityInterceptor/>
    <tx:TransactionInterceptor/>
</Interceptors
>

Oppure si può disattivarli entrambi dal proprio ambiente di test!

Si supponga di voler aggiungere qualche informazione extra all'annotazione @Transactional:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
    boolean requiresNew() default false;
}

Web Beans utilizzerà il valore di requiresNew per scegliere tra due diversi interceptor, TransactionInterceptor e RequiresNewTransactionInterceptor.

@Transactional(requiresNew=true) @Interceptor

public class RequiresNewTransactionInterceptor {
    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

Ora è possibile usare RequiresNewTransactionInterceptor in questo modo:

@Transactional(requiresNew=true)

public class ShoppingCart { ... }

Ma cosa succede se si ha solo un interceptor e si vuole che il manager ignori il valore di requiresNew quando si associa l'interceptor? Si può usare l'annotazione @NonBinding:

@InterceptorBindingType

@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
    @NonBinding String[] rolesAllowed() default {};
}

Solitamente si usano combinazioni di tipi di interceptor binding per associare pià interceptor ad un Web Bean. Per esempio, la seguente dichiarazione verrebbe impiegata per associare TransactionInterceptor e SecurityInterceptor allo stesso Web Bean:

@Secure(rolesAllowed="admin") @Transactional

public class ShoppingCart { ... }

Comunque in casi molto complessi un interceptor da solo potrebbe specificare alcune combinazioni di tipi di interceptor binding:

@Transactional @Secure @Interceptor

public class TransactionalSecureInterceptor { ... }

Allora quest'interceptor potrebbe venire associato al metodo checkout() usando una delle seguenti combinazioni:

public class ShoppingCart {

    @Transactional @Secure public void checkout() { ... }
}
@Secure

public class ShoppingCart {
    @Transactional public void checkout() { ... }
}
@Transactionl

public class ShoppingCart {
    @Secure public void checkout() { ... }
}
@Transactional @Secure

public class ShoppingCart {
    public void checkout() { ... }
}

Una limitazione del supporto del linguaggio Java per le annotazioni è la mancanza di ereditarietà delle annotazioni. In verità le annotazioni dovrebbero avere il riutilizzo predefinito per consentire che questo avvenga:

public @interface Action extends Transactional, Secure { ... }

Fortunatamente Web Beans provvede a questa mancanza di Java. E' possibile annotare un tipo di interceptor binding con altri tipi di interceptor binding. Gli interceptor binding sono transitivi — qualsiasi Web Bean con il primo interceptor binding eredita gli interceptor binding dichiarati come meta-annotazioni.

@Transactional @Secure

@InterceptorBindingType
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }

Ogni Web Bean annotato con @Action verrà legato ad entrambi TransactionInterceptor e SecurityInterceptor. (E anche TransactionalSecureInterceptor, se questo esiste.)

L'annotazione @Interceptors definita dalla specifica EJB è supportata per entrambi i Web Bean semplici ed enterprise, per esempio:

@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})

public class ShoppingCart {
    public void checkout() { ... }
}

Comunque, quest'approccio soffre dei seguenti difetti:

Quindi si raccomanda l'uso di interceptor binding di stile Web Beans.