Control CDI Containers in SE and EE

The Problem

Have you ever tried to boot a CDI container like JBoss Weld or Apache OpenWebBeans in Java SE? While doing this task you will end up knee deep in the implementation code of the container you are using! Not only is this a complicated task to do, but your code will also end up being non-portably because you need to invoke container specific methods.

Where do I need this at all?

Booting a CDI container is not only useful if you like to hack Java SE apps, like a standalone SWT application. It is also very valuable if you can boot a CDI container for the unit tests of your business services and you don’t like to setup Arquillian for that task.

The Solution – Apache DeltaSpike CdiControl

The Apache DeltaSpike project [1][2] is a collection of CDI-Extensions which are created by a large community of CDI enthusiasts. It consists of most of the Apache MyFaces CODI and JBoss Seam3 community members, plus other high-profile experts in this area. The functionality of DeltaSpike is growing with each day!

One of the many features which DeltaSpike already provides in this yet early stage is a way to boot any CDI-Container and control it’s Context lifecycles via a few very simple interfaces [3]. Your code will end up being completely independent of the CDI implementation you use!

There are currently two implementations of this API, one for Apache OpenWebBeans, the other one for JBoss Weld. Both also successfully passed a small internal TCK test suite. The respective implementation simply gets activated by putting the correct impl-JAR into the classpath.

Maven Integration

For an Apache Maven project, you can just add the cdictrl-api and one of the impl JARs as dependencies in your pom.xml.

For using it with Apache OpenWebBeans:

<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-api</artifactId>
    <version>${deltaspike.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-owb</artifactId>
    <version>${deltaspike.version}</version>
</dependency>

For JBoss Weld it’s almost the same, just with the weld impl jar:

<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-api</artifactId>
    <version>${deltaspike.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.deltaspike.cdictrl</groupId>
    <artifactId>deltaspike-cdictrl-weld</artifactId>
    <version>${deltaspike.version}</version>
</dependency>

Note: Those features will get released with deltaspike-0.2-incubating. In the meantime the deltaspike.version is 0.2-incubating-SNAPSHOT and it is available in the Apache snapshots repository:
https://repository.apache.org/content/repositories/snapshots/org/apache/deltaspike/cdictrl/deltaspike-cdictrl-api/0.2-incubating-SNAPSHOT/

How to use the API

There are basically two parts

  1. The CdiContainer Interface will provide you with a way to boot and shutdown the CDI Container in JavaSE apps.
  2. The ContextControl interface is provides a way to control the lifecycle of the built-in Contexts of the CDI container.

CdiContainer usage

You can use the CdiContainerLoader as a simple factory to gain access to the underlying CdiContainer implementation. If you like to boot a CDI container for a unit test or in a Java SE application then just use the following fragment:

// this will give you a CdiContainer for Weld or OWB, depending on the jar you added
CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();

// now we gonna boot the CDI container. This will trigger the classpath scan, etc
cdiContainer.boot();

// and finally we like to start all built-in contexts
cdiContainer.getContextControl().startContexts();

// now we can use CDI in our SE application. 
// And there is not a single line of OWB or Weld specific code in your project!

// finally we gonna stop the container 
cdiContainer.shutdown();

Pretty much self explaining, isn’t? Of course, those 2 classes are of no interest for Java EE applications since the CDI Container already gets properly booted and shut down by the Servlet container integration.

ContextControl usage

The ContextControl interface allows you to start and stop built-in standard Contexts like @RequestScoped, @ConversationScoped, @SessionScoped, etc. It is provided as @Dependent bean and can get injected in the classic CDI way. This is not only usable in Java SE projects but also very helpful in Servlets and Java EE containers!

The following samples should give you an idea about the power of this tool:

Restarting the RequestContext in a unit test

I pretty frequently had the problem that I needed to test my classes with attached and also with detached JPA entities. In most of our big real world projects we are using the entitymanager-per-request approach [4] and thus have a producer method which creates a @RequestScoped EntityManager. Since a single unit test is usually treated as one ‘request’ I had problems detaching my entities. With the ContextControl this is no problem anymore as the following code fragment shows:

@Test
public void testMyBusinessLogic() {
  doSomeJpaStuff()
  MyEntity me = em.find(...);
  
  ContextControl ctxCtrl = BeanProvider.getContextualReference(ContextControl.class);

  // stoping the request context will dispose the @RequestScoped EntityManager
  ctxCtrl.stopContext(RequestScoped.class);

  // and now immediately restart the context again
  ctxCtrl.startContext(RequestScoped.class);

  // the entity 'em' is now in a detached state!
  doSomeStuffWithTheDetachedEntity(em);
}

Attaching a Request Context to a new thread in EE

Everyone who tried to access @RequestScoped CDI beans in a new Thread created in a Servlet or any other Java EE related environment has for sure experienced the same pain: accessing the @RequestScoped bean will result in a ContextNotActiveException. But how comes? Well, the Request Context usually gets started for a particular thread via a simple ServletRequestListener. The problem is obvious: no servlet-request means that there is no Servlet Context for the Thread! But this sucks if you like to reuse your nice business services in e.g. a Quartz Job. The ContextControl can help you in those situations as well:

public class CdiJob implements org.quartz.Job {
  public void execute(JobExecutionContext context) throws JobExecutionException {
    ContextControl ctxCtrl = 
      BeanProvider.getContextualReference(ContextControl.class);

    // this will implicitly bind a new RequestContext to your current thread
    ctxCtrl.startContext(RequestScoped.class);

    doYourWork();

    // at the end of the Job, we gonna stop the RequestContext
    // to ensure that all beans get properly cleaned up.
    ctxCtrl.stopContext(RequestScoped.class);
  }
}

And there are tons of other situations where this can be useful…

LieGrue,
strub

PS: Gonna change the motd to ‘All is under (cdi-) control!’
PPS: we will most probably introduce a similar API in the CDI-1.1 specification

[1] https://git-wip-us.apache.org/repos/asf?p=incubator-deltaspike.git
[2] https://issues.apache.org/jira/browse/DELTASPIKE
[3] https://git-wip-us.apache.org/repos/asf?p=incubator-deltaspike.git;a=tree;f=deltaspike/cdictrl/api/src/main/java/org/apache/deltaspike/cdise/api
[4] http://docs.redhat.com/docs/en-US/JBoss_Enterprise_Web_Server/1.0/html/Hibernate_Entity_Manager_Reference_Guide/transactions.html

About struberg
I'm an Apache Software Foundation member and Java Champion blogging about Java, µC, TheASF, OpenWebBeans, Maven, MyFaces, CODI, GIT, OpenJPA, TomEE, DeltaSpike, ...

9 Responses to Control CDI Containers in SE and EE

  1. Geoffrey says:

    Pls dont abreviatte the mvn name to ctrl. Use control, its searachable and understandable.

  2. struberg says:

    Hi Geoffrey!

    Please join the currently ongoing discussion on the Apache DeltaSpike discussion list [1] and post your ideas over there. Every input and feedback is welcome!

    We are currently incorporating some of the new feedback already.
    We will e.g. most probably switch back from startContext(scopeAnnotation) to the startRequestContext() + startSessionContext() + … as I originally had with my CdiTest [2].
    Pete Muir had the good argument that this will be easier to extend in the future and will allow us to add methods like attachSessionContext(String sessionId) etc.
    Once we will reach an agreement I’ll update this blog post to reflect the final API.

    LieGrue,
    strub

    [1] deltaspike-dev-subscribe [a t] incubator.apache.org for subscribing and then mailto deltaspike-dev [a t] incubator.apache.org
    [2] https://svn.apache.org/repos/asf/openwebbeans/trunk/webbeans-test/cditest/src/main/java/org/apache/webbeans/cditest/CdiTestContainer.java

  3. Pingback: Tab Sweep – Upgrade to Java EE 6, Groovy NetBeans, JSR310, JCache interview, OEPE, and more « oracle fusion identity

  4. mpashworth says:

    Is it possible to get inject classes into a JUnit test and use these instances in the actual test? I am using Weld 1.1.6.

    I have tried:-

    @Inject @ProcessType
    private Processor processor; //It seems that Weld / DeltaSpike is processing the unit test class

    But in the test the processor instance is null and also I cannot seem to be able to retrieve the actual instance using the instance() method on Weld because CdiContainer does not have the instance method. How should I retrieve the instance that was injected into the unit test?

    Also the DeltaSpike version seems to be 0.2-incubating-SNAPSHOT and not deltaspike-0.2-incubating-SNAPSHOT.

  5. struberg says:

    Hi!
    I’ve now fixed the version, txs 4 the catch!

    I will try to tinker together another blog post explaining what I do in my own unit tests. Please give me a few hours since I need to do my $$dayjob first 😉

    LieGrue,
    strub

  6. Pingback: Unit Testing Strategies for CDI based projects « Struberg's Blog

  7. Manoj says:

    Might be a stupid question but does it support portlets?

    • struberg says:

      If you talk about the ContextControl, then yes it should also support Portlet environments. The problems you will hit here will not have much to do with CDI but with propagating the information from the FacesContext to a new Thread for example.

  8. Pingback: Control CDI Containers in SE and EE with Apache DeltaSpike CdiControl | mauroprogram's Blog

Leave a reply to Geoffrey Cancel reply