Title: OpenWebBeans CdiCtrl
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
.
https://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.
# Testing your application with Apache DeltaSpike CdiCtrl
## About CdiCtrl
``CdiCtrl`` is *not* part of Apache OpenWebBeans but a module of
[Apache DeltaSpike][1].
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][2].
## Adding OpenWebBeans CdiCtrl to your project
The following are the dependencies you need in your Apache Maven pom.xml file in addition to
OWB itself:
org.apache.deltaspike.cdictrl
deltaspike-cdictrl-api
${deltaspike.version}
test
org.apache.deltaspike.cdictrl
deltaspike-cdictrl-owb
${deltaspike.version}
test
## Why use CdiCtrl for your unit tests?
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`` 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:
:::java
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 getInstance(Class type, Qualifier... qualifiers) {
return BeanProvider.getContextualReference(type, qualifiers);
}
}
## Testing JavaEE applications
You can also plug in a cdictrl backend for [Apache TomEE][2] 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:
:::java
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 =
[1]: https://deltaspike.apache.org
[2]: https://tomee.apache.org