CdiCtrl
is not part of Apache OpenWebBeans but a module of
Apache DeltaSpike.
The CdiCtrl
interface abstracts away all the logic to boot a CDI Container
and controls the lifecycle of it's Contexts (Request Context, Session Context, etc).
The actual CDI Container is determined by using the java.util.ServiceLoader
.
There are a few different implementations available. Besides Apache OpenWebBeans
there are also plugins for JBoss Weld and Apache TomEE.
The following are the dependencies you need in your Apache Maven pom.xml file in addition to OWB itself:
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-api</artifactId>
<version>$ {deltaspike.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.deltaspike.cdictrl</groupId>
<artifactId>deltaspike-cdictrl-owb</artifactId>
<version>$ {deltaspike.version}</version>
<scope>test</scope>
</dependency>
Whenever you need to write unit tests for a full application, then you will need to
have a CDI container scann all your classes, create Bean<T>
from it and provide
them for injection. All this can be done by either using JUnits @RunWith
or
by simply creating a common base class for your unit tests which boots up the
container on your test classpath.
There is no need to restart the container for each and every of your unit tests
as this would cause a big performance loss. Instead it is usually sufficient to
use the CdiCtrls ContextControl
mechanism to just stop and restart the
respective CDI Contexts.
Such a base class could look roughly like the following:
import org.apache.deltaspike.cdise.api.CdiContainer;
import org.apache.deltaspike.cdise.api.CdiContainerLoader;
import org.apache.deltaspike.core.api.projectstage.ProjectStage;
import org.apache.deltaspike.core.util.ProjectStageProducer;
import org.apache.deltaspike.core.api.provider.BeanProvider;
public abstract class ContainerTest {
protected static volatile CdiContainer cdiContainer;
// nice to know, since testng executes tests in parallel.
protected static int containerRefCount = 0;
protected ProjectStage runInProjectStage() {
return ProjectStage.UnitTest;
}
/**
* Starts container
* @throws Exception in case of severe problem
*/
@BeforeMethod
public final void beforeMethod() throws Exception {
containerRefCount++;
if (cdiContainer == null) {
// setting up the Apache DeltaSpike ProjectStage
ProjectStage projectStage = runInProjectStage();
ProjectStageProducer.setProjectStage(projectStage);
cdiContainer = CdiContainerLoader.getCdiContainer();
cdiContainer.boot();
cdiContainer.getContextControl().startContexts();
}
else {
cleanInstances();
}
}
public static CdiContainer getCdiContainer() {
return cdiContainer;
}
/**
* This will fill all the InjectionPoints of the current test class for you
*/
@BeforeClass
public final void beforeClass() throws Exception {
beforeMethod();
// perform injection into the very own test class
BeanManager beanManager = cdiContainer.getBeanManager();
CreationalContext creationalContext = beanManager.createCreationalContext(null);
AnnotatedType annotatedType = beanManager.createAnnotatedType(this.getClass());
InjectionTarget injectionTarget = beanManager.createInjectionTarget(annotatedType);
injectionTarget.inject(this, creationalContext);
// this is a trick we use to have proper DB transactions when using the entitymanager-per-request pattern
cleanInstances();
cleanUpDb();
cleanInstances();
}
/**
* Shuts down container.
* @throws Exception in case of severe problem
*/
@AfterMethod
public final void afterMethod() throws Exception {
if (cdiContainer != null) {
cleanInstances();
containerRefCount--;
}
}
/**
* clean the NormalScoped contextual instances by stopping and restarting
* some contexts. You could also restart the ApplicationScoped context
* if you have some caches in your classes.
*/
public final void cleanInstances() throws Exception {
cdiContainer.getContextControl().stopContext(RequestScoped.class);
cdiContainer.getContextControl().startContext(RequestScoped.class);
cdiContainer.getContextControl().stopContext(SessionScoped.class);
cdiContainer.getContextControl().startContext(SessionScoped.class);
}
@AfterSuite
public synchronized void shutdownContainer() throws Exception {
if (cdiContainer != null) {
cdiContainer.shutdown();
cdiContainer = null;
}
}
public void finalize() throws Throwable {
shutdownContainer();
super.finalize();
}
/**
* Override this method for database clean up.
*
* @throws Exception in case of severe problem
*/
protected void cleanUpDb() throws Exception {
//Override in subclasses when needed
}
protected <T> T getInstance(Class<T> type, Qualifier... qualifiers) {
return BeanProvider.getContextualReference(type, qualifiers);
}
}
You can also plug in a cdictrl backend for Apache TomEE whenever you need to not only test CDI applications
but a full JavaEE application which has EJBs, managed DataSources, JTA, etc
The only thing you need to do is to replace your deltaspike-cdictrl-owb
dependency in your pom with
deltaspike-cdictrl-openejb
. Since Apache TomEE and Apache OpenEJB both contain OpenWebBeans as CDI container
you will get all the OWB functionality plus other JavaEE functionality.
You can pass DataSource configuration by simply providing a Properties
instance to
CdiContainer.boot(dbConfiguration)
in the beforeMethod method of the test class above:
public final void beforeMethod() throws Exception {
containerRefCount++;
if (cdiContainer == null) {
// setting up the Apache DeltaSpike ProjectStage
ProjectStage projectStage = runInProjectStage();
ProjectStageProducer.setProjectStage(projectStage);
cdiContainer = CdiContainerLoader.getCdiContainer();
Properties dbProperties = new Properties();
String dbvendor = ConfigResolver.getPropertyValue("dbvendor", "h2");
URL dbPropertiesUrl = getClass().getResource("/db/db-" + dbvendor + ".properties");
if (dbPropertiesUrl != null) {
InputStream is = dbPropertiesUrl.openStream();
try {
dbProperties.load(is);
}
finally {
is.close();
}
}
cdiContainer.boot(dbProperties);
}
else {
cleanInstances();
}
}
The db/db-mysql.properties
file for Apache OpenEJB (the former name of TomEE) would look like:
MYDS = new://Resource?type=DataSource
MYDS.JdbcDriver = org.h2.Driver
MYDS.JdbcUrl = jdbc:h2:file:/tmp/h2/myappdb
MYDS.JtaManaged = true
MYDS.UserName = sa
MYDS.Password =