1 Reply Latest reply on Feb 18, 2010 12:58 PM by dan.j.allen

    Using Arquillian to test a possible bug in Weld

    dan.j.allen

      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.j.allen

          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!