Loose coupling with strong typing

The first major theme of CDI is loose coupling. We’ve already seen three means of achieving loose coupling:

  • alternatives enable deployment time polymorphism,

  • producer methods enable runtime polymorphism, and

  • contextual lifecycle management decouples bean lifecycles.

These techniques serve to enable loose coupling of client and server. The client is no longer tightly bound to an implementation of an interface, nor is it required to manage the lifecycle of the implementation. This approach lets stateful objects interact as if they were services.

Loose coupling makes a system more dynamic. The system can respond to change in a well-defined manner. In the past, frameworks that attempted to provide the facilities listed above invariably did it by sacrificing type safety (most notably by using XML descriptors). CDI is the first technology, and certainly the first specification in the Java EE platform, that achieves this level of loose coupling in a typesafe way.

CDI provides three extra important facilities that further the goal of loose coupling:

  • interceptors decouple technical concerns from business logic,

  • decorators may be used to decouple some business concerns, and

  • event notifications decouple event producers from event consumers.

The second major theme of CDI is strong typing. The information about the dependencies, interceptors and decorators of a bean, and the information about event consumers for an event producer, is contained in typesafe Java constructs that may be validated by the compiler.

You don’t see string-based identifiers in CDI code, not because the framework is hiding them from you using clever defaulting rules—so-called "configuration by convention"—but because there are simply no strings there to begin with!

The obvious benefit of this approach is that any IDE can provide autocompletion, validation and refactoring without the need for special tooling. But there is a second, less-immediately-obvious, benefit. It turns out that when you start thinking of identifying objects, events or interceptors via annotations instead of names, you have an opportunity to lift the semantic level of your code.

CDI encourages you develop annotations that model concepts, for example,

  • @Asynchronous,

  • @Mock,

  • @Secure or

  • @Updated,

instead of using compound names like

  • asyncPaymentProcessor,

  • mockPaymentProcessor,

  • SecurityInterceptor or

  • DocumentUpdatedEvent.

The annotations are reusable. They help describe common qualities of disparate parts of the system. They help us categorize and understand our code. They help us deal with common concerns in a common way. They make our code more literate and more understandable.

CDI stereotypes take this idea a step further. A stereotype models a common role in your application architecture. It encapsulates various properties of the role, including scope, interceptor bindings, qualifiers, etc, into a single reusable package. (Of course, there is also the benefit of tucking some of those annotations away).

We’re now ready to meet some more advanced features of CDI. Bear in mind that these features exist to make our code both easier to validate and more understandable. Most of the time you don’t ever really need to use these features, but if you use them wisely, you’ll come to appreciate their power.

Producer methods

Producer methods let us overcome certain limitations that arise when a container, instead of the application, is responsible for instantiating objects. They’re also the easiest way to integrate objects which are not beans into the CDI environment.

According to the spec:

A producer method acts as a source of objects to be injected, where:

  • the objects to be injected are not required to be instances of beans, or

  • the concrete type of the objects to be injected may vary at runtime, or

  • the objects require some custom initialization that is not performed by the bean constructor.

For example, producer methods let us:

  • expose a JPA entity as a bean,

  • expose any JDK class as a bean,

  • define multiple beans, with different scopes or initialization, for the same implementation class, or

  • vary the implementation of a bean type at runtime.

In particular, producer methods let us use runtime polymorphism with CDI. As we’ve seen, alternative beans are one solution to the problem of deployment-time polymorphism. But once the system is deployed, the CDI implementation is fixed. A producer method has no such limitation:

import jakarta.enterprise.inject.Produces;

@SessionScoped
public class Preferences implements Serializable {
   private PaymentStrategyType paymentStrategy;
   ...
   @Produces @Preferred
   public PaymentStrategy getPaymentStrategy() {
       switch (paymentStrategy) {
           case CREDIT_CARD: return new CreditCardPaymentStrategy();
           case CHECK: return new CheckPaymentStrategy();
           case PAYPAL: return new PayPalPaymentStrategy();
           default: return null;
       }
   }
}

Consider an injection point:

@Inject @Preferred PaymentStrategy paymentStrategy;

This injection point has the same type and qualifier annotations as the producer method, so it resolves to the producer method using the usual CDI injection rules. The producer method will be called by the container to obtain an instance to service this injection point.

Scope of a producer method

The scope of the producer method defaults to @Dependent, and so it will be called every time the container injects this field or any other field that resolves to the same producer method. Thus, there could be multiple instances of the PaymentStrategy object for each user session.

To change this behavior, we can add a @SessionScoped annotation to the method.

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy() {
   ...
}

Now, when the producer method is called, the returned PaymentStrategy will be bound to the session context. The producer method won’t be called again in the same session.

Note
A producer method does not inherit the scope of the bean that declares the method. There are two different beans here: the producer method, and the bean which declares it. The scope of the producer method determines how often the method will be called, and the lifecycle of the objects returned by the method. The scope of the bean that declares the producer method determines the lifecycle of the object upon which the producer method is invoked.

Injection into producer methods

There’s one potential problem with the code above. The implementations of CreditCardPaymentStrategy are instantiated using the Java new operator. Objects instantiated directly by the application can’t take advantage of dependency injection and don’t have interceptors.

If this isn’t what we want, we can use dependency injection into the producer method to obtain bean instances:

@Produces @Preferred @SessionScoped
public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          CheckPaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
   switch (paymentStrategy) {
      case CREDIT_CARD: return ccps;
      case CHEQUE: return cps;
      case PAYPAL: return ppps;
      default: return null;
   }
}

Wait, what if CreditCardPaymentStrategy is a request-scoped bean? Then the producer method has the effect of "promoting" the current request scoped instance into session scope. This is almost certainly a bug! The request scoped object will be destroyed by the container before the session ends, but the reference to the object will be left "hanging" in the session scope. This error will not be detected by the container, so please take extra care when returning bean instances from producer methods!

There are at least two ways we could go to fix this bug. We could change the scope of the CreditCardPaymentStrategy implementation, but this would affect other clients of that bean. A better option would be to change the scope of the producer method to @Dependent or @RequestScoped.

Disposer methods

Some producer methods return objects that require explicit destruction. For example, somebody needs to close this JDBC connection:

@Produces @RequestScoped Connection connect(User user) {
   return createConnection(user.getId(), user.getPassword());
}

Destruction can be performed by a matching disposer method, defined by the same class as the producer method:

void close(@Disposes Connection connection) {
   connection.close();
}

The disposer method must have at least one parameter, annotated @Disposes, with the same type and qualifiers as the producer method. The disposer method is called automatically when the context ends (in this case, at the end of the request), and this parameter receives the object produced by the producer method. If the disposer method has additional method parameters, the container will look for a bean that satisfies the type and qualifiers of each parameter and pass it to the method automatically.

Since CDI 1.1 disposer methods may be used for destroying not only objects produced by producer methods but also objects producer by producer fields.

Interceptors

Interceptor functionality is defined in the Java Interceptors specification.

The Interceptors specification defines three kinds of interception points:

  • business method interception,

  • lifecycle callback interception, and

  • timeout method interception (EJB only).

A business method interceptor applies to invocations of methods of the bean by clients of the bean:

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

A lifecycle callback interceptor applies to invocations of lifecycle callbacks by the container:

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

An interceptor class may intercept both lifecycle callbacks and business methods.

A timeout method interceptor applies to invocations of EJB timeout methods by the container:

public class TimeoutInterceptor {
   @AroundTimeout
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}

Interceptor bindings

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

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

Now we can easily specify that our ShoppingCart is a transactional object:

@Transactional
public class ShoppingCart { ... }

Or, if we prefer, we can specify that just one method is transactional:

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

Implementing interceptors

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 @Interceptor and @Transactional.

@Transactional @Interceptor
public class TransactionInterceptor {
   @AroundInvoke
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}

Interceptors can take advantage of dependency injection:

@Transactional @Interceptor
public class TransactionInterceptor {

    @Resource UserTransaction transaction;

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

}

Multiple interceptors may use the same interceptor binding type.

Enabling interceptors

By default, all interceptors are disabled. We need to enable our interceptor. We can do it using beans.xml descriptor of a bean archive. However, this activation only applies to the beans in that archive. From CDI 1.1 onwards the interceptor can be enabled for the whole application using @Priority annotation.

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <interceptors>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>

Whoah! Why the angle bracket stew?

Well, having the XML declaration is actually a good thing. It solves two problems:

  • it enables us to specify an ordering for the interceptors in our system, ensuring deterministic behavior, and

  • it lets us enable or disable interceptor classes at deployment time.

Having two interceptors without @Priority, we could specify that our security interceptor runs before our transaction interceptor.

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <interceptors>
      <class>org.mycompany.myapp.SecurityInterceptor</class>
      <class>org.mycompany.myapp.TransactionInterceptor</class>
   </interceptors>
</beans>

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

It gets quite tricky when used along with interceptors annotated with @Priority. Interceptors enabled using @Priority are called before interceptors enabled using beans.xml, the lower priority values are called first.

Note
An interceptor enabled by @Priority and in the same time listed in beans.xml is only invoked once in the @Priority part of the invocation chain. E.g. the enablement via beans.xml will be ignored.

Interceptor bindings with members

Suppose we want to add some extra information to our @Transactional annotation:

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

CDI will use the value of requiresNew to choose between two different interceptors, TransactionInterceptor and RequiresNewTransactionInterceptor.

@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {
   @AroundInvoke
   public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}

Now we can use RequiresNewTransactionInterceptor like this:

@Transactional(requiresNew = true)
public class ShoppingCart { ... }

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

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

Multiple interceptor binding annotations

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 TransactionInterceptor and SecurityInterceptor to the same bean:

@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }

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

@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }

Then this interceptor could be bound to the checkout() method using any one of the following combinations:

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

Interceptor binding type inheritance

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:

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

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

@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }

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

Use of @Interceptors

The @Interceptors annotation defined by the Interceptors specification (and used by the Managed Beans and EJB specifications) is still supported in CDI.

@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
   public void checkout() { ... }
}

However, this approach suffers the following drawbacks:

  • the interceptor implementation is hardcoded in business code,

  • interceptors may not be easily disabled at deployment time, and

  • the interceptor ordering is non-global — it is determined by the order in which interceptors are listed at the class level.

Therefore, we recommend the use of CDI-style interceptor bindings.

Enhanced version of jakarta.interceptor.InvocationContext

For even more control over interceptors, Weld offers enhanced version of jakarta.interceptor.InvocationContext - org.jboss.weld.interceptor.WeldInvocationContext.

Note
The functionality described below is deprecated since Weld 6 and will be removed in the future. Users should instead use newly added methods directly from jakarta.interceptor.InvocationContext, hence removing the need to use Weld specific APIs for this purpose.

It comes with two additional methods - getInterceptorBindings and getInterceptorBindingsByType(Class<T> annotationType). You shouldn’t need this in most cases, but it comes handy when working with @Nonbinding values in interceptor binding annotations.

Assume you have the following interceptor binding:

@InterceptorBinding
@Inherited
@Target({ TYPE, METHOD, CONSTRUCTOR})
@Retention(RUNTIME)
public @interface FooBinding {

    @Nonbinding
    String secret() default "";

}

Then, in the interceptor class, you can retrieve the secret String in the following way:

@Priority(value = Interceptor.Priority.APPLICATION)
@Interceptor
@FooBinding(secret = "nonbinding")
public class AroundConstructInterceptor {

    @SuppressWarnings("unchecked")
    @AroundConstruct
    void intercept(InvocationContext ctx) throws Exception {
        if(ctx instanceof WeldInvocationContext) {
            Set<Annotation> bindings =  ((WeldInvocationContext)ctx).getInterceptorBindings();
	    for (Annotation annotation : bindings) {
                if (annotation.annotationType().equals(FooBinding.class)) {
                    FooBinding fooBinding = (FooBinding) annotation;
                    String secret = fooBinding.secret();
                }
            }
        }
        ctx.proceed();
    }
}

WeldInvocationContext can be used with the following interceptor types:

  • @AroundConstruct

  • @PostConstruct

  • @AroundInvoke

Alternatively, you can gain access to these binding directly from InvocationContext as we store them there using a key org.jboss.weld.interceptor.bindings. This key is easily accessible from WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY. Let’s alter the previous example to demonstrate this:

@Priority(value = Interceptor.Priority.APPLICATION)
@Interceptor
@FooBinding(secret = "nonbinding")
public class AroundConstructInterceptor {

    @SuppressWarnings("unchecked")
    @AroundConstruct
    void intercept(InvocationContext ctx) throws Exception {
        // retrieve data directly from InvocationContext
        Set<Annotation> bindings = (Set<Annotation>) ctx.getContextData().get(WeldInvocationContext.INTERCEPTOR_BINDINGS_KEY);
	if (bindings != null) {
            for (Annotation annotation : bindings) {
                if (annotation.annotationType().equals(FooBinding.class)) {
                    FooBinding fooBinding = (FooBinding) annotation;
                    String secret = fooBinding.secret();
                }
            }
        }
        ctx.proceed();
    }
}

Loosening the limitations of InterceptionFactory

Note
This is an experimental feature which goes beyond the scope of what CDI specification requires. While we aim to support this, the behaviour may slightly shift over time.

CDI 2.0 introduced the InterceptionFactory which can be used to intercept a bean created via producer method. The specification only allows users to operate on Java classes and when it comes to interfaces, it states that unportable behaviour results. Weld supports both ways as operating on interfaces effectively allows to bypass proxyability rules of implementation class. This however comes with a price - only annotations added programatically through InterceptionFactory.configure() will be taken into consideration.

Let’s have SomeImpl class that is an implementation of interface SomeInterface and InterceptThis is an InterceptorBinding. Let us also assume that we have secondary implementation of SomeInterface named SomeUnproxyableImpl that has a final method in it. All of the following producer methods will work:

    @Produces
    @ApplicationScoped
    // proxyable implementation which is also the InterceptionFactory paramater
    public SomeImpl produceBeanBasedOnImplClass(InterceptionFactory<SomeImpl> interceptionFactory) {
        interceptionFactory.configure().add(InterceptThis.Literal.INSTANCE);
        return interceptionFactory.createInterceptedInstance(new SomeImpl());
    }

    @Produces
    @ApplicationScoped
    // proxyable implementation but operating on an interface type as InterceptionFactory parameter
    public SomeInterface produceBeanBasedOnInterface(InterceptionFactory<SomeInterface> interceptionFactory) {
        interceptionFactory.configure().add(InterceptThis.Literal.INSTANCE);
        return interceptionFactory.createInterceptedInstance(new SomeImpl());
    }

    @Produces
    @ApplicationScoped
    // unproxyable implementation and operating on an interface type as InterceptionFactory parameter
    public SomeInterface produceBeanBasedOnInterfaceWithUnproxyableImpl(InterceptionFactory<SomeInterface> interceptionFactory) {
        interceptionFactory.configure().add(InterceptThis.Literal.INSTANCE);
        return interceptionFactory.createInterceptedInstance(new SomeUnproxyableImpl());
    }

Decorators

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

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. 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.

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 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 bean (possibly even an abstract class) that implements the type it decorates and is annotated @Decorator.

@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   ...
}

The decorator implements the methods of the decorated type that it wants to intercept.

@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);
      ...
   }
}

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.

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

Delegate object

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

@Decorator
public abstract class LargeTransactionDecorator
      implements Account {
   @Inject @Delegate @Any Account account;
   ...
}

A decorator is bound to any bean which:

  • has the type of the delegate injection point as a bean type, and

  • has all qualifiers that are declared at the delegate injection point.

This delegate injection point specifies that the decorator is bound to all beans that implement Account:

@Inject @Delegate @Any Account account;

A delegate injection point may specify any number of qualifier annotations. The decorator will only be bound to beans with the same qualifiers.

@Inject @Delegate @Foreign Account account;

The decorator may invoke the delegate object, which has much the same effect as calling InvocationContext.proceed() from an interceptor. The main difference is that the decorator can invoke any business method on the delegate object.

@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) );
      }
   }
}

Enabling decorators

By default, all decorators are disabled. We need to enable our decorator. We can do it using beans.xml descriptor of a bean archive. However, this activation only applies to the beans in that archive. From CDI 1.1 onwards the decorator can be enabled for the whole application using @Priority annotation.

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <decorators>
         <class>org.mycompany.myapp.LargeTransactionDecorator</class>
   </decorators>
</beans>

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

  • it enables us to specify an ordering for decorators in our system, ensuring deterministic behavior, and

  • it lets us enable or disable decorator classes at deployment time.

Decorators enabled using @Priority are called before decorators enabled using beans.xml, the lower priority values are called first.

Note
A decorator enabled by @Priority and in the same time listed in beans.xml is only invoked once in the @Priority part of the invocation chain. E.g. the enablement via beans.xml will be ignored.

Events

Dependency injection enables loose-coupling by allowing the implementation of the injected bean type to vary, either at deployment time or runtime. Events go one step further, allowing beans to interact with no compile time dependency at all. Event producers raise events that are delivered to event observers by the container.

This basic schema might sound like the familiar observer/observable pattern, but there are a couple of twists:

  • not only are event producers decoupled from observers; observers are completely decoupled from producers,

  • observers can specify a combination of "selectors" to narrow the set of event notifications they will receive, and

  • observers can be notified immediately, or can specify that delivery of the event should be delayed until the end of the current transaction.

The CDI event notification facility uses more or less the same typesafe approach that we’ve already seen with the dependency injection service.

Event payload

The event object carries state from producer to consumer. The event object is nothing more than an instance of a concrete Java class. (The only restriction is that an event type may not contain type variables). An event may be assigned qualifiers, which allows observers to distinguish it from other events of the same type. The qualifiers function like topic selectors, allowing an observer to narrow the set of events it observes.

An event qualifier is just a normal qualifier, defined using @Qualifier. Here’s an example:

@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface Updated {}

Event observers

An observer method is a method of a bean with a parameter annotated @Observes or @ObservesAsync.

public void onAnyDocumentEvent(@Observes Document document) { ... }

or in asynchronous version

public void onAnyDocumentEvent(@ObservesAsync Document document) { ... }

The annotated parameter is called the event parameter. The type of the event parameter is the observed event type, in this case Document. The event parameter may also specify qualifiers.

public void afterDocumentUpdate(@Observes @Updated Document document) { ... }

An observer method need not specify any event qualifiers—in this case it is interested in every event whose type is assignable to the observed event type. Such observer will trigger on both events shown below:

@Inject @Any Event<Document> documentEvent;
@Inject @Updated Event<Document> anotherDocumentEvent;

If the observer does specify qualifiers, it will be notified of an event if the event object is assignable to the observed event type, and if the set of observed event qualifiers is a subset of all the event qualifiers of the event.

The observer method may have additional parameters, which are injection points:

public void afterDocumentUpdate(@Observes @Updated Document document, User user) { ... }

Event producers

Event producers fire events either synchronously or asynchronously using an instance of the parameterized Event interface. An instance of this interface is obtained by injection:

@Inject @Any Event<Document> documentEvent;

Synchronous event producers

A producer raises synchronous events by calling the fire() method of the Event interface, passing the event object:

documentEvent.fire(document);

This particular event will only be delivered to synchronous observer method that:

  • has an event parameter to which the event object (the Document) is assignable, and

  • specifies no qualifiers.

The container simply calls all the synchronous observer methods, passing the event object as the value of the event parameter. If any observer method throws an exception, the container stops calling observer methods, and the exception is rethrown by the fire() method.

Asynchronous event producers

A producer raises asynchronous events by calling the fireAsync() method of the Event interface, passing the event object:

documentEvent.fireAsync(document);

This particular event will only be delivered to asynchronous observer method that:

  • has an event parameter to which the event object (the Document) is assignable, and

  • specifies no qualifiers.

fireAsync method returns immediately and all the resolved asynchronous observers are notified in one or more different threads. If any observer method throws an exception, the container will suppress it and notify remaining observers. The resulting CompletionStage will then finish exceptionally with CompletionException containing all previously suppressed exceptions.

Notification options

The Event.fireAsync() method may be called with a NotificationOptions parameter to configure the notification of asynchronous observer methods , e.g. to specify an Executor object to be used for asynchronous delivery. Weld defines the following non-portable notification options (see WeldNotificationOptions):

Key Value type Description

weld.async.notification.mode

String

The notification mode. Possible values are: SERIAL (default), PARALLEL. See also Notification modes.

weld.async.notification.timeout

Long or String which can be parsed as a long

The notification timeout (in milliseconds) after which the returned completion stage must be completed. If the time expires the stage is completed exceptionally with a CompletionException holding the java.util.concurrent.TimeoutException as its cause. The expiration does not abort the notification of the observers.

Note
It is also possible to use the key constants and static convenient methods declared on org.jboss.weld.events.WeldNotificationOptions from Weld API, e.g. WeldNotificationOptions.TIMEOUT or WeldNotificationOptions.withParallelMode().
Table 1. Notification modes
Mode Description

SERIAL

Asynchronous observers are notified serially in a single worker thread (default behavior).

PARALLEL

Asynchronous observers are notified in parallel assuming that the java.util.concurrent.Executor used supports parallel execution.

Applying qualifiers to event

Qualifiers can be applied to an event in one of two ways:

  • by annotating the Event injection point, or

  • by passing qualifiers to the select() of Event.

Specifying the qualifiers at the injection point is far simpler:

@Inject @Updated Event<Document> documentUpdatedEvent;

Then, every event fired via this instance of Event has the event qualifier @Updated. The event is delivered to every observer method that:

  • has an event parameter to which the event object is assignable, and

  • does not have any event qualifier except for the event qualifiers that match those specified at the Event injection point.

The downside of annotating the injection point is that we can’t specify the qualifier dynamically. CDI lets us obtain a qualifier instance by subclassing the helper class AnnotationLiteral. That way, we can pass the qualifier to the select() method of Event.

documentEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);

Events can have multiple event qualifiers, assembled using any combination of annotations at the Event injection point and qualifier instances passed to the select() method.

Conditional observer methods

By default, if there is no instance of an observer in the current context, the container will instantiate the observer in order to deliver an event to it. This behavior isn’t always desirable. We may want to deliver events only to instances of the observer that already exist in the current contexts.

A conditional observer is specified by adding receive = IF_EXISTS to the @Observes annotation.

public void refreshOnDocumentUpdate(@Observes(receive = IF_EXISTS) @Updated Document d) { ... }
Note
A bean with scope @Dependent cannot be a conditional observer, since it would never be called!

Event qualifiers with members

An event qualifier type may have annotation members:

@Qualifier
@Target({METHOD, FIELD, PARAMETER, TYPE})
@Retention(RUNTIME)
public @interface Role {
   RoleType value();
}

The member value is used to narrow the messages delivered to the observer:

public void adminLoggedIn(@Observes @Role(ADMIN) LoggedIn event) { ... }

Event qualifier type members may be specified statically by the event producer, via annotations at the event notifier injection point:

@Inject @Role(ADMIN) Event<LoggedIn> loggedInEvent;

Alternatively, the value of the event qualifier type member may be determined dynamically by the event producer. We start by writing an abstract subclass of AnnotationLiteral:

abstract class RoleBinding
   extends AnnotationLiteral<Role>
   implements Role {}

The event producer passes an instance of this class to select():

documentEvent.select(new RoleBinding() {
   public void value() { return user.getRole(); }
}).fire(document);

Multiple event qualifiers

Event qualifiers may be combined, for example:

@Inject @Blog Event<Document> blogEvent;
...
if (document.isBlog()) blogEvent.select(new AnnotationLiteral<Updated>(){}).fire(document);

The above shown event is fired with two qualifiers - @Blog and @Updated. An observer method is notified if the set of observer qualifiers is a subset of the fired event’s qualifiers. Assume the following observers in this example:

public void afterBlogUpdate(@Observes @Updated @Blog Document document) { ... }
public void afterDocumentUpdate(@Observes @Updated Document document) { ... }
public void onAnyBlogEvent(@Observes @Blog Document document) { ... }
public void onAnyDocumentEvent(@Observes Document document) { ... }}}

All of these observer methods will be notified.

However, if there were also an observer method:

public void afterPersonalBlogUpdate(@Observes @Updated @Personal @Blog Document document) { ... }

It would not be notified, as @Personal is not a qualifier of the event being fired. Or to put it more formally, @Updated and @Personal do not form a subset of @Blog and @Updated.

Transactional observers

Transactional observers receive their event notifications during the before or after completion phase of the transaction in which the event was raised. For example, the following observer method needs to refresh a query result set that is cached in the application context, but only when transactions that update the Category tree succeed:

public void refreshCategoryTree(@Observes(during = AFTER_SUCCESS) CategoryUpdateEvent event) { ... }

There are five kinds of transactional observers:

  • IN_PROGRESS observers are called immediately (default)

  • AFTER_SUCCESS observers are called during the after completion phase of the transaction, but only if the transaction completes successfully

  • AFTER_FAILURE observers are called during the after completion phase of the transaction, but only if the transaction fails to complete successfully

  • AFTER_COMPLETION observers are called during the after completion phase of the transaction

  • BEFORE_COMPLETION observers are called during the before completion phase of the transaction

Transactional observers are very important in a stateful object model because state is often held for longer than a single atomic transaction.

Imagine that we have cached a JPA query result set in the application scope:

import jakarta.ejb.Singleton;
import jakarta.enterprise.inject.Produces;

@ApplicationScoped @Singleton
public class Catalog {

   @PersistenceContext EntityManager em;

   List<Product> products;

   @Produces @Catalog
   List<Product> getCatalog() {
      if (products==null) {
         products = em.createQuery("select p from Product p where p.deleted = false")
            .getResultList();
      }
      return products;
   }

}

From time to time, a Product is created or deleted. When this occurs, we need to refresh the Product catalog. But we should wait until after the transaction completes successfully before performing this refresh!

The bean that creates and deletes `Product`s could raise events, for example:

import jakarta.enterprise.event.Event;

@Stateless
public class ProductManager {
   @PersistenceContext EntityManager em;
   @Inject @Any Event<Product> productEvent;

   public void delete(Product product) {
      em.delete(product);
      productEvent.select(new AnnotationLiteral<Deleted>(){}).fire(product);
   }

   public void persist(Product product) {
      em.persist(product);
      productEvent.select(new AnnotationLiteral<Created>(){}).fire(product);
   }
   ...
}

And now Catalog can observe the events after successful completion of the transaction:

import jakarta.ejb.Singleton;

@ApplicationScoped @Singleton
public class Catalog {
   ...
   void addProduct(@Observes(during = AFTER_SUCCESS) @Created Product product) {
      products.add(product);
   }

   void removeProduct(@Observes(during = AFTER_SUCCESS) @Deleted Product product) {
      products.remove(product);
   }
}

Enhanced version of jakarta.enterprise.event.Event

Weld API offers slight advantage when dealing with events - org.jboss.weld.events.WeldEvent, an augmented version of jakarta.enterprise.event.Event.

Currently there is only one additional method, select(Type type, Annotation…​ qualifiers). This method allows to perform well-known selection with java.lang.reflect.Type as parameter which means things can get pretty generic. While there are no limitations to what you can select, there are limitation to the WeldEvent instance you perform selection on. In order to preserve type-safety, you have to invoke this method on WeldInstance<Object>. Using any other parameter will result in IllegalStateException. Usage looks just as you would except:

@Inject
WeldEvent<Object> event;

public void selectAndFireEventForType(Type type) {
  event.select(type).fire(new Payload());
}

Stereotypes

The CDI specification defines a stereotype as follows:

In many systems, use of architectural patterns produces a set of recurring bean roles. A stereotype allows a framework developer to identify such a role and declare some common metadata for beans with that role in a central place.

A stereotype encapsulates any combination of:

  • a default scope, and

  • a set of interceptor bindings.

A stereotype may also specify that:

  • all beans with the stereotype have defaulted bean names, or that

  • all beans with the stereotype are alternatives.

A bean may declare zero, one or multiple stereotypes. Stereotype annotations may be applied to a bean class or producer method or field.

A stereotype is an annotation, annotated @Stereotype, that packages several other annotations. For instance, the following stereotype identifies action classes in some MVC framework:

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

We use the stereotype by applying the annotation to a bean.

@Action
public class LoginAction { ... }

Of course, we need to apply some other annotations to our stereotype or else it wouldn’t be adding much value.

Default scope for a stereotype

A stereotype may specify a default scope for beans annotated with the stereotype. For example:

@RequestScoped
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}

A particular action may still override this default if necessary:

@Dependent @Action
public class DependentScopedLoginAction { ... }

Naturally, overriding a single default isn’t much use. But remember, stereotypes can define more than just the default scope.

Interceptor bindings for stereotypes

A stereotype may specify a set of interceptor bindings to be inherited by all beans with that stereotype.

@RequestScoped
@Transactional(requiresNew=true)
@Secure
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}

This helps us get technical concerns, like transactions and security, even further away from the business code!

Name defaulting with stereotypes

We can specify that all beans with a certain stereotype have a defaulted EL name when a name is not explicitly defined for that bean. All we need to do is add an empty @Named annotation:

@RequestScoped
@Transactional(requiresNew=true)
@Secure
@Named
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}

Now, the LoginAction bean will have the defaulted name loginAction.

Alternative stereotypes

A stereotype can indicate that all beans to which it is applied are `@Alternative`s. An alternative stereotype lets us classify beans by deployment scenario.

@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Mock {}

We can apply an alternative stereotype to a whole set of beans, and activate them all with one line of code in beans.xml.

@Mock
public class MockLoginAction extends LoginAction { ... }
<beans>
   <alternatives>
      <stereotype>org.mycompany.testing.Mock</stereotype>
   </alternatives>
</beans>

Stereotypes with @Priority

A stereotype can declare a @Priority annotation which then affects enablement and ordering of beans. This is typically useful in combination with @Alternative to immediately mark a bean as a globally enabled alternative:

@Alternative
@Priority(Interceptor.Priority.APPLICATION + 5)
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface EnabledAlternativeStereotype {}

A @Priority annotation declared directly on the bean always precedes any @Priority annotation inherited via stereotypes.

Stereotype stacking

This may blow your mind a bit, but stereotypes may declare other stereotypes, which we’ll call stereotype stacking. You may want to do this if you have two distinct stereotypes which are meaningful on their own, but in other situation may be meaningful when combined.

Here’s an example that combines the @Action and @Auditable stereotypes:

@Auditable
@Action
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface AuditableAction {}

Built-in stereotypes

CDI defines one standard stereotype, @Model, which is expected to be used frequently in web applications:

@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Model {}

Instead of using JSF managed beans, just annotate a bean @Model, and use it directly in your JSF view!

Specialization, inheritance and alternatives

When you first start developing with CDI, you’ll likely be dealing only with a single bean implementation for each bean type. In this case, it’s easy to understand how beans get selected for injection. As the complexity of your application grows, multiple occurrences of the same bean type start appearing, either because you have multiple implementations or two beans share a common (Java) inheritance. That’s when you have to begin studying the specialization, inheritance and alternative rules to work through unsatisfied or ambiguous dependencies or to avoid certain beans from being called.

The CDI specification recognizes two distinct scenarios in which one bean extends another:

  • The second bean specializes the first bean in certain deployment scenarios. In these deployments, the second bean completely replaces the first, fulfilling the same role in the system.

  • The second bean is simply reusing the Java implementation, and otherwise bears no relation to the first bean. The first bean may not even have been designed for use as a contextual object.

The second case is the default assumed by CDI. It’s possible to have two beans in the system with the same part bean type (interface or parent class). As you’ve learned, you select between the two implementations using qualifiers.

The first case is the exception, and also requires more care. In any given deployment, only one bean can fulfill a given role at a time. That means one bean needs to be enabled and the other disabled. There are a two modifiers involved: @Alternative and @Specializes. We’ll start by looking at alternatives and then show the guarantees that specialization adds.

Using alternative stereotypes

CDI lets you override the implementation of a bean type at deployment time using an alternative. For example, the following bean provides a default implementation of the PaymentProcessor interface:

public class DefaultPaymentProcessor
      implements PaymentProcessor {
   ...
}

But in our staging environment, we don’t really want to submit payments to the external system, so we override that implementation of PaymentProcessor with a different bean:

public @Alternative
class StagingPaymentProcessor
      implements PaymentProcessor {
   ...
}

or

public @Alternative
class StagingPaymentProcessor
      extends DefaultPaymentProcessor {
   ...
}

We’ve already seen how we can enable this alternative by listing its class in the beans.xml descriptor.

But suppose we have many alternatives in the staging environment. It would be much more convenient to be able to enable them all at once. So let’s make @Staging an @Alternative stereotype and annotate the staging beans with this stereotype instead. You’ll see how this level of indirection pays off. First, we create the stereotype:

@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Staging {}

Then we replace the @Alternative annotation on our bean with @Staging:

@Staging
public class StagingPaymentProcessor
      implements PaymentProcessor {
   ...
}

Finally, we activate the @Staging stereotype in the beans.xml descriptor:

<beans
   xmlns="http://xmlns.jcp.org/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
   <alternatives>
         <stereotype>org.mycompany.myapp.Staging</stereotype>
   </alternatives>
</beans>

Now, no matter how many staging beans we have, they will all be enabled at once.

A minor problem with alternatives

When we enable an alternative, does that mean the default implementation is disabled? Well, not exactly. If the default implementation has a qualifier, for instance @LargeTransaction, and the alternative does not, you could still inject the default implementation.

@Inject @LargeTransaction PaymentProcessor paymentProcessor;

So we haven’t completely replaced the default implementation in this deployment of the system. The only way one bean can completely override a second bean at all injection points is if it implements all the bean types and declares all the qualifiers of the second bean. However, if the second bean declares a producer method or observer method, then even this is not enough to ensure that the second bean is never called! We need something extra.

CDI provides a special feature, called specialization, that helps the developer avoid these traps. Specialization is a way of informing the system of your intent to completely replace and disable an implementation of a bean.

Using specialization

When the goal is to replace one bean implementation with a second, to help prevent developer error, the first bean may:

  • directly extend the bean class of the second bean, or

  • directly override the producer method, in the case that the second bean is a producer method, and then

explicitly declare that it specializes the second bean:

@Specializes
public class MockCreditCardPaymentProcessor
      extends CreditCardPaymentProcessor {
   ...
}

When an enabled bean specializes another bean, the other bean is never instantiated or called by the container. Even if the other bean defines a producer or observer method, the method will never be called.

So why does specialization work, and what does it have to do with inheritance?

Since we’re informing the container that our alternative bean is meant to stand in as a replacement for the default implementation, the alternative implementation automatically inherits all qualifiers of the default implementation. Thus, in our example, MockCreditCardPaymentProcessor inherits the qualifiers @Default and @CreditCard.

Furthermore, if the default implementation declares a bean EL name using @Named, the name is inherited by the specializing alternative bean.

Java EE component environment resources

Java EE 5 already introduced some limited support for dependency injection, in the form of component environment injection. A component environment resource is a Java EE component, for example a JDBC datasource, JMS queue or topic, JPA persistence context, remote EJB or web service.

Naturally, there is now a slight mismatch with the new style of dependency injection in CDI. Most notably, component environment injection relies on string-based names to qualify ambiguous types, and there is no real consistency as to the nature of the names (sometimes a JNDI name, sometimes a persistence unit name, sometimes an EJB link, sometimes a non-portable "mapped name"). Producer fields turned out to be an elegant adaptor to reduce all this complexity to a common model and get component environment resources to participate in the CDI system just like any other kind of bean.

Fields have a duality in that they can both be the target of Java EE component environment injection and be declared as a CDI producer field. Therefore, they can define a mapping from a string-based name in the component environment, to a combination of type and qualifiers used in the world of typesafe injection. We call a producer field that represents a reference to an object in the Java EE component environment a resource.

Defining a resource

The CDI specification uses the term resource to refer, generically, to any of the following kinds of object which might be available in the Java EE component environment:

  • JDBC `Datasource`s, JMS `Queue`s, `Topic`s and `ConnectionFactory`s, JavaMail `Session`s and other transactional resources including JCA connectors,

  • JPA `EntityManager`s and `EntityManagerFactory`s,

  • remote EJBs, and

  • web services.

We declare a resource by annotating a producer field with a component environment injection annotation: @Resource, @EJB, @PersistenceContext, @PersistenceUnit or @WebServiceRef.

@Produces @WebServiceRef(lookup="java:app/service/Catalog")
Catalog catalog;
@Produces @Resource(lookup="java:global/env/jdbc/CustomerDatasource")
@CustomerDatabase Datasource customerDatabase;
@Produces @PersistenceContext(unitName="CustomerDatabase")
@CustomerDatabase EntityManager customerDatabasePersistenceContext;
@Produces @PersistenceUnit(unitName="CustomerDatabase")
@CustomerDatabase EntityManagerFactory customerDatabasePersistenceUnit;
@Produces @EJB(ejbLink="../their.jar#PaymentService")
PaymentService paymentService;

The field may be static (but not final).

A resource declaration really contains two pieces of information:

  • the JNDI name, EJB link, persistence unit name, or other metadata needed to obtain a reference to the resource from the component environment, and

  • the type and qualifiers that we will use to inject the reference into our beans.

Note
It might feel strange to be declaring resources in Java code. Isn’t this stuff that might be deployment-specific? Certainly, and that’s why it makes sense to declare your resources in a class annotated @Alternative.

Typesafe resource injection

These resources can now be injected in the usual way.

@Inject Catalog catalog;
@Inject @CustomerDatabase Datasource customerDatabase;
@Inject @CustomerDatabase EntityManager customerDatabaseEntityManager;
@Inject @CustomerDatabase EntityManagerFactory customerDatabaseEntityManagerFactory;
@Inject PaymentService paymentService;

The bean type and qualifiers of the resource are determined by the producer field declaration.

It might seem like a pain to have to write these extra producer field declarations, just to gain an additional level of indirection. You could just as well use component environment injection directly, right? But remember that you’re going to be using resources like the EntityManager in several different beans. Isn’t it nicer and more typesafe to write

@Inject @CustomerDatabase EntityManager

instead of

@PersistenceContext(unitName="CustomerDatabase") EntityManager

all over the place?