Web Beans support interception as defined by the package javax.interceptor. Interceptors may be bound to a simple Web Bean, enterprise Web Bean or EJB 3 style session, singleton or message driven bean using the javax.interceptor.Interceptors annotation, or by using a Web Beans interceptor binding.
Interceptors are usually used to implement cross-cutting concerns, functionality that is orthogonal to the type system. In addition, Web Beans provides support for decorators. A decorator intercepts method invocations for a specific API type. Unlike interceptors, decorators are typesafe, and cannot be used to implement cross-cutting concerns.
Producer methods and JMS endpoints may not declare interceptors or decorators.
Method interception by interceptors and decorators applies to business method invocations of a simple Web Bean, enterprise Web Bean or EJB bean.
For a simple Web Bean, a method invocation is considered a business method invocation if:
the method was invoked upon an object obtained by calling Manager.getInstance(), passing the Bean object representing the simple Web Bean (this includes any instance of the Web Bean injected by the Web Bean manager), and
the method is non-private and non-static.
Invocations of initializer methods by the Web Bean manager during Web Bean creation are not considered to be business method invocations.
Invocations of Web Bean remove methods by the Web Bean manager during enterprise Web Bean destruction are not considered to be business method invocations.
Invocations of @PreDestroy and @PostConstruct callbacks by the Web Bean manager are not considered to be business method invocations.
All invocations of producer methods, disposal methods and observer methods are considered to be business method invocations.
Business method invocations of an enterprise Web Bean or EJB session, singleton or message driven bean are defined by the EJB specification.
Self-invocations of a simple Web Bean are considered to be business method invocations. However, self-invocations of an enterprise Web Bean or EJB session, singleton or message driven bean are not considered to be business method invocations.
An interceptor may be a method interceptor, a lifecycle callback interceptor, or both.
An interceptor method for business method invocations is a method of an interceptor with return type Object and a single parameter of type javax.interceptor.InvocationContext, annotated @AroundInvoke.
Interceptor methods for business method invocations are called when a business method is invoked.
If an interceptor has an interceptor method for business method invocations, we describe it as a business method interceptor.
An interceptor method for a lifecycle callback is a method of a Web Beans interceptor implementation class with return type void and a single parameter of type javax.interceptor.InvocationContext, annotated @PostConstruct, @PreDestroy, @PrePassivate or @PostActivate.
Interceptor methods for a lifecycle callbacks are called when the corresponding @PostConstruct, @PreDestroy, @PrePassivate or @PostActivate callbacks is invoked.
If an interceptor has an interceptor method for a lifecycle callback, we describe it as a lifecycle callback interceptor.
Any Web Bean implementation class may declare interceptors using @Interceptors. If the Web Bean is an EJB bean, the EJB container is responsible for calling interceptors declared using @Interceptors. Otherwise, the Web Bean manager is responsible for calling the interceptors. In both cases, the semantics are fully defined by the EJB specification.
As an extension to the functionality defined by the javax.interceptor package, Web Beans provides an alternative method of binding interceptors to simple Web Beans, enterprise Web Beans and EJB session, singleton and message driven beans. Interceptors bound using this mechanism are always called by the Web Bean manager.
Even when interceptors are bound using this mechanism, the interception semantics are defined by the EJB specification.
An interceptor binding type is a Java annotation defined as @Target({TYPE, METHOD}) or @Target(TYPE) and @Retention(RUNTIME). All interceptor binding types must also specify the @InterceptorBindingType meta-annotation.
@InterceptorBindingType @Target({TYPE, METHOD}) @Retention(RUNTIME) public @interface Transactional {}
Multiple interceptors may be bound to the same interceptor binding type or types.
An interceptor binding type may declare other interceptor bindings.
@InterceptorBindingType @Target({TYPE, METHOD}) @Retention(RUNTIME) @Transactional public @interface DataAccess {}
Interceptor bindings are transitive—an interceptor binding declared by an interceptor binding type is inherited by all Web Beans and other interceptor binding types that declare that interceptor binding type.
Interceptor binding types declared @Target(TYPE) may not be applied to interceptor binding types declared @Target({TYPE, METHOD}).
Interceptor bindings may be applied to a stereotype by annotating the stereotype annotation:
@Transactional @Secure @Production @RequestScoped @Stereotype @Target(TYPE) @Retention(RUNTIME) public @interface Action {}
An interceptor binding declared by a stereotype are inherited by any Web Bean that declares that stereotype.
If a stereotype declares interceptor bindings, it must be defined as @Target(TYPE).
A Web Beans interceptor is a simple Web Bean with an implementation class that is also an interceptor class as defined by the EJB specification. Web Beans interceptors must declare at least one interceptor binding type.
A Web Beans interceptor may be either a business method interceptor, a lifecycle callback interceptor or both.
Web Beans lifecycle callback interceptors may only declare interceptor binding types that are defined as @Target(TYPE). If a lifecycle callback interceptor declares an interceptor binding type that is defined @Target({TYPE, METHOD}), a DefinitionException is thrown by the Web Bean manager at initialization time.
If a Web Beans interceptor does not declare any interceptor binding type, a DefinitionException is thrown by the Web Bean manager at initialization time.
Open issue: do we need to support defining interceptor methods in XML?
Open issue: should we support injection into interceptor methods?
A Web Beans interceptor may be declared by annotating the interceptor implementation class with the @Interceptor stereotype, along with at least one interceptor binding type.
@Transactional @Interceptor public class TransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... } }
Additional Web Beans interceptors may be declared in web-beans.xml, using the interceptor implementation class name and the <Interceptor> element:
<myfwk:TransactionInterceptor> <Interceptor/> <myfwk:Transactional/> </myfwk:TransactionInterceptor>
When an interceptor is declared in XML, the Web Bean manager ignores any interceptor binding annotations applied to the interceptor class.
If the interceptor implementation class is already annotated @Interceptor, two different Web Beans interceptors exist, with different interceptor binding types.
A Web Beans lifecycle callback interceptor may be bound to any simple Web Bean that is not an interceptor or decorator, any enterprise Web Bean or any EJB session, singleton or message-driven bean by declaring, at the class level, the same interceptor binding types that were declared by the interceptor.
A Web Beans business method interceptor may be bound to all non-static, non-private, non-final methods of a simple Web Bean that is not an interceptor or decorator or to all business methods of an enterprise Web Bean or EJB session, singleton or message-driven bean by declaring the same interceptor binding types, at the class level, that were declared by the interceptor.
A Web Beans business method interceptor may be bound to a non-static, non-private, non-final method of a simple Web Bean that is not an interceptor or decorator or to a business method of an enterprise Web Bean or EJB session, singleton or message-driven bean by declaring the same interceptor binding types, at the method level, that were declared by the interceptor.
If a simple Web Bean implementation class that is not an interceptor or decorator is declared final, or has any non-static, non-private, final methods, and also declares an interceptor binding type or a stereotype with interceptor bindings, a DefinitionException is thrown by the Web Bean manager at initialization time.
If a non-static, non-private method of a simple Web Bean implementation class is declared final and also declares an interceptor binding type, an DefinitionException is thrown by the Web Bean manager at initialization time.
Interceptor binding types may be declared by annotating the implementation class of a simple Web Bean, enterprise Web Bean, or by annotating the bean class of an EJB session, singleton or message-driven bean.
In the following example, the TransactionInterceptor will be applied at the class level, and therefore applies to all business methods of the class:
@Transactional public class ShoppingCart { ... }
In this example, the TransactionInterceptor will be applied at the method level:
public class ShoppingCart { @Transactional public void placeOrder() { ... } }
Web Beans interceptors may be enabled or disabled at deployment time. Disabled interceptors are never called at runtime.
For a Web Bean defined using annotations, the interceptor bindings for the Web Bean include all interceptor bindings declared by annotating the implementation class, together with all interceptor bindings of all stereotypes declared by the Web Bean.
Class-level or method-level interceptor binding types may be applied to any Web Bean declared in web-beans.xml.
In the following example, the TransactionInterceptor will be applied at the class level:
<myapp:ShoppingCart> <myfwk:Transactional/> </myapp:ShoppingCart>
In this example, the TransactionInterceptor will be applied at the method level:
<myapp:ShoppingCart> <myapp:placeOrder> <myfwk:Transactional/> </myapp:placeOrder> </myapp:ShoppingCart>
If any class-level interceptor binding type is specified in XML, the interceptor binding annotations appearing on the implementation class are ignored. The class-level interceptor bindings for the Web Bean include all interceptor bindings declared using XML, together with all interceptor bindings of all stereotypes declared by the Web Bean.
Otherwise, if no class-level interceptor binding types are specified in XML, the interceptor binding annotations that appear on the implementation class are used. The class-level interceptor bindings for the Web Bean include all interceptor bindings declared by annotating the implementation class, together with all interceptor bindings of all stereotypes declared by the Web Bean.
If any method-level interceptor binding type is specified in XML, the interceptor binding annotations appearing on that method are ignored. The method-level interceptor bindings for that method include only the interceptor bindings declared using XML.
Otherwise, if no method-level interceptor binding types are specified in XML, the interceptor binding annotations that appear on that method are used. The method-level interceptor bindings for that method include all the interceptor bindings declared by annotating the method.
By default, interceptors bound via interceptor binding types are not enabled. An interceptor must be explicitly enabled by listing its implementation class under the <Interceptors> element in web-beans.xml.
<Interceptors> <myfwk:TransactionInterceptor/> <myfwk:LoggingInterceptor/> </Interceptors>
The order of the interceptor declarations determines the interceptor ordering. Interceptors which occur earlier in the list are called first.
If the <Interceptors> element is specified in more than one web-beans.xml document, a DefinitionException is thrown by the Web Bean manager at initialization time.
Interceptors declared using @Interceptors or in ejb-jar.xml are called before interceptors declared using Web Beans interceptor bindings.
Interceptors are called before decorators.
Open issue: how can we guarantee this for EJBs?
The Bean object for an interceptor must extend the abstract class Interceptor.
public abstract class Interceptor extends Bean<Object> { protected Interceptor(Manager manager) { super(manager); } public abstract Set<Annotation> getInterceptorBindingTypes(); public abstract Method getMethod(InterceptionType type); }
An InterceptionType identifies the kind of lifecycle callback or business method.
public enum InterceptionType { AROUND_INVOKE, POST_CONSTRUCT, PRE_DESTROY, PRE_PASSIVATE, POST_ACTIVATE }
The getMethod() method returns the interceptor method for the specified kind of lifecycle callback or business method. The getMethod() method must return a null value if the interceptor does not intercepts callbacks or business methods of the given type.
The following method returns the ordered list of enabled interceptors for a set of interceptor binding types.
public interface Manager { List<Interceptor> resolveInterceptors(InterceptionType type, Annotation... interceptorBindingTypes); ... }
If two instances of the same interceptor binding type are passed to resolveInterceptors(), a DuplicateBindingTypeException is thrown.
If no interceptor binding type instance is passed to resolveInterceptors(), an IllegalArgumentException is thrown.
If an instance of an annotation that is not an interceptor binding type is passed to resolveInterceptors(), an IllegalArgumentException is thrown.
The following algorithm must be used by the Web Bean manager when resolving interceptors:
First, the Web Bean manager identifies the set of matching enabled interceptors where for each declared interceptor binding annotation, there exists an interceptor binding annotation in the set of given binding annotations or, recursively, meta-annotations of those annotations, with (a) the same type and (b) the same annotation member value for each member which is not annotated @NonBinding (see Section 6.2.9.2, “Interceptor binding types with members”).
Next, the Web Bean manager narrows the set of matching interceptors according to whether the interceptor intercepts the given kind of lifecycle callback or business method.
Next, the Web Bean manager orders the matching interceptors according to the interceptor ordering specified in Section 6.2.7, “Interceptor enablement and ordering” and returns the resulting list of interceptors. If no matching interceptors exist in the set, an empty list is returned.
An interceptor class may specify multiple interceptor binding types, in which case the interceptor will be applied only to Web Beans and EJB beans with an implementation class that also declares all the binding types, and to methods of Web Beans and EJB beans where all the binding types appear on either the method or implementation class.
Consider the following interceptor:
@Transactional @Secure @Interceptor public class TransactionalSecurityInterceptor { @AroundInvoke public void aroundInvoke() { ... } }
This interceptor will be bound to all methods of this Web Bean:
@Transactional @Secure public class ShoppingCart { ... }
The interceptor will also be bound to the placeOrder() method of this Web Bean:
@Transactional public class ShoppingCart { @Secure public void placeOrder() { ... } }
However, it will not be bound to the placeOrder() method of this Web Bean, since the @Secure interceptor binding type does not appear:
@Transactional public class ShoppingCart { public void placeOrder() { ... } }
According to the interceptor resolution algorithm defined above, interceptor binding types may have annotation members.
This interceptor binding type declares a member:
@InterceptorBindingType @Target({TYPE, METHOD}) @Retention(RUNTIME) public @interface Transactional { boolean requiresNew() default false; }
Any interceptor with that interceptor binding type must select a member value:
@Transactional(requiresNew=true) @Interceptor public class RequiresNewTransactionInterceptor { @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... } }
The RequiresNewTransactionInterceptor applies to this Web Bean:
@Transactional(requiresNew=true) public class ShoppingCart { ... }
But not to this Web Bean:
@Transactional public class ShoppingCart { ... }
Annotation member values are compared using equals().
An annotation member may be excluded from consideration using the @NonBinding annotation.
@InterceptorBindingType @Target({TYPE, METHOD}) @Retention(RUNTIME) public @interface Transactional { @NonBinding boolean requiresNew() default false; }
Array-valued or annotation-valued members of an interceptor binding annotation must be annotated @NonBinding. If an array-valued or annotation-valued member of an interceptor binding annotation is not annotated @NonBinding, a DefinitionException is thrown by the Web Bean manager at initialization time.
When a simple or enterprise Web Bean is created, the Web Bean manager must:
Identify the interceptors for each lifecycle callback and business method by calling Manager.resolveInterceptors() passing the interceptor bindings for the callback or business method, including all interceptor bindings defined at the class level, method level and by stereotypes.
If this is a simple Web Bean, identify the interceptors defined using the @Interceptors annotation for each lifecycle callback and business method.
For each unique interceptor, call Manager.getInstance(), passing the Interceptor object, to obtain an instance of the interceptor. For a given interceptor and a given Web Bean instance, the Web Bean manager must call Manager.getInstance() exactly once.
For each lifecycle callback and business method build an ordered list of returned interceptor instances.
The resulting ordered lists of interceptor instances are called interceptor stacks.
Whenever a business method or lifecycle callback is invoked on an instance of a simple Web Bean, enterprise Web Bean or EJB session, singleton, or message driven bean, the Web Bean manager intercepts the method invocation and invokes interceptors of the callback or business method.
The Web Bean manager identifies the first interceptor in the interceptor stack for the method. If no such interceptor exists, the Web Bean manager starts processing the decorator stack, as defined in Section 6.3.9, “Decorator invocation”. Otherwise, the Web Bean manager builds an instance of javax.interceptor.InvocationContext and calls the appropriate interceptor method of the interceptor.
When any interceptor is invoked by the Web Bean manager, it may in turn call InvocationContext.proceed(). The Web Bean manager then identifies the first interceptor in the interceptor stack for the method such that the interceptor has not previously been invoked during this business method or lifecycle callback invocation. If no such interceptor exists, the Web Bean manager starts processing the decorator stack. Otherwise, the Web Bean manager calls the appropriate interceptor method.
Eventually, by recursion, the interceptor stack is exhausted of uninvoked interceptors.
A decorator implements one or more API types and intercepts business method invocations for methods defined by the implemented API types. These API types are called decorated types.
A decorator is a simple Web Bean. The set of decorated types of a decorator includes all interfaces implemented directly or indirectly by the implementation class, except for java.io.Serializable. The decorator implementation class and its superclasses are not decorated types of the decorator. The decorator class may be abstract.
Alternative definition: the set of decorated types includes all interfaces implemented directly and indirectly by both the decorator implementation class and the declared type of the delegate attribute.
Decorators may be bound to any enterprise Web Bean, any simple Web Bean that implements an interface and is not an intercepor or decorator, or to any EJB bean. Decorators are called by the Web Bean manager, according to the semantics defined in Section 6.3.9, “Decorator invocation”.
A decorator is declared by annotating the implementation class with the @Decorator stereotype.
@Decorator class TimestampLogger implements Logger { ... }
Additional decorators may be declared in web-beans.xml, using the decorator implementations class name and the <Decorator> element:
<myfwk:TimestampLogger> <Decorator/> ... </myfwk:TimestampLogger>
If the decorator implementation class is already annotated @Decorator, two different decorators exist.
All decorators inject a delegate attribute using the @Decorates annotation or <Decorates> element:
@Decorator class TimestampLogger implements Logger { @Decorates Logger logger; ... }
<myfwk:TimestampLogger> <Decorator/> <myfwk:logger> <Decorates> <myfwk:Logger/> </Decorates> </myfwk:logger> </myfwk:TimestampLogger>
In this case, the decorator is bound to any Web Bean or EJB bean that has the type of the delegate attribute as an API type.
The declared type of the delegate attribute must be a Java interface type. If the declared type of a delegate attribute is not a Java interface type, a DefinitionException is thrown by the Web Bean manager at initialization time.
The delegate may optionally declare one or more binding types:
@Decorator class TimestampLogger implements Logger { @Decorates @Debug Logger logger; ... }
<myfwk:TimestampLogger> <Decorator/> <myfwk:logger> <Decorates> <myfwk:Logger> <myfwk:Debug/> </myfwk:Logger> </Decorates> </myfwk:logger> </myfwk:TimestampLogger>
In this case, the decorator is bound to any Web Bean or EJB bean that has the type of the delegate attribute as an API type, and declares all the binding types specified by the delegate attribute.
All delegate binding types must be explicitly declared. If no binding type is explicitly declared by the delegate attribute, the set of binding types is empty.
A decorator must declare exactly one delegate attribute. If a decorator declares more than one delegate attribute, or does not declare a delegate attribute, a DefinitionException is thrown by the Web Bean manager at initialization time.
When a decorator is declared in XML, it must explicitly declare a delegate attribute. The Web Bean manager ignores any delegate attribute declared using annotations.
If a decorator applies to a simple Web Bean, and the Web Bean implementation class is declared final, a DefinitionException is thrown by the Web Bean manager at initialization time.
If a decorator applies to a simple Web Bean with a non-static, non-private, final method, and the decorator also implements that method, a DefinitionException is thrown by the Web Bean manager at initialization time.
A decorator is not required to implement all of the API types of its delegate attribute. If a decorator does not implement an API type of the delegate attribute, that API will not be intercepted by the decorator.
A decorator may be an abstract Java class, and is not required to implement all methods of its API types. If a decorator does not implement a method of one of its API types, that method will not be intercepted by the decorator.
The declared type of the decorator delegate attribute must implement or extend all of the decorated types of the decorator. If a decorator delegate attribute does not implement or extend a decorated type of the decorator, a DefinitionException is thrown by the Web Bean manager at initialization time.
By default, decorators are not enabled. A decorator must be explicitly enabled by listing its implementation class under the <Decorators> element in web-beans.xml.
<Decorators> <myfwk:TimestampLogger/> <myfwk:IdentityLogger/> </Decorators>
The order of the decorator declarations determines the decorator ordering. Decorators which occur earlier in the list are called first.
If the <Decorators> element is specified in more than one web-beans.xml document, a DeploymentException is thrown by the Web Bean manager at initialization time.
Decorators are called after interceptors.
Would it be better to unify interceptors and decorators into a single stack, so that they can be interleaved?
The Bean object for an interceptor must extend the abstract class Decorator.
public abstract class Decorator extends Bean<Object> { protected Decorator(Manager manager) { super(manager); } public abstract Class<?> getDelegateType(); public abstract Set<Annotation> getDelegateBindingTypes(); public abstract void setDelegate(Object instance, Object delegate); }
The following method returns the ordered list of enabled decorators for a set of API types and a set of binding types.
public interface Manager { List<Decorator> resolveDecorators(Set<Class<?>> types, Annotation... bindingTypes); ... }
The first argument is the set of API types of the decorated Web Bean. The annotations are binding types declared by the decorated Web Bean.
If two instances of the same binding type are passed to resolveDecorators(), a DuplicateBindingTypeException is thrown.
If an instance of an annotation that is not a binding type is passed to resolveDecorators(), an IllegalArgumentException is thrown.
If the set of API types is empty, an IllegalArgumentException is thrown.
The following algorithm must be used by the Web Bean manager when resolving decorators:
First, the Web Bean manager identifies the set of matching enabled decorators where the declared type of the delegate attribute is one of the given API types. For this purpose, primitive types are considered to be identical to their corresponding wrapper types in java.lang, array types are considered identical only if their element types are identical and parameterized types are considered identical only if both the type and all type parameters are identical.
Next, the Web Bean manager considers the given binding annotations. If no binding annotations were passed to resolveDecorators(), the Web Bean manager assumes the binding annotation @Current. The Web Bean manager narrows the set of matching decorators to just those where for each binding annotation declared by the decorator delegate attribute, there is a given binding annotation with (a) the same type and (b) the same annotation member value for each member which is not annotated @NonBinding (see Section 4.9.2.1, “Binding annotations with members”).
Next, the Web Bean manager orders the matching decorators according to the decorator ordering specified in Section 6.3.5, “Decorator enablement and ordering” and returns the resulting list of decorators. If no matching decorators exist in the set, an empty list is returned..
When a simple or enterprise Web Bean is created, the Web Bean manager must:
Identify the decorators for the Web Bean by calling Manager.resolveDecorators() passing the API types and binding types of the Web Bean.
For each decorator, call Manager.getInstance(), passing the Decorator object, to obtain an instance of the decorator.
For each returned decorator instance, call Decorator.setDelegate() to inject an object that implements the declared type of the delegate attribute to the delegate attribute of the decorator instance.
Build an ordered list of the decorator instances.
The resulting ordered list of decorator instances is called the decorator stack.
Whenever a business method is invoked on an instance of a simple Web Bean, enterprise Web Bean or EJB session, singleton, or message driven bean, the Web Bean manager intercepts the business method invocation and, after processing the interceptor stack, as defined in Section 6.2.11, “Interceptor invocation”, invokes decorators of the Web Bean.
The Web Bean manager searches for the first decorator in the decorator stack for the instance that implements the method that is being invoked as a business method. If no such decorator exists, the Web Bean manager invokes the business method of the Web Bean instance. Otherwise, the Web Bean manager calls the method of the decorator.
When any decorator is invoked by the Web Bean manager, it may in turn invoke a method of the delegate attribute. The Web Bean manager intercepts the delegate invocation and searches for the first decorator in the decorator stack for the instance such that:
the decorator implements the method that is being invoked upon the delegate, and
the decorator has not previously been invoked during this business method invocation.
If no such decorator exists, the Web Bean manager invokes the business method of the Web Bean instance. Otherwise, the Web Bean manager calls the method of the decorator.
Eventually, by recursion, the decorator stack is exhausted of uninvoked decorators.