blob: f7ff77f407a6ef18d7e3aa641ecaca8e33941b27 [file] [log] [blame]
= Container Control Module
:Notice: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at. http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
== 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#, Configure DeltaSpike in Your Projects>>. For Maven-independent projects, see <<configure#config-maven-indep,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 <<cdiimp#,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:
[source,xml]
----
<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`:
[source]
----
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.
[source,java]
----
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.
[source,java]
----
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.
[source,java]
----
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 <<cdiimp#javase6,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.
[source,xml]
----
<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
http://docs.redhat.com/docs/en-US/JBoss_Enterprise_Web_Server/1.0/html/Hibernate_Entity_Manager_Reference_Guide/transactions.html[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
[source,java]
---------------------------------------------------------------------------------------
@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
[source,java]
---------------------------------------------------------------------------------------------
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:
[source,java]
-----------------------------------------------------------------
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.
[source,java]
------------------------------------------------------------------------------------------
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.
[source,java]
--------------------------------------------------------------------------------------------------------
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`.
[source,java]
-----------------------------------------------------------------------------------
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();
-----------------------------------------------------------------------------------