20.10.2013
Tests with Arquillian, Tomcat and Dependency Injection
This short article describes how to setup the environment to test CDI (Dependency Injection) with Arquillian and Maven in an embedded Tomcat container.
The great article Arqullian - Getting Started describes how to setup the environment for an embedded Glasfish, a managed JBoss and an embedded Weld. But the description for Tomcat is missing.
It takes me a while until my environment was up and running, so I decided to write this short article. The setup is not very complicated, but you have to add some lines and commands at the correct position. :-)
This article assumes the you have the Greeter
example of the article Arqullian - Getting Started in your workspace.
Extend the pom.xml
First of all you have to add a new Profile
for the embedded Tomcat in the pom.xml
.
<!-- Tomcat Embedded --> <profile> <id>arquillian-tomcat-embedded</id> <activation> <activeByDefault>true</activeByDefault> </activation> <dependencies> <dependency> <groupId>org.jboss.arquillian.container</groupId> <artifactId>arquillian-tomcat-embedded-7</artifactId> <version>1.0.0.CR5</version> <scope>test</scope> </dependency> <!-- org.apache.tomcat --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>7.0.42</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>7.0.42</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-logging-juli</artifactId> <version>7.0.42</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.eclipse.jdt.core.compiler</groupId> <artifactId>ecj</artifactId> <version>3.7</version> <scope>provided</scope> </dependency> <!-- Weld for Dependency Injection @Inject --> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet</artifactId> <version>1.1.9.Final</version> </dependency> <dependency> <groupId>org.jboss.shrinkwrap.resolver</groupId> <artifactId>shrinkwrap-resolver-impl-maven</artifactId> <scope>test</scope> </dependency> </dependencies> </profile>
Line 8 to 13 adds the embedded container for the Tomcat 7 and the lines 16 to 39 are needed for the Tomcat runtime. (see Tomcat 7.0 - Embedded)
Important are the lines 42-46. There you add the Weld-Servlet which is needed to setup the CDI for testing.
The ShrinkWrap-Resolver
in line 48-52 is needed to add some dependencies in the class GreeterTest
.
Create a arquillian.xml file
Now you have to create a configuration file for Arquillian (arquillian.xml)
.
Hint
Contrary to the article Tomcat 7.0 - Embedded I didn't need the file arquillian.xml
with the unpackArchive
property. I guess the newer version of Weld is able to find the classes of the CDI althought they a packed in the war file.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://jboss.org/schema/arquillian" xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> <container qualifier="tomcat" default="true"> <configuration> <property name="tomcatHome">target/tomcat-embedded-7</property> <property name="workDir">work</property> <property name="bindHttpPort">8888</property> <property name="unpackArchive">true</property> <property name="serverName">arquillian-tomcat-embedded-7</property> </configuration> </container> </arquillian>
Important is line 11 where you tell Tomcat to unzip the war files so Weld is able to find the classes. (see Tomcat 7.0 - Embedded)
Create a web.xml
In order to initialize the Weld servlet a web.xml file is needed.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <listener> <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class> </listener> </web-app>
You only have to register the listener for the Weld environment.
Change the GreeterTest class
Next you have to add some lines in the class GreeterTest
.
package org.arquillian.example; import java.io.File; import javax.inject.Inject; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.junit.Arquillian; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.EmptyAsset; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(Arquillian.class) public class GreeterTest { @Inject Greeter greeter; @Deployment public static WebArchive createDeployment() { File[] lib = Maven.resolver() .resolve("org.jboss.weld.servlet:weld-servlet:1.1.9.Final") .withTransitivity().as(File.class); WebArchive jar = ShrinkWrap.create(WebArchive.class) .addClass(Greeter.class) .addClass(PhraseBuilder.class) .addAsManifestResource("arquillian.xml") .addAsLibraries(lib) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .setWebXML("web.xml") ; System.out.println(jar.toString(true)); return jar; } @Test public void should_create_greeting() { Assert.assertEquals("Hello, Earthling!", greeter.createGreeting("Earthling")); greeter.greet(System.out, "Earthling"); } }
The lines 25 to 27 are used to resolve the dependencies for Weld and store them in a File
array. The libraries in the array are added in line 33 to the war file. In line 32 you add the (see arquillian.xml). Also you have to add the arquillian.xml
web.xml
file in line 35.
That's all. If you start the GreeterTest
now, the embedded Tomcat is started and the injection of the @Inject Greeter greeter;
is working.
Further informations about ShrinkWrap and the Maven Resolver can be found here: How to I add Maven artifacts to my ShrinkWrap archives?.
Arquillian test example for the arquillian-container-tomcat
On GitHub you can find an example with a configuration for the embedded Tomcat container. This example uses the org.jboss.shrinkwrap.resolver.api.DependencyResolvers
and the org.jboss.shrinkwrap.resolver.api.maven.MavenDependencyResolver
to resolve the dependencies for the Arquillian tests. (TomcatEmbeddedInContainerTestCase.java
)
The problem is that the two classes are moved during a refactoring. :-) In the version 1.0.0 they are still available. In newer versions they are gone and you have to use another way to resolve the dependencies. How to I add Maven artifacts to my ShrinkWrap archives?
Some expections
In this section I summarized some exceptions I got when I tried to initialize the embedded Tomcat container. Perhaps these StackTraces help others to find their errors faster.
To fix the following exception, you have to add some lines in the pom.xml
so that the Tomcat runtime is found. See the lines 15-39 in section Extend the pom.xml
Caused by: java.lang.NoClassDefFoundError: org/apache/catalina/Host at java.lang.Class.getDeclaredConstructors0(Native Method) at java.lang.Class.privateGetDeclaredConstructors(Class.java:2483) at java.lang.Class.getConstructor0(Class.java:2793) at java.lang.Class.getDeclaredConstructor(Class.java:2043) at org.jboss.arquillian.core.impl.loadable.SecurityActions$1.run(SecurityActions.java:182) at org.jboss.arquillian.core.impl.loadable.SecurityActions$1.run(SecurityActions.java:179) at java.security.AccessController.doPrivileged(Native Method) at org.jboss.arquillian.core.impl.loadable.SecurityActions.getConstructor(SecurityActions.java:178) at org.jboss.arquillian.core.impl.loadable.SecurityActions.newInstance(SecurityActions.java:152) at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.createServiceInstance(ServiceRegistryLoader.java:103) at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.all(ServiceRegistryLoader.java:55) at org.jboss.arquillian.core.impl.loadable.ServiceRegistryLoader.onlyOne(ServiceRegistryLoader.java:67) at org.jboss.arquillian.container.impl.client.container.ContainerRegistryCreator.createRegistry(ContainerRegistryCreator.java:97) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115) at org.jboss.arquillian.core.impl.ManagerImpl.bindAndFire(ManagerImpl.java:236) at org.jboss.arquillian.core.impl.InstanceImpl.set(InstanceImpl.java:74) at org.jboss.arquillian.config.impl.extension.ConfigurationRegistrar.loadConfiguration(ConfigurationRegistrar.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115) at org.jboss.arquillian.core.impl.ManagerImpl.start(ManagerImpl.java:261) at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.(EventTestRunnerAdaptor.java:56) ... 15 more Caused by: java.lang.ClassNotFoundException: org.apache.catalina.Host at java.net.URLClassLoader$1.run(URLClassLoader.java:366) at java.net.URLClassLoader$1.run(URLClassLoader.java:355) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:354) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 51 more
If you got a NullPointerException when running the JUnit test the CDI is not initialized. I describe how to solve it above.
java.lang.NullPointerException at org.arquillian.example.GreeterTest.should_create_greeting(GreeterTest.java:32) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.jboss.arquillian.junit.Arquillian$6$1.invoke(Arquillian.java:270) at org.jboss.arquillian.container.test.impl.execution.LocalTestExecuter.execute(LocalTestExecuter.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:115) at org.jboss.arquillian.core.impl.EventImpl.fire(EventImpl.java:67) at org.jboss.arquillian.container.test.impl.execution.ContainerTestExecuter.execute(ContainerTestExecuter.java:38) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.invokeObservers(EventContextImpl.java:99) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:81) at org.jboss.arquillian.test.impl.TestContextHandler.createTestContext(TestContextHandler.java:89) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88) at org.jboss.arquillian.test.impl.TestContextHandler.createClassContext(TestContextHandler.java:75) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88) at org.jboss.arquillian.test.impl.TestContextHandler.createSuiteContext(TestContextHandler.java:60) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.jboss.arquillian.core.impl.ObserverImpl.invoke(ObserverImpl.java:94) at org.jboss.arquillian.core.impl.EventContextImpl.proceed(EventContextImpl.java:88) at org.jboss.arquillian.core.impl.ManagerImpl.fire(ManagerImpl.java:135) at org.jboss.arquillian.test.impl.EventTestRunnerAdaptor.test(EventTestRunnerAdaptor.java:111) at org.jboss.arquillian.junit.Arquillian$6.evaluate(Arquillian.java:263) at org.jboss.arquillian.junit.Arquillian$4.evaluate(Arquillian.java:226) at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314) at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46) at org.jboss.arquillian.junit.Arquillian$5.evaluate(Arquillian.java:240) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.jboss.arquillian.junit.Arquillian$2.evaluate(Arquillian.java:185) at org.jboss.arquillian.junit.Arquillian.multiExecute(Arquillian.java:314) at org.jboss.arquillian.junit.Arquillian.access$100(Arquillian.java:46) at org.jboss.arquillian.junit.Arquillian$3.evaluate(Arquillian.java:199) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.jboss.arquillian.junit.Arquillian.run(Arquillian.java:147) at org.junit.runner.JUnitCore.run(JUnitCore.java:157) at org.junit.runner.JUnitCore.run(JUnitCore.java:136) at org.jboss.arquillian.junit.container.JUnitTestRunner.execute(JUnitTestRunner.java:65) at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.executeTest(ServletTestRunner.java:160) at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.execute(ServletTestRunner.java:126) at org.jboss.arquillian.protocol.servlet.runner.ServletTestRunner.doGet(ServletTestRunner.java:90) at javax.servlet.http.HttpServlet.service(HttpServlet.java:734) at javax.servlet.http.HttpServlet.service(HttpServlet.java:847) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1002) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:724)
The following entries in the logfile are a hint that Weld is not initialized properly and the file web.xml
is missing.
INFO: No global web.xml found Okt 20, 2013 6:48:30 PM org.jboss.arquillian.testenricher.cdi.container.BeanManagerProducer lookup INFO: BeanManager not found.