Container Control Module

Overview

The Container Control module provides CDI container booting and shutdown, crucial for CDI use in Java SE6+ environments, and associated context lifecycle management. The module abstracts individual CDI container implementations, ensuring projects are container-independent.

Project Setup

The configuration information provided here is for Maven-based projects and it assumes that you have already declared the DeltaSpike version and DeltaSpike Core module for your projects, as detailed in Configure DeltaSpike in Your Projects. For Maven-independent projects, see Configure DeltaSpike in Maven-independent Projects.

Enable CDI For Your Java Environment

This module requires a CDI implementation to be available in the Java environment where your projects are deployed. Dependent on the Java environment you choose, some setup may be necessary as detailed at the Enable CDI For Your Java Environment page.

Declare Container Control Module Dependencies

Add the Container Control module to the list of dependencies in the project pom.xml file using this code snippet:

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

Or if you’re using Gradle, add these dependencies to your build.gradle:

     compile 'org.apache.deltaspike.cdictrl:deltaspike-cdictrl-api'

Start the CDI Container from Your Project

To start a CDI container in your application, you must instantiate a CdiContainer object and call the #boot method. When #boot is called, the CdiContainer scans CDI-enabled archives for beans and CDI extensions. Before the application exits, #shutdown must be called to correctly destroy all beans. An example is given in the code snippet here.

import org.apache.deltaspike.cdise.api.CdiContainer;
import org.apache.deltaspike.cdise.api.CdiContainerLoader;

public class MainApp {
    public static void main(String[] args) {

        CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();

        // You can use CDI here

        cdiContainer.shutdown();
    }
}

Starting the container does not automatically start all CDI Contexts. Contexts must be started independently using the provided ContextControl class. An example of starting the Context for @ApplicationScoped beans is added to the code snippet here.

import org.apache.deltaspike.cdise.api.CdiContainer;
import org.apache.deltaspike.cdise.api.CdiContainerLoader;
import org.apache.deltaspike.cdise.api.ContextControl;
import javax.enterprise.context.ApplicationScoped;

public class MainApp {
    public static void main(String[] args) {

        CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();

        // Starting the application-context enables use of @ApplicationScoped beans
        ContextControl contextControl = cdiContainer.getContextControl();
        contextControl.startContext(ApplicationScoped.class);

        // You can use CDI here

        cdiContainer.shutdown();
    }
}

To resolve project beans, you can use the DeltaSpike BeanProvider class. Whether EchoService is a concrete implementation or just an interface depends on the application. In the case that it is an interface, the corresponding implementation is resolved. The resolved bean is a standard CDI bean and it can be used for all CDI concepts, such as @Inject, in the class without further uses of BeanProvider. An example of resolving the bean without qualifiers is given in the code snippet here.

EchoService echoService = BeanProvider.getContextualReference(EchoService.class, false);

CdiContainer

The CdiContainer interface provides booting and shutdown of the CDI containers from deployed applications, with CdiContainerLoader a simple factory providing access to the underlying CdiContainer implementation.

This is useful to Java SE6+ applications in which a standalone CDI implementation must be provided and booted and shutdown by the application. Booting and shutdown of the CDI container for Java EE and servlet containers is managed by the servlet container integration.

For instructions and examples on using this feature in your projects, see Enable CDI For Your Java Environment: Java SE6+.

ContextControl Usage

The ContextControl interface provides life-cycle control of the CDI container built-in contexts. This includes starting and stoping built-in standard contexts like @RequestScoped, @ConversationScoped, and @SessionScoped. It is provided as an @Dependent bean and can be injected in the classic CDI way. This feature can be used and is helpful in all Java environments, including Java SE, as illustrated here.

Procedure for building an uber jar

Uber jar or executable jar can created by using the maven shade plugin. Some things you needs to be aware of when you use it.

  • Multiple beans.xml and javax.enterprise.inject.spi.Extension files needs to be merged into the final jar using a transformer.

<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
  • The asm:asm:3.3.1 transitive dependency of OpenWebBeans isn’t properly included in the Uber jar. Add it as a project dependency if you use OWB. (Only needed for OWB 1.1.8 !)

  • Some frameworks, like logging frameworks, aren’t CDI compatible. So you need to exclude them from scanning. Use for example the scan feature of Weld to define which packages needs to be excluded.

Restart the RequestContext in Unit Tests

In unit testing it can be necessary to test with attached and also with detached JPA entities. A very common approach for JPA is the entitymanager-per-request approach and thus have a producer method which creates a @RequestScoped EntityManager. Since a single unit test is usually treated as one ‘request’ a problem arises detaching entities.

Using ContextControl to Detach Entities
@Test
public void testMyBusinessLogic()
{
    doSomeJpaStuff()
    MyEntity me = em.find(...);

    ContextControl ctxCtrl = BeanProvider.getContextualReference(ContextControl.class);

    //stop the RequestContext to dispose of the @RequestScoped EntityManager
    ctxCtrl.stopContext(RequestScoped.class);

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

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

Attach a RequestContext to a New Thread in EE

Accessing the @RequestScoped bean in a new thread will result in a ContextNotActiveException. The RequestContext usually gets started for a particular thread via a simple ServletRequestListener. So "no servlet-request" means that there is no Servlet-Context for the current (/new) Thread. You might face such issues, if you would like to reuse business services in for example a Quartz Job.

Using ContextControl to Control the RequestContext for a Quartz-Job
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 the current thread
        ctxCtrl.startContext(RequestScoped.class);

        try
        {
            doYourWork();
        }
        finally
        {
            //stop the RequestContext to ensure that all request-scoped beans get cleaned up.
            ctxCtrl.stopContext(RequestScoped.class);
        }
    }
}

Embedded Servlet Support

From DeltaSpike 1.0.2, you can use DeltaSpike to power embedded Servlet runtimes. This work is done via Servlet Listeners. The configuration is specific to each container, below are some examples.

The two main listeners are CdiServletRequestListener and CdiServletContextListener. CdiServletRequestListener is responsible for starting a RequestContext on each incoming request. In most containers this is all you need. For Tomcat specifically, you need to use CdiServletContextListener which registers the CdiServletRequestListener.

The main use case for this feature is for lightweight embedded runtimes, microservices. For each of these, it is assumed that you are using the following start up code somewhere:

CdiContainer cdiContainer = CdiContainerLoader.getCdiContainer();
cdiContainer.boot();
cdiContainer.getContextControl().startContexts();

Jetty

For Jetty, you need to add an EventListener which will be your CdiServletRequestListener. The object must be instantiated. This must be done before the server is started.

Server server = new Server(port);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);

context.addEventListener(new CdiServletRequestListener());
context.addServlet(new ServletHolder(new YourServlet()),"/*");

server.start();

Undertow

For Undertow, you register the CdiServletRequestListener via ListenerInfo by passing in the class to their builders. Then you add the ListenerInfo to your deployment before starting.

ServletInfo servletInfo = Servlets.servlet("YourServletName", YourServlet.class).setAsyncSupported(true)
    .setLoadOnStartup(1).addMapping("/*");
ListenerInfo listenerInfo = Servlets.listener(CdiServletRequestListener.class);
DeploymentInfo di = new DeploymentInfo()
        .addListener(listenerInfo)
        .setContextPath("/")
        .addServlet(servletInfo).setDeploymentName("CdiSEServlet")
        .setClassLoader(ClassLoader.getSystemClassLoader());
DeploymentManager deploymentManager = Servlets.defaultContainer().addDeployment(di);
deploymentManager.deploy();
Undertow server = Undertow.builder()
        .addHttpListener(port, "localhost")
        .setHandler(deploymentManager.start())
        .build();
server.start();

Tomcat

For Tomcat, you need to register the CdiServletContextListener instead of the CdiServletRequestListener. It is added as an ApplicationListener by passing in the class name as a String.

Tomcat tomcat = new Tomcat();
tomcat.setPort(port);
File base = new File("...");
Context ctx = tomcat.addContext("/",base.getAbsolutePath());
StandardContext standardContext = (StandardContext)ctx;
standardContext.addApplicationListener(CdiServletContextListener.class.getName());
Wrapper wrapper = Tomcat.addServlet(ctx,"YourServlet",YourServlet.class.getName());
wrapper.addMapping("/*");
tomcat.start();