<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-api</artifactId>
<version>${deltaspike.version}</version>
<scope>compile</scope>
</dependency>
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.
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.
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.
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'
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);
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+.
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.
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.
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.
@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);
}
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.
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);
}
}
}
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();
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();
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();
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();