What’s new in CDI 2.0

Me, Myself and I

  • Antoine Sabot-Durand
  • Red Hat
  • CDI spec lead
  • @antoine_sd
  • next-presso.com
  • github.com/antoinesd

Agenda

Slides here http://bit.ly/newcdi2
  • Support for Java SE
  • Request Context activation
  • Events enhancement
  • Interceptors enhancement
  • Built-in annotation Literals
  • Configurator APIs

Java SE Support

Why did we add SE support?

To align on many other Java EE spec which support Java SE bootstrapping
To boost CDI adoption for Spec and Frameworks
To provide a mean of building new stacks out of Java EE

Splitting the CDI spec for Java SE

CDI core
CDI for Java SE
CDI for Java EE

Bootstrap API

public static void main(String[] args) {


    SeContainer container = SeContainerInitializer.newInstance() (1)
                                  .disableDiscovery()
                                  .addBeanClasses(MyService.class)
                                  .initialize();


    MyService service = container.select(MyService.class).get(); (2)
    service.sayHello();


    container.close();

}
1SeContainerInitializer is a new api to configure and bootstrap a CDI container under Java SE.
2it returns a SeContainer that implements Instance<Object> allowing programmatic lookup

Main configuration option for the container

public abstract class SeContainerInitializer {
    public SeContainerInitializer addBeanClasses();
    public SeContainerInitializer addPackages();
    public SeContainerInitializer addExtensions();
    public SeContainerInitializer enableInterceptors();
    public SeContainerInitializer enableDecorators();
    public SeContainerInitializer selectAlternatives();
    public SeContainerInitializer selectAlternativeStereotypes();
    public SeContainerInitializer addProperty(); (1)
    public SeContainerInitializer setProperties();
    public SeContainerInitializer disableDiscovery(); (2)
    public SeContainerInitializer setClassLoader(); (3)
1implementation specific properties can be passed to the container
2By default bean discovery mode is annotated, you can disable it and add your bean classes with addBeanClasses() methods

Controlling Request Context

Request context is automatically activated in multiple places

During servlet, filter or listener execution
During Java EE web service invocation
During asynchronous observer invocation
During any remote or asynchronous method invocation of any EJB
During any call to an EJB timeout method and delivery to any MDB
During @Postconstruct callback invocation for any bean

Why adding a manual control?

In Java SE user may want to use @RequestScoped bean
3rd party framework developers may want to have their own request lifecycle without having to create a custom context.

What about other built-in context?

Doesn’t make sense for Application Context
Dependent context is always active by definition
Conversation Context is already controlled programmatically
Session context is very special animal, we may consider adding this for it in a next CDI version

Control via interceptor

@ApplicationScoped
public class MyBean {



    @ActivateRequestContext (1)
    public void doRequest(String body) {

    
    // Request Context will be activated during this invocation


    }

}
1CDI 2.0 provides @ActivateRequestContext interceptor to activate request during method invocation

Pogrammatic control

@ApplicationScoped
public class MyBean {



    @Inject

    private RequestContextController requestContextController;
 (1)

    public void doRequest(String body) {

        // activate request context

        requestContextController.activate();



        // do work in a request context.



        // deactivate the request context

        requestContextController.deactivate();

    }


}
1CDI 2.0 provides a new built-in bean of type RequestContextController to allow manual activation and deactivation of the Request Context.

Events enhancement

CDI 2.0 events enhancement

Events ordering
Asynchronous events
Better programmatic solution to fire event
Better observers control and creation

Events enhancement

Observers ordering

Ordering Observers

By adding a @Priority (from commons annotations) on an observer.
The lowest value is first
Observers with no explicit priority have a middle range priority
Observer with the same priority are called in an undefined order
No priority on async events

Ordering Observers

public void observer1(@Observes @Priority(1) Payload p) { ... }(1)


public void observer2(@Observes @Priority(2) Payload p) { ... }
1Commons Annotation 1.3 now allows using @Priority on a method parameter
Also work on extension observers:
public class MyExtension implements Extension {



  public void firstPat(@Observes @Priority(1) ProcessAnnotatedType<?> pat) {
  }

  public void secondPat(@Observes @Priority(2) ProcessAnnotatedType<?> pat) {
  }
}

Events enhancement

Asynchronous events

We have improved the Event interface

public interface Event<T> {
     ...

    public void fire(T event); (1)

    public CompletionStage<T> fireAsync(T event); (2)

    public CompletionStage<T> fireAsync(T event, NotificationOptions options); (3)

     ...
}
1CDI 1.0 synchronous event firing
2Async event firing. It returns a object implementing the Java 8 CompletionStage<T> (like CompletableFuture)
3Async event firing with options (to provide a specific Executor or a implementation specific property)

So firing an async event is as easy as…​

@ApplicationScoped
public class MyBean {

    @Inject
    
Event<Payload> event;
 (1)

    public void someEvenMoreCriticalBusinessMethod() {
    
    event.fireAsync(new Payload()); (2)
    
}
}
1We still inject the Event built-in bean
2Here we call fireAsync in a call and forget pattern (do nothing with CompletionStage)

On a consuming side…​

We introduced a new observer kind: @ObservesAsync
@ApplicationScoped
public class MyOtherBean {

    public void callMe(@ObservesAsync Payload payload) {
 (1)
        // I am called in another thread
    }

}
1this observer will be invoked asynchronously

Sync Vs Async in a Nutshell

Sync events (with fire()) only trigger @Observes
Async event (with fireAsync()) only trigger @ObservesAsync

Events enhancement

Better programmatic solution to fire events

Events outside programming model

When you can’t @Inject the Event built-in bean
BeanManager bm = CDI.current.getBeanManager(); (1)

bm.fireEvent(myPayload); (2)
1you need to retrieve the BeanManager (if you are in an extension, you can inject it in any lifecycle observer)
2using the legacy fireEvent method

In CDI 2.0 we introduced getEvent()

More type safe approach
Similar to Event<Object> injection so less side effect
reusable and allows fireAsync()
BeanManager bm = CDI.current.getBeanManager();

bm.getEvent().fireAsync(myPayload); (1)
1firing an asynchronous event

Observers and Extension

Better programmatic solution to fire events

ProcessObserverMethod enhancement

You can now configure ObserverMethod way more in Extensions
We added the following methods to ProcessObserverMethod
    public void setObserverMethod(ObserverMethod<T> observerMethod); (1)

    public ObserverMethodConfigurator<T> configureObserverMethod(); (2)

    public void veto(); (3)
1replacing the ObserverMethod by a custom one.
2configuring the observed ObserverMethod to change its behavior
3vetoing the observer method (it will be ignored)

Interceptors enhancement

Interceptors easier to apply

Introducing InterceptionFactory

public interface InterceptionFactory<T> {

    InterceptionFactory<T> ignoreFinalMethods(); (1)

    AnnotatedTypeConfigurator<T> configure(); (2)

    T createInterceptedInstance(T instance); (3)
}
1forces creation of the interceptor even if the class to intercept has non static final methods (those shouldn’t be called in the intercepted instance of course)
2allow addition of Interceptor bindings on the class and or method of the class to intercept
3create an intercepted version of the instance passed in parameter

Accessing InterceptionFactory

By injecting the InterceptionFactory built-in bean. Useful to apply interceptors on a produced bean
@Produces


public MyClass createMyClass(InterceptionFactory<MyClass> ify) {

     
ify.configure().addAnnotation(new AnnotationLiteral<Transactional>() {};
    return ify.createInterceptedInstance(new MyClass());


}
By calling BeanManager.createInterceptionFactory(). Useful for custom beans creation or other advanced usages

Applying interceptor binding on methods

You can add your interceptor on specific methods instead of all class
@Produces


public MyClass createMyClass(InterceptionFactory<MyClass> ify) {

    AnnotatedTypeConfigurator<MyClass> atc = ify.configure();


    atc.filterMethods(m -> m.getJavaMember().getName().equals("doSomething")) (1)
        .findFirst()
        .ifPresent(m -> m.add(new AnnotationLiteral<Transactional>() { }));
 (2)
    return ify.createInterceptedInstance(new MyClass());

 (3)
}
1retrieving the doSomething() method in MyClass
2adding @Literal on it
3returning an instance of MyClass whose doSomething() is now transactional

Built-in annotation Literals

Annotation Literals: the type safe way

CDI is using Annotation Literals in various places
It’s a mean to provide an annotation instance
So we provided AnnotationLiterals for:
  • All scopes
  • @Inject
  • @Qualifier
  • @Alternative & @Specializes
  • @Typed
  • @Vetoed
  • @NonBinding
  • @New

Using these literals

If the annotation doesn’t have members it’s straightforward
ApplicationScoped apsl = ApplicationScoped.Literal.INSTANCE; (1)
1for annotation included in CDI, Literal is defined the annotation class
If the annotation have members use the of() method
Named myNamed = NamedLiteral.of("myName"); (1)
1annotations coming from outside CDI have their literal in javax.enterprise.inject.literal package

Configurators API

SPI made easy

CDI SPI is a very powerful tool

Yet, it is mainly used by advanced users
In CDI 1.x using SPI use to generate verbose and non elegant code
In CDI 2.0 the configurators solve this
So in there are no more excuse to avoid SPI coding

SPI elements having a configurator

Major element having a configurator
  • AnnotatedType hierarchy
  • BeanAttributes
  • Bean
  • InjectionPoint
  • ObserverMethod
  • Producer
These configurators are accessible in lifecycle container event when writing extensions
But also in the standard programming model.
Did you notice where we used one configurator in previous slides?

Imagine this use case

You use a legacy framework you don’t own
This framework instantiates components using a config Map
public class LegacyClass {
...
    public void setConfig(Map<String, Object> config) {
        this.config = config;
    }
...
}
You’d like to inject the map instead of setting it the classical way

Solving this in CDI 1.2 1/

You’ll use an InjectionTargetFactory to do the job (and a producer for the config Map)
To have the map injected you’ll need to to create an AnnotatedType that simulate this
public class LegacyClass {
...
    @Inject
    public void setConfig(Map<String, Object> config) {
        this.config = config;
    }
...
}

Solving this in CDI 1.2 - Perform the injection

Injection code will look like
public class InjectionBean {

    @Inject
    BeanManager bm;

    public LegacyClass getInjectedLegacy(LegacyClass component) {
        AnnotatedType<LegacyClass> atmc = bm.createAnnotatedType(LegacyClass.class);
        bm.getInjectionTargetFactory(new LegacyAnnotatedType(atmc)) (1)
                .createInjectionTarget(null)
                .inject(component,bm.createCreationalContext(null));
        return component;
    }
}
1the verbose part

Solving this in CDI 1.2 - Implementing AnnotatedType

public class LegacyAnnotatedType implements AnnotatedType<LegacyClass> {

    AnnotatedType<LegacyClass> delegate;

    Set<AnnotatedMethod<? super LegacyClass>> methods = new HashSet<>();

    public LegacyAnnotatedType(AnnotatedType<LegacyClass> atmc) {
        delegate = atmc;
        for (AnnotatedMethod<? super LegacyClass> am : delegate.getMethods()) {
            if(am.getJavaMember().getName().equals("setConfig"))
                methods.add(new InjectedMethod(am));
            else
                methods.add(am);
        }
    }

    public Set<AnnotatedMethod<? super LegacyClass>> getMethods() {
        return methods;
    }

    // 8 more methods calling delegate
}

Solving this in CDI 1.2 - Implementing AnnotatedMethod

public class InjectedMethod<T> implements AnnotatedMethod<T> {

    AnnotatedMethod<T> delegate;
    Set<Annotation> annotations;

    public InjectedMethod(AnnotatedMethod<T> am) {
        delegate = am;
        annotations = new HashSet<>(delegate.getAnnotations());
        annotations.add(new AnnotationLiteral<Inject>() {
        });
    }

    @Override
    public Set<Annotation> getAnnotations() {
        return annotations;
    }

    @Override
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
        if(annotationType.equals(Inject.class))
            return true;
        return delegate.isAnnotationPresent(annotationType);
    }

    // 8 more methods calling delegate
}

Solving this in CDI 2.0 - In original code

@ApplicationScoped
public class InjectionBean {

    @Inject
    BeanManager bm;

    public LegacyClass getInjectedLegacy(LegacyClass component) {
        AnnotatedType<LegacyClass> atmc = bm.createAnnotatedType(LegacyClass.class);

        InjectionTargetFactory<LegacyClass> itf = bm.getInjectionTargetFactory(atmc));
        itf.configure().filterMethods(m -> m.getJavaMember()
                .getName().equals("setConfig"))
                .findFirst()
                .ifPresent(m -> m.add(InjectLiteral.INSTANCE));

        itf.createInjectionTarget(null)
                .inject(component, bm.createCreationalContext(null));

        return component;
    }

}

Playing with CDI 2.0

You have 2 out of the box solutions

CDI SE with Weld 3.0
Patch JBoss WildFly to update CDI

CDI SE

Just add this dep to your pom.xml
<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-shaded</artifactId>
    <version>3.0.0.CR2</version>
</dependency>
And you’re ready to
public class RunMe {

    public static void main(String[] args) {
        SeContainer container = SeContainerInitializer.newInstance()
                .initialize();
        // Put your code here
    }
}

Patch JBoss WildFly

Get the patch for WildFly: http://download.jboss.org/weld/3.0.0.CR2/
Get WildFly 10.1.0.Final: http://wildfly.org/downloads/
Unzip WildFly (not the patch) and go to $WILDFLY_HOME/bin directory
Type the following command:
$ ./jboss-cli.sh --command="patch apply $PATH_TO_PATCH/wildfly-10.1.0.Final-weld-3.0.0.CR2-patch.zip"
Now you have a WildFly server running CDI 2.0 with Weld 3.0.0

Conclusion

References

Slides are accessible here http://bit.ly/newcdi2
Slides source https://github.com/antoinesd/Whats-new-in-CDI-2.0
Slides generated with Asciidoctor and DZSlides backend
Original slide template - Dan Allen & _Sarah White
License of this doc: CC BY-SA 4.0

Antoine Sabot-Durand