1 Reply Latest reply: Feb 18, 2010 12:58 PM by Dan Allen RSS

Using Arquillian to test a possible bug in Weld

Dan Allen Master

I 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).

  • 1. Re: Using Arquillian to test a possible bug in Weld
    Dan Allen Master

    Another possible bug was reported in Weld (actually JBoss AS 6.0.0.M2, but Weld is involved) in this thread on seamframework.org. The developer reported that Weld is unable to locate no-interface EJB stateless session beans in JBoss AS 6.0.0.M2.

     

    Once again, we turn to Arquillian.

     

    Working with the setup we defined in the previous post, let's get right to the code and the test. First, we define two EJB stateless session beans, one with an interface (NoInterfaceSessionBean), and one without (FormalSessionBean):

     

    public @Stateless @Named class NoInterfaceSessionBean {
       public boolean isFound() {
          return true;
       }
    }
    
    @Local
    public interface FormalSessionBeanLocal {
       boolean isFound();
    }
    
    public @Stateless @Named class FormalSessionBean
          implements FormalSessionBeanLocal {
       @Override
       public boolean isFound() {
          return true;
       }
    }
    

     

    Next, we create a test case that attempts to invoke the two beans. We'll inject the beans to test into the test class.

     

    @Test(groups = "javaee")
    public class NoInterfaceEjbBugTest extends Arquillian {
       @Deployment
       public static Archive<?> createDeployment() {
          return Archives.create("test.jar", JavaArchive.class)
             .addPackage(NoInterfaceSessionBean.class.getPackage())
             .addPackage(Expressions.class.getPackage())
             .addManifestResource(new ByteArrayAsset(new byte[0]), ArchivePaths.create("beans.xml"));
       }
    
       @Inject Instance<FormalSessionBeanLocal> formalSessionBean;
       @Inject Instance<NoInterfaceSessionBean> noInterfaceSessionBean;
       @Inject Expressions expressions;
    
       @Test
       public void testSessionBeansOnTypeSafeLookup() {
          assertTrue(formalSessionBean.get().isFound());
          assertTrue(noInterfaceSessionBean.get().isFound());
       }
    
       @Test
       public void testSessionBeansOnNameLookup() {
          assertTrue(
             expressions.evaluateValueExpression("#{formalSessionBean.found}", Boolean.class));
          assertTrue(
             expressions.evaluateValueExpression("#{noInterfaceSessionBean.found}", Boolean.class));
       }
    }
    

     

    Now start up JBoss AS 6.0.0.M2 and run the test:

     

    mvn test -Pjbossas-remote-60 -Dtest=NoInterfaceEjbBugTest

     

    You should see that the test fails. Unfortuantely, due to a bug with serialization in Arquillian, the reason for the failure is not being communicated back to the test runner. If you comment out the no-interface session bean calls, you'll see that the tests pass.

     

    So indeed, this is a real bug!