Are you playing around with the shiny new 3.1 EJB API?
Using Maven for your Java projects?
Need an easy way to write and execute tests for your EJBs that depends on an Java Application Server?
No problem using Maven Archetypes, the Maven EJB Plugin and the GlassFish embedded Application Container..
Prerequisites
For the following tutorial we’re going to need an installation of Maven and of course – the Java Development Kit!
-
Maven >=2
-
A text editor or IDE of choice
Project Setup
We want to make our life as easy as possible and there is an interesting archetype for ejb-components available: org.codehaus.mojo.archetypes:ejb-javaee6 ..
-
At first we’re creating a new project using Maven Archetypes (choose “ejb-javaee6” and EJB version 1.3 !!) using your IDE with a Maven plugin installed or via console like this
mvn archetype:generate [INFO] Generating project in Interactive mode [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0) Choose archetype: [..] 242: remote -> ejb-javaee6 (Archetype for an EJB package using Java EE 6.) [..] Choose a number: 98: 242 Choose version: 1: 1.0 2: 1.0.1 3: 1.0.2 4: 1.1 5: 1.2 6: 1.3 Choose a number: 6: 6 [.. etc ..]
-
Now we just need to add the dependency for the embedded GlassFish server and the Java repository to our pom.xml. It is important to add the dependency for glassfish-embedded-all as the first dependency to the list (→ Troubleshooting)!
-
Finally our pom.xml should look like this one
<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>com.hascode.tutorial</groupId> <artifactId>ejb-tutorial</artifactId> <version>0.0.1</version> <packaging>ejb</packaging> <name>hasCode.com ejb-tutorial EJB</name> <properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.glassfish.extras</groupId> <artifactId>glassfish-embedded-all</artifactId> <version>3.0</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>6.0</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-ejb-plugin</artifactId> <version>2.3</version> <configuration> <ejbVersion>3.1</ejbVersion> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.1</version> <executions> <execution> <phase>validate</phase> <goals> <goal>copy</goal> </goals> <configuration> <outputDirectory>${endorsed.dir}</outputDirectory> <silent>true</silent> <artifactItems> <artifactItem> <groupId>javax</groupId> <artifactId>javaee-endorsed-api</artifactId> <version>6.0</version> <type>jar</type> </artifactItem> </artifactItems> </configuration> </execution> </executions> </plugin> </plugins> <finalName>ejb-tutorial</finalName> </build> <repositories> <repository> <id>maven2-repository.dev.java.net</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/2/</url> <layout>default</layout> </repository> <repository> <id>maven2-repository.dev.java.net.glassfish</id> <name>Java.net Repository for Maven</name> <url>http://download.java.net/maven/glassfish/</url> </repository> </repositories> </project>
-
Create a dumb JavaBean named UserBean in the package com.hascode.tutorial.ejb_tutorial.bean
package com.hascode.tutorial.ejb_tutorial.bean; public class UserBean { private Long id; private String firstName; private String lastName; // getter, setter ommitted.. }
-
Create the EJB to deliver a list of all available UserBeans named UserEJB .. a simple @Stateless annotation is enough to make this bean a stateless Enterprise Java Bean ..
package com.hascode.tutorial.ejb_tutorial.bean; import java.util.ArrayList; import java.util.List; import javax.ejb.Stateless; @Stateless public class UserEJB { public List<UserBean> findAll() { List<UserBean> users = new ArrayList<UserBean>(); UserBean user = new UserBean(); user.setFirstName("Alfred"); user.setLastName("Newman"); user.setId(1l); UserBean anotherUser = new UserBean(); anotherUser.setFirstName("Albert"); anotherUser.setLastName("Einstein"); anotherUser.setId(2l); users.add(user); users.add(anotherUser); return users; } }
-
That is all we need for now .. now let’s do some testing …
Testing in the embedded EJB Container
Now that we’ve got all our classes together we should test their functions and the wiring in the EJB container. This is where the new embeddable EJB Container API saves our lives.
-
We’re writing a new test case named UserEJBTest in src/test/resources
package com.hascode.tutorial.ejb_tutorial.bean; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.util.List; import javax.ejb.embeddable.EJBContainer; import javax.naming.Context; import javax.naming.NamingException; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; public class UserEJBTest { private static EJBContainer ejbContainer; private static Context ctx; @BeforeClass public static void setUp() { ejbContainer = EJBContainer.createEJBContainer(); ctx = ejbContainer.getContext(); } @AfterClass public static void tearDown() { ejbContainer.close(); } @Test public void testFindAll() { try { UserEJB userEJB = (UserEJB) ctx.lookup("java:global/classes/UserEJB!com.hascode.tutorial.ejb_tutorial.bean.UserEJB"); assertNotNull(userEJB); List<UserBean> users = userEJB.findAll(); assertNotNull(users); assertEquals(2, users.size()); } catch (NamingException e) { throw new AssertionError(e); } } }
-
So what’s happening here? First we’re starting the embedded EJB Container, lookup the UserEJB via JNDI and assert that the user information we were looking for is present via the EJB.
-
If you’re not sure what the corresponding JDNI name for your bean is, take a look at the console output on the EJB container startup .. when the UserEJB is registered the following helpful information shows you available JNDI names
INFO: Portable JNDI names for EJB UserEJB : [java:global/classes/UserEJB, java:global/classes/UserEJB!com.hascode.tutorial.ejb_tutorial.bean.UserEJB]
-
The test should run without errors
Figure 1. Running the test in Eclipse
Download Sources
Troubleshooting
-
“java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/ejb/embeddable/EJBContainer“ – I got this error when glassfish-embedded-all was not the first dependency in the dependencies list in my pom.xml – changing the order helped ..
-
“java.lang.AssertionError: javax.naming.NamingException: Lookup failed for ‘java:global/classes/UserEJB!com.hascode.tutorial.ejb_tutorial.bean.UserEJB’ in SerialContext [Root exception is javax.naming.NameNotFoundException: classes]“ – To fix this error, initialize the EJBContainer instance in the unit test like this (thanks to Ian Smith for mentioning)
Map properties = new HashMap(); properties.put(EJBContainer.MODULES, new File("target/classes")); ejbContainer = EJBContainer.createEJBContainer(properties);
Alternative: Arquillian
Meanwhile, I’ve switched to using Arquillian for testing Java EE applications. If you’re interested, please feel free to have a look at the following tutorials of mine:
-
Arquillian Tutorial: Writing Java EE 6 Integration Tests and more..
-
Arquillian Transaction Extension: Transaction Rollback for your Java EE Integration Tests
-
Java EE: Setting up and Testing Form-Based JDBC Authentication with Arquillian and Maven
-
Marrying Java EE and BDD with Cucumber, Arquillian and Cukespace
Resources
Article Updates
-
2015-03-21: Links to Arquillian articles added.