“Offline” EJB containers

From time to time you may encounter different situations when you want your enterprise beans (both @EJB and @Inject – aka CDI) to be injected independently from the EJB container. Usually in these situations you have no access to any EJB container at all. Such situations can occur when running integration tests, applications where a piece of functionality has to be available on both (non-enterprise)client and server side and many others (there must be other cases… I can’t really think of any right now, but I am certain they exist). Well in these cases you’ve got to inject your stuff somehow, unless you want to end up with a bunch of NullPointerExceptions.

In this post we will be examining two ways of solving this problem; two frameworks separated from application servers: OpenEJB and Spring. For the sake of the post let’s consider a toy problem: withdrawing money from an ATM. I don’t know anything about banking systems, but somehow tend to end up using such examples. So, let’s say our application looks like this (sure, this design is not very realistic, but anyway, it’s got injection and that’s what matters, right?):

  Atm

Implementation (dummy) can be found here.

Let’s say that we want to write two integration test cases, one for the success one for the failure path:

		private AtmFacade atmFacade;
		//...
		@Test
		public void withdraw_endsSuccessfully_whenAmountIsLowEnough() {
			assertTrue(atmFacade.withdraw(1234, 500).toLowerCase().contains("transaction successful"));
		}

		@Test
		public void withdraw_endsUnsuccessfully_whenAmountIsTooHigh() {
			assertTrue(atmFacade.withdraw(1234, 50000000).toLowerCase().contains("transaction failed"));
		}

Once we get rid of all the null pointers, we are done 🙂

Solution 1 – OpenEJB.

OpenEJB can be obtained from here. Let’s now see the integration tests:

package bank.trans;

import static org.junit.Assert.assertTrue;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.openejb.OpenEJB;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.core.LocalInitialContextFactory;
import org.junit.Before;
import org.junit.Test;

import bank.iface.AtmFacade;

public class AtmFacadeImplTest_OpenEJB {

	private AtmFacade atmFacade;

	@Before
	public void setup() throws NamingException, OpenEJBException {
		atmFacade = getBean();
	}

	public AtmFacade getBean() throws NamingException, OpenEJBException {

		Properties p = new Properties();
		p.put(Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName());

		if (!OpenEJB.isInitialized()) {
			OpenEJB.init(p);
		}
		InitialContext initialContext = new InitialContext(p);
		return ((AtmFacade) initialContext.lookup("AtmFacadeImplLocal"));
	}

	@Test
	public void withdraw_endsSuccessfully_whenAmountIsLowEnough() throws NamingException, OpenEJBException {
		assertTrue(atmFacade.withdraw(1234, 500).toLowerCase().contains("transaction successful"));
	}

	@Test
	public void withdraw_endsUnsuccessfully_whenAmountIsTooHigh() throws NamingException, OpenEJBException {
		assertTrue(atmFacade.withdraw(1234, 50000000).toLowerCase().contains("transaction failed"));
	}

}

That’s right. All we had to do is to write a simple method in which we initialized the container. (By the way, I was experiencing strange problems when OpenEJB jars were not on the very top of my classpath). Note how the the initial context factory is defined through the property Context.INITIAL_CONTEXT_FACTORY. Without this, you’d get an exception like:

javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
	at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
	at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
	at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
	at javax.naming.InitialContext.lookup(InitialContext.java:411)
	at bank.trans.AtmFacadeImplTest_OpenJPA.getBean(AtmFacadeImplTest_OpenJPA.java:37)
	at bank.trans.AtmFacadeImplTest_OpenJPA.setup(AtmFacadeImplTest_OpenJPA.java:25)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Also watch out for your beans’ JNDI names. Once the OpenEJB container is up and running you should see those JNDI names in the container’s logs. If there are no JNDI names there, take care, OpenEJB might not be working properly (or the log is switched off).

As you can see, using this solution you need not modify your existing code, which is a big plus. There is no boilerplate code generated, you’ve got both CDI and EJB support, no external configuration (unless you explicitly want to add some), in short; it’s really easy to use. However it’s really unlikely to already have OpenEJB on your classpath, so you have to add “several” jars to make it work.

Let’s run the tests, and, dada! We get:

openEJB

3.297 secs. Yep, that much. The good news is that as you can see only the first test (which gets to initialize the container) will run that long, the second one completes literally in no time.

Solution 2 – Spring.

Spring is also a free tool, you can download it from here.

To make our tests run with Spring, it will be a little bit trickier. The first thing is to create an applicationContext.xml (or whatever.xml) file in your resources directory. In this file you have to define the object graph of your application. Our applicationContext.xml will look like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <bean id="atmFacadeImpl" scope="prototype" class="bank.trans.AtmFacadeImpl">
    	<property name="authenticator" ref="clientAuthenticator"></property>
    	<property name="balanceController" ref="balanceController"></property>
    	<property name="receiptCreator" ref="receiptCreator"></property>
    </bean>

    <bean id="clientAuthenticator" class="bank.trans.ClientAuthenticator"></bean>

    <bean id="balanceController" class="bank.trans.BalanceController">
    </bean>

    <bean id="receiptCreator" class="bank.trans.ReceiptCreator">
    	<property name="responseTextCreator" ref="responseTextCreator"></property>
    </bean>

    <bean id="responseTextCreator" class="bank.trans.ResponseTextCreator"></bean>

</beans>

This file basically is divided into two parts: first you define your beans, then you describe what bean gets injected into which one. The good news is that there is no difference between CDI and EJB beans, no different syntax, they are treated uniformly. Huh.

Okay, let’s then write the tests. The initialization part is the only one to change. We end up with something like this:

package bank.trans;

import static org.junit.Assert.assertTrue;

import java.io.File;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import bank.iface.AtmFacade;

	public class AtmFacadeImplTest_Spring {

		private AtmFacade atmFacade;

		@Before
		public void setup() {
			atmFacade = getBean();
		}

		public AtmFacade getBean() {
			Resource xmlResource = new FileSystemResource(new File("./resources/applicationContext.xml"));
			BeanFactory beanFactory = new XmlBeanFactory(xmlResource);

			return (AtmFacade)beanFactory.getBean("atmFacadeImpl");
		}

		@Test
		public void withdraw_endsSuccessfully_whenAmountIsLowEnough() {
			assertTrue(atmFacade.withdraw(1234, 500).toLowerCase().contains("transaction successful"));
		}

		@Test
		public void withdraw_endsUnsuccessfully_whenAmountIsTooHigh() {
			assertTrue(atmFacade.withdraw(1234, 50000000).toLowerCase().contains("transaction failed"));
		}

}

You didn’t think it can even get easier, but it just did 🙂 You just tell Spring where it can find your bean definitions, get a reference to the bean under test and that’s it…Or is it? Let’s run the tests, and we get:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'atmFacadeImpl' defined in file [C:\Documents and Settings\T\blog\Bank\.\resources\applicationContext.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'receiptCreator' of bean class [bank.trans.AtmFacadeImpl]: Bean property 'receiptCreator' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1429)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1134)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:522)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:314)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
	at bank.trans.AtmFacadeImplTest_Spring.getBean(AtmFacadeImplTest_Spring.java:30)
	at bank.trans.AtmFacadeImplTest_Spring.setup(AtmFacadeImplTest_Spring.java:23)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:601)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'receiptCreator' of bean class [bank.trans.AtmFacadeImpl]: Bean property 'receiptCreator' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
	at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1042)
	at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:902)
	at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:75)
	at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:57)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1426)
	... 30 more

Turns out that Spring requires setters for ALL your beans. Let’s just add all the setters in all our files and we get:

springt

1.422 secs. The first test, again, initializes the object graph, the second one runs extremely fast. However, using this solution you get some boilerplate code; and of course, whenever you rename something (I often do that) you have to edit the context xml file too. In return, you get faster runtimes, and much higher chance that you already have Spring on your classpath (as Spring can really do a bunch of things). Also note how cleverly can Spring understand the javax injection annotations  along with its own @Autowired annotation. I like that  very much, very smart feature.

I guess many other alternatives are out there, some of them might even be easier to use. These two options I have experience with, that’s the reason they were presented here.

Advertisements

Author: tamasgyorfi

Senior software engineer, certified enterprise architect and certified Scrum master. Feel free to connect on Twitter: @tamasgyorfi

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s