Tuesday, December 18, 2012

Tutorial EJB3 Integration Test with Arquillian part3 - JBoss 5 managed container

Document  Version 1.0

In the part 1-2 of the "Tutorial EJB3 Integration Test with Arquillian" we successfully tested EJB with Arquillian, deployed on JBoss 7 managed and remote container.

But there are still cases, where upgrade to JBoss 7 is not an  option, for whateve resons, therefore Arquillian test need to be done in older version of JBoss containers. Compare to the Arquillian test with  JBoss 7, Arquillian test with older JBoss  versionis are not so good documented.

In the part 3-5 of this serial  I would try to to provide an "one stop" guide to setting up Arquillian with JBoss 5 container.

Arquillian part3 - JBoss 5 managed container

1. Create a simple Maven project in Eclipse

Follow the section 1 of "EJB3 Integration Test with Arquillian part1-JBoss 7 managed container" to create a new Eclipse project "testarq_jb5_managed". Be sure in step2, to name the ArtifactId as "testarq_jb5_managed".

2. Create project's pom.xml file

As you may remember, for Arquillian with JBoss 7, there is a dependency in pom.xml file called "arquillian-bom". We used this bom dependency to resolve all the Aquillian and JBoss artifacts. But unfortunately for JBoss 5, I could not find such a bom dependency, so all the dependencies in this pom.xml need to be configured "manually". 

Create a "pom.xml" file in the "testarq_jb5_managed" project, which we created in section 1. Replace the content of "pom.xml" will the content below:


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>testarg</groupId>
<artifactId>testarq_jb5_1_managed</artifactId>
<version>0.0.1</version>
<description>test arr</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 1.0.3.Final 1.0.0.Alpha5 -->
<version.arquillian_core>1.0.3.Final</version.arquillian_core>
<version.org.jboss.jbossas>5.1.0.GA</version.org.jboss.jbossas>
<version.org.jboss.jbossas_server-manager>1.0.3.GA</version.org.jboss.jbossas_server-manager>
<version.ejb3_api>3.1.0</version.ejb3_api>
</properties>
<repositories>
<!-- Use this repository you could find all jars for jb5. it compiles. 
Test runs with error.. -->
<repository>
<id>maven-us.nuxeo.org</id>
<url>https://maven-us.nuxeo.org/nexus/content/repositories/public</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
</plugin>
</plugins>
</build>
<dependencies>
</dependencies>
<profiles>
<profile>
<id>arquillian-jbossas-managed_5_1</id>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-jbossas-managed-5.1</artifactId>
<version>1.0.0.CR3</version> 
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.jbossas</groupId>
<artifactId>jboss-server-manager</artifactId>
<version>1.0.3.GA</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.jbossas</groupId>
<artifactId>jboss-as-client</artifactId>
<version>5.1.0.GA</version>
<type>pom</type>
<scope>provided</scope>
<!-- it is the javassist:javassist:jar:3.4.GA: , causing method not 
found exception. But exclude this lib caused class not found exception so 
now first exclude this, and then try introducing a correct javasist lib -->
<exclusions>
<exclusion>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.9.0.GA</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<version>${version.arquillian_core}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.container</groupId>
<artifactId>arquillian-container-spi</artifactId>
<version>${version.arquillian_core}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>trove</groupId>
<artifactId>trove</artifactId>
<version>1.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</profile>
</profiles>
</project>


 ! Instead of using the default "maven central repository", we have to switch repository to "maven-us.nuxeo.org", as configured in the repositories section of the above "pom.xml" file.
Maven could not find some dependencies in "central maven repository" ( maybe they were removed, or not hosted there at all °_°).

3. Implement source code and test code

As code sample we will take a typical use case in JEE application:
A business layer/class uses a DAO (data access object) to access business data. Here business class and DAO class are both stateless session bean.

To implement and test the above use case, we need to create following classes in the "testarq_jb5_managed" project:
2 for bussiness classes:
GrussGottEjbLocal.java
GrussGottEjb.java

2 for DAO classes:
HalloDaoLocal.java
HalloDao.java

and the test class:
TestGrussGott.java

Now the "testarq_jb5_managed" project in Eclipse will looks like:



Replace the content of java files with following contents:



HalloDaoLocal.java
package test.ejb;

import javax.ejb.Local;
@Local
public interface HalloDaoLocal {
public String getText();
}




HalloDao.java
package test.ejb;

import javax.ejb.Stateless;
@Stateless
public class HalloDao implements HalloDaoLocal {
@Override
public String getText() {
return "!";
}
}


"HalloDao" is still not realy a DAO, it is not equipted  with any persistence or database utilities.
Persistence with JPA / Hibernate will be shown in the part 4 of this serial. For now, we just want to simulate calling a session bean in an other session bean, therefore, no accessing database is really necessary.  


GrussGottEjbLocal.java
package test.ejb;

import javax.ejb.Local;
@Local
public interface GrussGottEjbLocal {
public String sayHello(String name);
public String sayHelloTxt(String name);
}


GrussGottEjb.java
package test.ejb;

import javax.ejb.EJB;
import javax.ejb.Stateless;
@Stateless
public class GrussGottEjb implements GrussGottEjbLocal {
@EJB
HalloDaoLocal halloDao;
@Override
public String sayHello(String name) {
return "Hallo "  + name;
}
@Override
public String sayHelloTxt(String name) {
String txt = halloDao.getText();
return "Hallo " + name + txt;
}
}


TestGrussGott.java
package test.ejb;

import javax.ejb.EJB;
import junit.framework.Assert;
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.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * @author he
 * 
 */
@RunWith(Arquillian.class)
public class TestGrussGott {
@Deployment
public static JavaArchive createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class, "hgrussEjb.jar");
jar.addClass(GrussGottEjbLocal.class).addClass(GrussGottEjb.class);
jar.addClass(HalloDao.class).addClass(HalloDaoLocal.class);
//jar.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
System.out.println(jar.toString(true));

return jar;
}
@EJB
GrussGottEjbLocal grusEjb;

@Test
public void sayHallo() {
String result = grusEjb.sayHello("hehe");
Assert.assertEquals("Hallo hehe", result);
}
@Test
public void sayHalloTxt() {
String result = grusEjb.sayHelloTxt("hehe");
Assert.assertEquals("Hallo hehe!", result);
}
}

! In the "TestGrussGott.java"  the line for "beans.xml" is commented out. This line is put here only to show, that unlike Arquillian test with JBoss 7 container, for Arquillian test with JBoss 5 container, the "beans.xml" is not needed.  

4. Download JBoss "jboss-5.1.0.GA"

Dowload the JBoss server  from www.jboss.org, and extract it to your favorite location. 
On my machine , I extracted jboss to:


/home/he/jboss-5.1.0.GA

As usual, we need to set up PATH and JAVA_HOME system variables for jboss. When you have followed the part 1-2 of this turorial serial, where JBoss 7 is used, you mus have PATH and JAVA_HOME set to your jdk7 installation. This should be changed to a jdk6 location. 
Following is the setting on my machine: 


>  export JAVA_HOME=/usr/java/jdk1.6.0_32/
>  export PATH=/usr/java/jdk1.6.0_32/bin:$PATH

5. arquillian.xml

We still need to tell Arquillian where to find JBosss installation, and which JBoss instance is to be managed by Arquillian.
Create an "arquillian.xml" in the folder"src/test/resources" with the content below:


<?xml version="1.0" encoding="UTF-8"?>
<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="jboss5" default="true">
<configuration>
<property name="jbossHome">path_2_your_jboss/jboss-5.1.0.GA</property>
<property name="profileName">default</property>
</configuration>
</container>
</arquillian>


6. Run the tests

We have learned how to run the Maven test in Eclipse. Now we will try runing the tests using command line.

6.1 Set system properties "PATH" and "JAVA_HOME"

Make sure you have correctly set "PATH" and "JAVA_HOME". In case of linux you could check this using following command:


> which java
/usr/java/jdk1.6.0_32/bin/java


> echo $JAVA_HOME
/usr/java/jdk1.6.0_32/

Command to set the properties:

  export JAVA_HOME=/usr/java/jdk1.6.0_32/
  export PATH=/usr/java/jdk1.6.0_32/bin:$PATH


6.2 Run tests

Run Maven tests using following command:

>  mvn clean test -Parquillian-jbossas-managed_5_1

When you have followed all the steps till now, with no mistakes, the test results will be :


Test Failed!
To find out more details about the test failure, we will have a close look at the maven/surefile test report,  this is the "test.ejb.TestGrussGott.txt" in /target/surefile-reports/:



...
-------------------------------------------------------------------------------
Test set: test.ejb.TestGrussGott
-------------------------------------------------------------------------------
Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 124.531 sec <<< FAILURE!
sayHallo(test.ejb.TestGrussGott)  Time elapsed: 1.442 sec  <<< ERROR!
java.lang.NullPointerException
at test.ejb.TestGrussGott.sayHallo(TestGrussGott.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
....


The Exception above says that in the "TestGrussGott,java", we have a problem on the line :

...
@Test
public void sayHallo() {
String result = grusEjb.sayHello("hehe");
Assert.assertEquals("Hallo hehe", result);
}
...

Here we have a NullPointerException for the object "grusEjb", i.e. "grusEjb" is null. This objected was injected in test class using @EJB annotation:

@EJB
GrussGottEjbLocal grusEjb;

We remember in the test cases using JBoss 7, we have always been doing like this, why this time failed?

Actually, our problem lies really in this place, in the @EJB annotation! 
To put it short:
Arquillian can not find the "GrussGottEjb" using only @EJB, althrough the bean is deployed in JBoss.

7. Finding the EJB with annotation

When only @EJB is used, Arquillian tries to look up the "GrussGottEjb" session bean using following JNDI (could be seen in JBoss server.log):


....
FINE  [org.jboss.arquillian.test.spi.TestEnricher] 
(http-localhost%2F127.0.0.1-8080-2) Could not lookup @javax.ejb.EJB(beanName=, mappedName=, beanInterface=class java.lang.Object, description=, name=), 
other Enrichers might, move on. Exception: No EJB found in JNDI, 
tried the following names: java:global/test.ear/test/GrussGottEjbLocalBean,
 java:global/test.ear/test/GrussGottEjbLocal, 
java:global/test/GrussGottEjbLocal, 
java:global/test/GrussGottEjbLocalBean, 
java:global/test/GrussGottEjbLocal/no-interface, 
test/GrussGottEjbLocalBean/local, 
test/GrussGottEjbLocalBean/remote, 
test/GrussGottEjbLocal/no-interface, 
GrussGottEjbLocalBean/local,
 GrussGottEjbLocalBean/remote, 
GrussGottEjbLocal/no-interface,
 ejblocal:test.ejb.GrussGottEjbLocal, 
test.ejb.GrussGottEjbLocal,
....


But, when the "hgrussEjb.jar" is deployed by Arquillian in JBoss, it is bund to the following JNDI as shown in JBoss "server.log":

...

jndi:test/GrussGottEjb/local-test.ejb.GrussGottEjbLocal

jndi:test/GrussGottEjb/local

jndi:test/GrussGottEjb/remote
...

Now problem is clear: using @EJB only is not enough! 
The solution is actually easy.  In the "TestGrussGott,java", just change the annotation  from: 


@EJB

GrussGottEjbLocal grusEjb;

to :

 @EJB(mappedName="test/GrussGottEjb/local")


GrussGottEjbLocal grusEjb;

8. Run test again




 Now run the test again:

>  mvn clean test -Parquillian-jbossas-managed_5_1



And you would see in the console:








9. Troubleshooting


9.1 Exception


....
Failed to collect dependencies for....
...
Could not transfer artifact org.jboss.aop:jboss-aop-aspects:pom:2.1.1.GA from/to repository.jboss.org (http://repository.jboss.org/maven2): 
Access denied to: http://repository.jboss.org/maven2/org/jboss/aop/jboss-aop-aspects/2.1.1.GA/jboss-aop-aspects-2.1.1.GA.pom, 
...


This indicates that dependencies could not be downloaded from Maven central repository. Check your pom.xml file in section 2 of this turorial, make sure you have configured /customized Maven repository (for example "maven-us.nuxeo.org"" ).

9.2 When you observer error information like:

Error in Console:
Tests in error: 
  test.ejb.TestGrussGott: Could not start remote container

Error in "test.ejb.TestGrussGott.tx":
org.jboss.arquillian.container.spi.client.container.LifecycleException: Could not start remote container
...
Caused by: java.io.IOException: Server failed to start; see logs. exit code: 0
...


And Error in JBoss "server.log"
 Failed to get property value for bean: org.jboss.web.tomcat.service.management.ConnectorBean, property: bytesSent
java.lang.IllegalStateException: java.lang.RuntimeException: Error creating MBeanProxy: jboss.web:name=http-localhost-8080,type=GlobalRequestProcessor

...
Caused by: java.lang.RuntimeException: Error creating MBeanProxy: jboss.web:name=http-localhost-8080,type=GlobalRequestProcessor


The reason for the above errors is very likely running JBoss with the wrong Java version. So please make sure to run JBoss with jdk6, anot NOT jdk7. Check PATH and JAVA_HOME of your system again.



1 comment: