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

EJB 3.1 Embedded Application Server Components

Prerequisites

For the following tutorial we’re going to need an installation of Maven and of course – the Java Development Kit!

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

    ejb31 test 300x93
    Figure 1. Running the test in Eclipse

Download Sources

I have put the sources for the examples here on GitHub .. you may download it there or check it out using

git clone http://github.com/hascode/ejb-3.1-embedded-tutorial.git

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);

Article Updates

  • 2015-03-21: Links to Arquillian articles added.