Using Arquillian to test a possible bug in Weld
dan.j.allen Feb 17, 2010 8:01 PMI was recently notified of a possible bug in Weld. I decided to give Arquillian a try to prove whether or not the bug was real.
I'll start with the problem. A developer reported that Java EE resource injections were not being applied to a bean with at least one active CDI interceptor. The developer was invoking the bean from EL, so there was a chance this only happened when the bean was looked up by name. We'll cover all bases.
First, create a project using the minimal Java EE Weld archetype.
Next, let's define two managed beans, one without any interceptors (NativeBean) and one with an interceptor (InterceptedBean).
@Model public class NativeBean { @PersistenceContext(unitName="em") private EntityManager em; public EntityManager getEm() { return em; } } @Model public class InterceptedBean { @PersistenceContext(unitName = "em") private EntityManager em; @Inject private NativeBean nativeBean; @Transactional public EntityManager getEm() { return em; } @Transactional public EntityManager getEmFromNativeBean() { return nativeBean != null ? nativeBean.getEm() : null; } }
We'll also need an interceptor (TransactionIntereceptor) and an interceptor binding annotation (Transactional).
@Transactional @Interceptor public class TransactionInterceptor { @Inject private UserTransaction tx; @AroundInvoke public Object workInTransaction(InvocationContext invocation) throws Exception { try { tx.begin(); Object result = invocation.proceed(); tx.commit(); return result; } catch (Exception e) { tx.rollback(); throw e; } } } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) @InterceptorBinding @Documented @Inherited public @interface Transactional {}
Now we need to add Arquillian to our project. That involves the following steps:
1. Add the Arquillian JUnit or TestNG test-scoped dependency. We'll use TestNG:
<dependency> <groupId>org.jboss.arquillian</groupId> <artifactId>arquillian-testng</artifactId> <version>${arquillian.version}</version> <scope>test</scope> </dependency>
Until Arquillian is released, you'll have to check out the project and install it into your Maven repository so you have a version to depend on.
2. Add a profile for a Java EE container. We'll use JBoss AS 6. We should execute our tests against JBoss AS 6.0.0.M1 and 6.0.0.M2 (and GlassFish V3) to see if there was a recent fix in Weld that corrects the bug.
<profiles> <profile> <id>jbossas-remote-60</id> <dependencies> <dependency> <groupId>org.jboss.arquillian</groupId> <artifactId>arquillian-jboss-remote-60</artifactId> <version>${arquillian.version}</version> </dependency> </dependencies> </profile> </profiles>
3. Add jndi.properties to src/test/resources
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces java.naming.provider.url=jnp://localhost:1099
We're almost ready to write our test. But since we need to evaluate EL, we'll need to throw in some classes for bootstrapping EL in our test case. You can find details in the thread Evaluating EL expressions in a test case.
One thing to note, you'll need to be an the el-impl JAR in server/default/lib because the bootstrap code references a couple of implementation classes.
Okay, we are ready to write our test.
@Test(groups = "javaee") public class ResourceInjectionBugTest extends Arquillian { @Deployment public static Archive<?> createDeployment() { return Archives.create("test.jar", JavaArchive.class) .addPackage(Transactional.class.getPackage()) .addPackage(Expressions.class.getPackage()) .addManifestResource("test-beans.xml", ArchivePaths.create("beans.xml")) .addManifestResource("test-persistence.xml", ArchivePaths.create("persistence.xml")); } @Inject Instance<NativeBean> nativeBean; @Inject Instance<InterceptedBean> interceptedBean; @Inject Expressions expressions; @Test public void testResourceInjectionOnTypeSafeLookup() { assertNotNull(nativeBean.get().getEm()); assertNotNull(interceptedBean.get().getEm()); } @Test public void testResourceInjectionOnNameLookup() { assertNotNull(expressions.evaluateValueExpression("#{nativeBean.em}")); assertNotNull(expressions.evaluateValueExpression("#{interceptedBean.em}")); assertNotNull(expressions.evaluateValueExpression("#{interceptedBean.emFromNativeBean}")); } }
You'll notice that we are looking up our beans in both a type-safe and non-type-safe way and seeing if the EntityManager gets injected with and without the interceptor.
The interceptor is activated in the test-beans.xml file:
<beans> <interceptors> <class>org.jboss.weld.resourceinjectionbug.TransactionInterceptor</class> </interceptors> </beans>
For completeness, I'll include the contents of src/test/resources/test-persistence.xml:
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="em" transaction-type="JTA"> <!-- JBoss AS-specific datasource name --> <jta-data-source>java:/DefaultDS</jta-data-source> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> </properties> </persistence-unit> </persistence>
To run the test, first start the JBoss AS that you want to test on. Then, execute the test as follows:
mvn test -Pjbossas-remote-60
Try this out on different versions of JBoss AS (with different versions of Weld) and see when this bug shows up. From this exercise, I learned that this bug is present in Weld 1.0, but has been fixed in the Weld 1.0.1 series (Weld 1.0.1-CR1 is included in JBoss AS 6.0.0.M2).