Weld 3.0.0.Alpha1 released!

2014-10-2
Jozef Hartinger

Today we are releasing Weld 3.0.0.Alpha1. This release serves as an early proof of concept of some of the ideas that are being discussed by the CDI Expert Group for the upcoming CDI 2.0 specification. Furthermore, this is a great opportunity for the community to test-drive some of the proposed improvements and provide us with feedback. Be warned though that this released does not come with any guarantee of stability and that binary and functional compatibility is likely to be broken in the following releases.

Ordering of observer methods

The first of the new features are ordered observer methods. This requirement came up a long ago and several approaches were proposed in the meantime, as documented by CDI-4.The Alpha1 release employs the general-purpose @Priority annotation to determine the order in which observer methods are to be notified.

There are many open questions about this feature. Here are some of the rules we set for the purpose of this experimental implementation. These are by no means set in stone but instead we encourage you to give us feedback on these:

  • Each observer method has a certain priority value. An observer method that does not define a priority explicitly is given the default priority which is 2500 (in the middle of the Interceptor.Priority.APPLICATION range)

  • The priority of an observer method determines the order. An observer method with lower value is called before an observer method with higher value

  • If multiple observer methods define the same priority value, the order is undefined

  • Observer methods priorities should match existing priority range convention

  • The ordering applies to both transactional and non-transactional observer methods

  • The priority annotation is applied on the event parameter (not the observer method)

Here’s an example:

public void sendWelcome(@Observes @Priority(APPLICATION + 800) @Registered User user) {
    // ...
}

Note that javax.annotation.Priority can currently only be applied to types. Therefore, we temporarily created a substitute called org.jboss.weld.experimental.Priority which is identical to the original except that it can also be applied on parameters (of observer methods). The plan is to propagate this change back to javax.annotation.Priority. In the meantime, Weld’s twin gets the job done.

In addition to the @Priority annotation, the SPI was also enhanced to expose the priority of an observer method. We are not touching javax.enterprise interfaces just yet. Instead, the org.jboss.weld.experimental package contains proposals for how the new SPI should look like. As the package name suggests, this SPI is good for a test-drive but do not expect it to be stable. To use the experimental SPIs, you’ll need to add a dependency on weld-api.

Here’s an example of reading the observer method priority using the SPI in an Extension. ExperimentalProcessObserverMethod and ExperimentalObserverMethod interfaces come from the org.jboss.weld.experimental package.

public class SimpleExtension implements Extension {

    void observe(@Observes ExperimentalProcessObserverMethod<User, ?> event) {
        ExperimentalObserverMethod<User> observerMethod = event.getObserverMethod();
        this.priority = observerMethod.getPriority();
    }
}

Use the corresponding CDI ticket for any feedback on these features.

Vetoing and modifying observer methods

We’ll stay with observer methods for a few more paragraphs. A request was raised in the CDI issue tracker for making it possible to disable an observer method using the ProcessObserverMethod SPI.

This Alpha1 release introduces the veto() methods (aligned with ProcessAnnotatedType.veto()) for this:

void disableDebuggingObservers(@Observes ExperimentalProcessObserverMethod<?, Debug> event) {
    if (projectStage != ProjectStage.Development) {
        event.veto();
    }
}

In addition, the observer method metadata may be altered by an extension. This is done similarly to how InjectionTarget, InjectionPoint or BeanAttribute metadata are modified which is most often by wrapping the original object (decorator design pattern). Any piece of metadata (including aforementioned priority) can be altered. For example, the transaction phase:

void alterObserver(@Observes ExperimentalProcessObserverMethod<User, ?> event) {
    event.setObserverMethod(new ForwardingExperimentalObserverMethod<User>(event.getObserverMethod()) {
        @Override
        public TransactionPhase getTransactionPhase() {
            return TransactionPhase.AFTER_SUCCESS;
        }
    });
}

Repeatable qualifiers and interceptor bindings

This release serves as a proof of concept for supporting repeating qualifiers and interceptor bindings. You can now fully utilize Java 8 features and use multiple qualifiers or interceptor bindings of the same type in the same location, for example:

public class School {

    @Produces
    @Speaks("English")
    @Speaks("French")
    public Student graduate() {
        // ...
    }
}

Repeating qualifiers can be used for both bean and event resolution. See the Java documentation for how to define a repeating annotation.

Interceptor bindings in invocation context

This is an often recurring scenario. An interceptor binding defines several @NonBinding members which serve as configuration for the interceptor. How does an interceptor get hold of these values? This is not easy as the interceptor binding may often appear on the intercepted method, the class that defined it or may be inherited from another interceptor binding or stereotype (even transitively!). This problem gave birth to utilities such as this one. Still, even after all this effort the result is not entirely correct as it was obtained using Java reflection ignoring the fact that the interceptor binding may have been modified (e.g. using ProcessAnnotatedType).

This problem is being addressed as CDI-468. The Alpha1 release of Weld implements this feature and exposes new methods for obtaining interceptor bindings that are in effect for the interception. This is done using ExperimentalInvocationContext.getInterceptorBindings() or ExperimentalInvocationContext.getInterceptorBindingsByType().

Again, ExperimentalInvocationContext can be found in the org.jboss.weld.experimental package and depicts how the future version of javax.interceptor.InvocationContext could look like.

@Interceptor
@Secure
public class SecurityInterceptor {

    @Inject
    private User user;

    @AroundInvoke
    public Object intercept(ExperimentalInvocationContext ctx) throws Exception {
        Secure binding = ctx.getInterceptorBindingsByType(Secure.class).iterator().next();
        if (!user.getRoles().contains(binding.requireRole())) {
            throw new SecurityException();
        }
        return ctx.proceed();
    }
}

Again, your feedback is welcome at CDI-468.

Changes in the Annotated layer

CDI provides an abstraction over the Reflection API - AnnotatedType and friends - which mostly allows extensions to alter the set of annotations present on a type, field, method, etc.

The Alpha1 release contains two minor additions to this API. Firstly, the API now supports Java 8 repeating annotations with a new method ExperimentalAnnotated.getAnnotationsByType(Class<T> annotationClass)

Secondly, it is now possible to access the java.lang.reflect.Parameter instance that is wrapped by AnnotatedParameter using ExperimentalAnnotatedParameter.getJavaParameter();

See CDI-471 and CDI-481 for details.