Thursday, March 22, 2012

EJB testing with JUnit + OpenEJB + JPA 2.x

One disadvantage of using EJB technology in our application is that they need a container where they can be deployed. Typically you would lose too much time testing them because of the application server; It has to be running so that the EJBs are deployed, and if you have trouble and have to fix some code then you have to redeploy those EJBs just to do some tests... Well, you can use an embedded container in your tests where your EJBs can be automatically deployed and do some tests, everything without the need of an expensive server running and thus, saving a lot of time!

OpenEJB is a lightweight EJB container which can be used to perform EJB testing. In this example we deploy an EJB that saves some data in a table.
In default mode, OpenEJB automatically uses OpenJPA as the JPA implementation. We are going to change it and use Hibernate instead.

By the time I write this post, OpenEJB 3.2.0-SNAPSHOT is the current version that supports JPA2, which is the one that's going to be used in the example. Previous versions of OpenEJB like 3.1.4 only support JPA1, so, be careful when if you come into strange and undescriptive errors and you don't know what may be going on...


So, first of all we need the following jar files:

pom.xml
<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>ar.com.openejb</groupId>
 <artifactId>openejb3-example</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>openejb3-example</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>
 
 <repositories>
  <repository>
   <id>JBoss Developer</id>
   <url>https://repository.jboss.org/nexus/content/groups/developer/</url>
  </repository>
 </repositories>

 <dependencies>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.6.9.Final</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.2</version>
   <scope>test</scope>
  </dependency>

  <!-- Here we use PostgreSQL, you may use another database -->
  <dependency>
   <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>9.1-901.jdbc4</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.apache.openejb</groupId>
   <artifactId>openejb-core</artifactId>
   <version>3.2.0-SNAPSHOT</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.jboss.ejb3</groupId>
   <artifactId>jboss-ejb3-api</artifactId>
   <version>3.1.0</version>
   <scope>test</scope>
  </dependency>

  <!-- This one is to let OpenEJB log stuff -->
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-jcl</artifactId>
   <version>1.6.4</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

The java.naming.factory.initial is the minimum variable requirement to start the container. Here, it is set via jndi.properties file, just make sure it is available in the classpath.

jndi.properties
#This is the minimun requirement to lauch OpenEJB
java.naming.factory.initial=org.apache.openejb.client.LocalInitialContextFactory

#We want OpenEJB to log everything in debug mode
log4j.category.OpenEJB.options = debug
log4j.category.OpenEJB.startup = debug
log4j.category.OpenEJB.startup.config = debug

#We tell OpenEJB to scan the whole classpath
openejb.deployments.classpath.include=".*"
openejb.deployments.classpath.exclude=""

#Definition of the default datasource
myDS=new://Resource?type=DataSource
myDS.JdbcDriver=org.postgresql.Driver
myDS.JdbcUrl=jdbc:postgresql://localhost:5432/openejb3-example
myDS.JtaManaged=true
myDS.DefaultAutoCommit=true
myDS.UserName=postgres
myDS.Password=123456

The service we have created where we inject an entity manager:

PersonServiceBean.java
@Stateless
public class PersonServiceBean implements PersonService {
 
 @PersistenceContext(unitName="openejb3-example")
 private EntityManager entityManager;

 @Override
 public Integer save(Person person) throws Exception {
  entityManager.persist(person);
  
  return person.getId();
 }
}

Now we can start the context with InitialContext() which will read our jndi.properties file and start automatically the OpenEJB container and allow us to lookup services.

PersonServiceTest.java
private PersonService service;

 @Before
 public void init() throws NamingException {
  InitialContext ctx = new InitialContext();
  service = (PersonService) ctx.lookup("PersonServiceBeanLocal");
 }
 
 @Test
 public void saveTest() throws Exception {
  Person person = new Person();
  person.setName("Rudolph");
  
  Integer id = service.save(person);
  
  System.out.println("The person id is " + id);
 }

This is the test persistence file:

persistence.xml
<persistence version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence" xsi:schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
 <persistence-unit name="openejb3-example" transaction-type="JTA">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <class>ar.com.model.Person</class>
  <properties>
   <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
   <property name="hibernate.hbm2ddl.auto" value="update" />
   <property name="hibernate.show_sql" value="true" />
  </properties>
 </persistence-unit>
</persistence>

2 comments:

  1. Hi,

    just two few notes:

    1) with openejb > 4 there is an openejb-core-hibernate which replaces openjpa by hibernate so no more need to bring hibernate manually in your pom.

    2) the org.apache.openejb:javaee-api jar is easier to use than bringing back all API jars.

    Thanks for your post!

    ReplyDelete
  2. Thanks for the notes, I will take a look at that version. Also, the Arquillian-OpenEJB combo seems even easier to use, will take a look at it too.

    ReplyDelete