Friday, October 30, 2009

Unit Testing EJBs and JPA with Embeddable GlassFish

Java EE 6 has been a topic here at CTP for quite a while now. During summer I had the pleasure of conducting an internship which was targeted to explore the possibilities of the upcoming standard, namely EJB 3.1, JPA 2.0, JAX-RS, JSF 2.0 and JCDI on top of GlassFish v3 - and it turned out surprisingly productive, although all implementations were far from being in a release state!

Only one thing we failed to run in a stable way: The new EJB 3.1 container API refused to run our unit tests. The main problem was JPA support - persistence units got not recognized and EJBs usually failed with a NullPointerException calling the EntityManager.

Alexis and Adam recently blogged about this specific feature, and of course I had to get back and try it out myself! Indeed, starting up a container and looking up EJBs works fine. But to test your Entities there are still a few tweaks you might want to apply.

Start with creating a Maven project and add the embeddable GlassFish dependency. I'm also using TestNG instead of JUnit, as it has a nice @BeforeSuite annotation which allows starting the container only once before running your tests.

<dependency>
<groupId>org.glassfish.extras</groupId>
<artifactId>glassfish-embedded-all</artifactId>
<version>3.0-b69</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.9</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>

As described in Alexis' blog, you can start your EJBContainer with a reference to your GlassFish domain. This will allow you to start up data sources you most probably need to properly test your JPA code.

The disadvantage here is that you either depend on a hardcoded location or a system property which each of your team members have to set. Or, in case of your continuous integration system, you might not want to have a GlassFish installation at all.

Fortunately you can create a mini GlassFish domain with only a few files. The image below shows the files you need and how I placed them in my Maven module:



You can take your existing domain.xml containing your data sources and place it in here - you can reference it now relatively to your module location. Your unit tests then start with:

private static Context ctx;
private static EJBContainer container;

@BeforeSuite
public static void createContainer() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, new File("target/classes");
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
"./src/test/glassfish");
container = EJBContainer.createEJBContainer(properties);
ctx = container.getContext();
}

This will run your unit tests against your development database. In case you want to run them in a local database, you can simple replace the connection pool config in your domain.xml, e.g. with a local Derby installation:

<jdbc-connection-pool datasource-classname="org.apache.derby.jdbc.EmbeddedDataSource"
res-type="javax.sql.DataSource" name="[your DS name]" ping="true">
<property name="ConnectionAttributes" value="create=true" />
<property name="DatabaseName" value="./target/unit-test" />
<property name="Password" value="" />
<property name="User" value="" />
</jdbc-connection-pool>

This creates the database in your target folder and requires adding Derby to your Maven POM:

<dependency>
<groupId>org.apache.derby</groupId>
<artifactId>derby</artifactId>
<version>10.5.3.0_1</version>
<scope>test</scope>
</dependency>

Unfortunately this setup might not match with the configuration in your persistence.xml and generate invalid SQL for your test database. You can either solve this with Maven filters in different profiles, or alternatively create a staging directory for your EJBContainer. I'm using the Apache Commons IO tools here for convenience:

...
private static final String MODULE_NAME = "embedded";
private static final String TARGET_DIR = "target/" + MODULE_NAME;

@BeforeSuite
public static void createContainer() throws Exception {
File target = prepareModuleDirectory();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, target);
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root",
"./src/test/glassfish");
...
}

private static File prepareModuleDirectory() throws IOException {
File result = new File(TARGET_DIR);
FileUtils.copyDirectory(new File("target/classes"), result);
FileUtils.copyFile(new File("target/test-classes/META-INF/persistence.xml"),
new File(TARGET_DIR + "/META-INF/persistence.xml"));
return result;
}

You can use the @AfterSuite annotation to clean up the temporary folder. Note that with this setup, the EJB lookups change:

protected <T> T lookupBy(Class<T> type) throws NamingException {
return (T) ctx.lookup("java:global/" + MODULE_NAME + "/"
+ type.getSimpleName());
}

29 comments:

majson said...

Another interesting approach to testing JPA queries is a stack called Unitils (http://unitils.org/summary.html). Nicely integrated with TestNG / JUnit and DBUnit but offers much more.

I'm using it in my projects and works just perfect.

leob said...

Great post, and it all worked fine, except for the following issue that I ran into.

I created two separate JUnit test classes for my two EJBs. In each test class I invoked EJBContainer.createEJBContainer from my @BeforeClass method. If I ran each test class individually, it worked fine. However when running "mvn test" to run both test classes, I got all kinds of nasty errors. Specifically I would get JDBC/JPA errors. When I switched from Derby embedded to Derby server these errors would disappear, but obviously I was keen on using Derby embedded for my unit tests.

Then I noticed that, when the second test class ran, embedded Glassfish was complaining that it couldn't initialize something because a singleton object had already been created. This was obviously a result of the first test class already having created an embedded Glassfish container. Even when calling container.close() this singleton apparently keeps hanging around in the JVM - changing the global state and causing the second test class to fail.

Nasty, but the solution turns out to be simple: run the Maven Surefire plugin with forkMode 'perTest', like this:

<build>
   <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
            <forkMode>perTest</forkMode>
         </configuration>
      </plugin>
   </plugins>
</build>

This has Maven Surefire run each test class in a separate JVM, which fixes the Glassfish singleton issues.

The only thing I'd still like to figure out is how to run embedded Derby in memory (which should be possible) as that would probably considerably speed up unit testing a lot...

leob said...

Hi,

Unfortunately, I just found out that the very moment when I added a @Remote interface to one of my EJBs, it just BROKE my carefully crafted Maven-embedded EJB unit tesing setup.

Introduction of a @Remote interface (on whichever EJB in my application) made EJBContainer.createEJBContainer crash throwing the following exception:

java.lang.NullPointerException
at com.sun.enterprise.iiop.security.SecIORInterceptor.getServerPort(SecIORInterceptor.java:191)
.............
at com.sun.corba.ee.impl.naming.cosnaming.TransientNameService.(TransientNameService.java:90)
.............
at org.glassfish.enterprise.iiop.api.GlassFishORBHelper.getORB(GlassFishORBHelper.java:89)
at com.sun.ejb.containers.BaseContainer.(BaseContainer.java:556)
at com.sun.ejb.containers.StatelessSessionContainer.(StatelessSessionContainer.java:150)


Apparently it's crashing while trying to initialize RMI-IIOP (even though I had no intention to unit test the @Remote interface).

This is annoying... any ideas how to fix or work around it?

Thomas said...

Sounds like something that might be interesting for the GlassFish team...

Just as a quick workaround, maybe it helps to disable security in the IIOP listeners. Have a look at your domain.xml and switch off

<iiop-listener port="3820" id="SSL" address="0.0.0.0" security-enabled="true">

or completely remove the listeners.

leob said...

Thanks for the quick reply.

I tried disabling security on the listeners, disabling the listeners themselves, and deleting the listeners altogether, but unfortunately none of it fixed the problem.

It could well be that to solve the problem you'd have to use the GlassFish embedded API (http://dlc.sun.com/pdf/821-1208/821-1208.pdf) to initialize and start a complete embedded server rather than just the embedded EJB container.

(a full embedded server that's listening on ports would be needed anyway if you want to unit-test web services or web pages rather than EJBs)

For now I'll "solve" the problem by simply not using @Remote. I'm not that keen on using IIOP-RMI and remoting anyway - I'm leaning more towards EJB Lite with the Jave EE 6 web profile and using no-interface EJBs (with JAX-WS or JAX-RS for external interfaces when necessary).

The Opinionated Bastard said...

Tip:
Use memory:databasename as the database name for Derby in order to make an in-memory database. Even better, use

memory:databasename;create=true

to get a new database each time.

leob said...

Thanks for that... except that I tried this already and it didn't work, unfortunately.

It does work if you define your datasource right inside persistence.xml (as a non-JTA datasource - JPA used from within a Java SE environment - not container-managed).

What I could NOT get working was
setting up the datasource in my app server (GlassFish 3, domain.xml), referring to it from persistence.xml via its JNDI name (as a JTA datasource - using container-managed JPA).

Could be I'm doing something wrong, could also be a bug... stopped looking at it and decided to be happy with embedded but non-memory. At some point you have to stop wasting your precious time...

The Opinionated Bastard said...

Sorry, should have been more specific. You have to use memory:dbname in the domain.xml file, not in the persistence.xml file. Then GF will start it in memory, and the persistence.xml file can find it after that.

leob said...

Man you're my hero... thanks for the tip coz I have it working now!

Actually I was almost there but there was one subtlety that I had wrong. So here's my datasource definition in domain.xml:

<jdbc-connection-pool datasource-classname="org.apache.derby.jdbc.EmbeddedXADataSource" res-type="javax.sql.XADataSource" name="messageDB">
<property name="databaseName" value="memory:messageDB" />
<property name="connectionAttributes" value=";create=true" />
</jdbc-connection-pool>
<jdbc-resource pool-name="messageDB" jndi-name="jdbc/messageDB" />

Note that I've set the "databaseName" property to "memory:messageDB", and that I've set ";create=true" using the "connectionAttributes" property (because appending ";create=true" directly to the database name did not work).

The Opinionated Bastard said...

Glad I could help.

I'm having trouble testing my EJBs though, because it doesn't look like the EJB Container is building the transactions like it should. Do I need to build and close an EJBContainer for each test? I've been re-using the one.

leob said...

What kind of problem are you having exactly with your transactions? What's your unit test doing?

Obviously you can try moving the 'createEJBContainer' call from your @BeforeClass method to a @Before method, and see if that fixes your problem.

If it does, then evidently you're stuck with rather slow unit tests (I wonder if you can still call them that), but in any case you know where the problem comes from.

It sounds a bit similar to the problem I had getting embedded Derby working, which I fixed by running my Maven tests in separate VMs (Maven Surefire "fork" option, see previous post). Apparently the embedded EJB container was keeping state in a static variable or a singleton or whatever.

I'm getting the idea that this EJB unit testing stuff is not as simple and trivial as they would like you to believe...

sarbogast said...

First of all, thanks a lot for this great article. It gave me hope again about testing my EJB's.
Unfortunately, I have an issue when Maven starts up GlassFish:

INFO: [Thread[GlassFish Kernel Main Thread,5,main]] exiting
10 mars 2010 17:04:53 org.glassfish.ejb.embedded.EJBContainerProviderImpl createEJBContainer
GRAVE: ejb.embedded.exception_instantiating
java.lang.NoSuchMethodError: org.objectweb.asm.ClassReader.accept(Lorg/objectweb/asm/ClassVisitor;I)V

Does that ring a bell to you?

Richard Kolb said...

Very Nice. Thank you

Laird Nelson said...

Hi; how does the EJBContainer find your EJBs to test?

I've followed every tutorial under the sun and my EJBs are never found. Alexis says explicitly that code present on the classpath will be inspected and deployed (if it is an EJB of course). I have found this (like lots of other Glassfish-related articles) to simply be untrue. How'd you get this to work?

Thanks for the great writeup.

Best,
Laird

leob said...

Hi Laird,

I did it by passing configuration data inside a Properties object to the EJBContainer#createEJBContainer method, like this:


@BeforeClass
public static void setUpClass() throws Exception {
container = createContainer();
}

@AfterClass
public static void tearDownClass() throws Exception {
container.close();
}

protected static EJBContainer createContainer() {
Map properties = new HashMap();
properties.put("org.glassfish.ejb.embedded.glassfish.installation.root", "./src/test/glassfish");
properties.put(EJBContainer.MODULES, new File("target/classes"));

return EJBContainer.createEJBContainer(properties);
}

@Before
public void setUp() throws Exception {
testBean = (CalcService) container.getContext().lookup("java:global/classes/CalcService");
}

Michael said...

I cannot seem to get my embedded Derby database to work. I get a NameNotFoundException.

ppires said...

how do you delete your temporary folder?

ppires said...

nevermind, i found the solution myself.


org.glassfish
maven-embedded-glassfish-plugin

true

ابن فلسطين said...

hello,
i have followed this article and the one here http://blogs.sun.com/alexismp/entry/testing_ejb_3_1_s
but i have no luck to get it working
i'm using netbeans 6.9.1 GF 3.01

Driv said...

Why are you declaring provided

??

Driv said...

Sory, but the blog filters xml, the question was:

Why is the scope of the embedded glassfish declared as provided?

Thomas said...

Scope provided means that the classes are visible both for compile (contains all the Java EE APIs) and test, but it will not be packaged with your WAR file.

Alternatively you might use scope test and use a EE API dependency as provided. Note that Eclipse does not distinguish test and compile classpaths and you might run into weird conflicts when running your tests in Eclipse.

Chirag said...

Thanks, very good post. Is there a way to specify custom domain instead of default domain "domain1"?

Thanks

N W said...

I tried the staging directory approach outlined in this blog with GF 3.1 FCS (bld 43). It does not work. When I start up my test the embedded container complains about files missing from the staging area. Things like admin-keyfile, cacerts.jks etc. There are like 20 files beyond what this blog talks about. Then even after satisfying all the files things still do not work. I am using a PU with embedded Derby, however, when deploying against the staging directory the EJB container tries to connect the the derby network server port 1527?! I gave up wasting time on this and simply pointed things to my GFv3.1 install and was able to get the EJB container to start and lookup my bean against embedded derby. The next issue I have is the fact that there is no easy way to specify an alternate PU for an injected PersistenceContext. If anyone has any ideas (save swapping in a different persistence.xml at packaing time) I'd love to hear them.

Thomas said...

Hi N W,

yes very likely that this approach is totally out of date with GF 3.1. The post was published even before GF 3.0 final release.

While still in Alpha, Arquillian seems to be a much better way to run tests against a container. Works with both JBoss 6 and GlassFish (not sure if 3.1 is already supported though) and in both embedded and in-container mode.

Noah said...

Hi Thomas -

Yes, I was eventually able to get things to work using a full glassfish install and some tricks to copying in a persistence.xml file for tests. EE56 - easy to code, hard to test when you want container services. I haven't looked at Arquillian in a while, went to a talk given by the devs last year. Looks interesting but like you said its alpha and I need to code and test, not invest a lot of time jockeying another tool that isn't baked yet. I'll def. keep an eye on it though.

Rwanou said...

Great tutorial, thanks a lot.

for those like me trying to port it to GlassFish 3.1, there are lots of drawbacks. I couldn't have the container work with remote EJB's due to a known bug: http://java.net/jira/browse/GLASSFISH-15775
Before that, please include SLF4J API and JDK impl, or else you will get stuck (nasty JNDI lookup fail on your JDBC pools).

If anybody has an idea on how to manage the container using GF 3.1, I'd more than happy to know how you managed.

Thomas said...

We're using Arquillian to test with an embedded GlassFish 3.1 in our Query CDI Extension. There were still some tweaks required to get it run but overall I'd recommend the Arquillian approach over what I suggested originally here.

Paul Schyska said...

Hi, nice tutorial. I've found a little simpler approach than replicating a glassfish *installation* by only modyfing the default embedded *instance*, also fixing some other problems (like using different persistence.xml) - I've written a small tutorial on my blog at http://pschyska.blogspot.com/2011/06/unit-testing-ejb-31-with-netbeans-maven.html

Cheers, Paul