Saturday, August 16, 2008

Rapid EJB Development with Unit Tests

Anyone who develops EJBs know that development can be a tedious and slow process. This is because JavaEE application servers are heavy and have slow startup and deployment times. So every time you make a change to your bean, you have to redeploy it and due to the nature of web applications, many of these go paired with a series of steps you need to complete to bring your application to the state it was in previously.

Much of this can be improved by creating servlets/pages dedicated to the current development task, which in some ways can be seen as a unit test. Though even with this you are still slowed down by the deploy requirement. On top of this, deploying only the EJB jar isn't always enough. With some setups you need to restart/redeploy your WAR for it to update the EJB references.

This can be very frustrating at times, especially when you're under pressure to fix a bug, you're running out of time or when you're just plain struggling and have change/compile/deploy has become routine. Naturally I try and optimize everything I do as well as the techniques to do them, but EJB development is something I've never really been able to optimize to a level which can be labeled "Satisfactory". At least not until a few weeks ago.

I've known of OpenEJB for a long time. During one of my routine joins to #openejb on Freenode to see how things are progressing, and to talk to David Blevins (cofounder and developer of OpenEJB, as well as a great person), I discovered how much OpenEJB has matured with their 3.0 release. I decided to give it a go and downloaded the Tomcat release, which is basically just a WAR you deploy as a Tomcat web application enabling Tomcat to run EJBs.

It was good, and my development speed improved quite a bit since Tomcat starts up much quicker, takes up less memory, and deploys much faster than Glassfish. I started using Tomcat for development and every now and then would deploy into Glassfish (which is our production application server) to see if everything is still working fine, fixing compatibility bugs when needed.

At one point I ran into a minor problem and decided to join #openejb again (this time for support). While talking to David Blevins, the conversation came to him convincing me to give OpenEJB's embedded mode a try by creating a simple unit test. He offered to take me through it step by step.

I decided to take his offer to the test (excuse the pun), and downloaded the OpenEJB 3.0 jar. It was a very simple process. I just instructed Netbeans to create a JUnit test for one of my EJBs, included the OpenEJB JAR archives to the list of test libraries and added some initialization code to the test case's setup method.

That was it. The next step was to run the test, and it all worked. In less than 5 minutes I was up and running, and my average compile-to-test time decreased from about 90 seconds to under 20 seconds. Not only this but now, whenever I make changes to my entities and/or EJBs, I can run my test suite to ensure that everything is still working as the design intends it to be.

I can truly say that embedded OpenEJB makes EJB development much easier, and definitely more comfortable. Still having to do development of my JSF pages the old fashioned way (compile/deploy/test) I am always reminded about how much of a different OpenEJB has made for me.

Time saving put aside, what else makes OpenEJB a fantastic piece of software?

Well, what doesn't? The developers of OpenEJB truly put a lot of effort into making your life easier. When I run into an error/exception in my EJBs, I've come to the habit of reproducing it in a unit test, and running it through OpenEJB.

Why, you ask? Because of the error messages/suggestions. Depending on the log level it has been configured with, OpenEJB gives different and always very descriptive error messages, sometimes even making suggestions on how to fix it.

David demonstrated this to me the first time with this example. To inject an EntityManager you annotate it's declaration with @PersistenceContext. For example:
  @PersistenceContext
private EntityManager em;

If you accidentally use this annotation to inject an EntityManagerFactory, OpenEJB will give the following error message:
ERROR - FAIL ... BusinessFacadeBean: Mistaken use of @PersistenceContext on an EntityManagerFactory reference. Use @PersistenceUnit for ref "vds.facades.BusinessFacadeBean/em"

Not only does it tell you exactly what you did wrong, but also makes a suggestion as to how to fix it. There are many of these suggestions.

If you haven't tried it yet, I definitely recommend doing so as soon as you can. I postponed trying for a very long time, and definitely regret having done so. I wish I started developing EJBs this way since the beginning.

Give it a try

If you decide to try it, here is a quick howto. These instructions are for Netbeans. If you use another IDE, adapt them appropriately (same goes for using Ant scripts).
In this example I will use an example EJB TestStatelessBean, with a Local interface TestStatelessLocal.
  1. Download the OpenEJB zip/tar and extract it.

  2. From the extracted files copy the “lib” directory to desired location (You can skip this step if you wish).

  3. Inside your Netbeans project, right-click the “Test Libraries” node, and select “Add JAR/Folder...”

  4. Navigate to “lib” directory in (1) and select all of the JAR libraries contained within. Then click “OK”

  5. Now right-click the “Test Packages” node, and select “New->Test for an Existing Class”

  6. Follow the instructions, selecting one of your EJBs as the class you're creating a test for, in this example: TestStatelessBean

  7. A class called TestStatelessBeanTest will be created, with test methods (identifiable with the @Test annotation) for each of it's methods. Clean out/delete these @Test methods, since they create instances of your EJBs instead of doing InitialContext lookups. Besides, we are doing a simple test.

  8. Now, add the following initialization code to the setup method:
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
    "org.apache.openejb.client.LocalInitialContextFactory");
    InitialContext initialContext = new InitialContext(properties);
    TestStatelessLocal testStateless =
    (TestStatelessLocal) initialContext.lookup("TestStatelessBeanLocal");
    testStateless.testMethod();

  9. And run your test.


That's it. The basic idea is just to add the OpenEJB JARs to your classpath, initialize the InitialContext and then do the EJB lookups (as shown in step 8). Note that you need an ejb-jar.xml in your META-INF directory. If it's not there, just create an empty one with contents: <ejb-jar/>). See the end of this article for complete source code of all examples mentioned throughout this blog entry.

Injection with @Resource, @PersistenceContext, @EJB and so forth will be done on any classes constructed by OpenEJB, ex. EJBs. So if we were to add an entity manager to our TestStatelessBean, we can do so by annotating it with @PersistenceContext and OpenEJB will take care of the rest. You can also configure custom injection for your classes, which can be very useful for things like debugging/logging.

OpenEJB ships with ActiveMQ as it's JMS provider so Message Driven Beans are supported, and OpenJPA as it's persistence provider for entity beans. Since you're probably using another persistence provider with your application server, you're likely to want to test your entities using this provider. Configuring OpenEJB for your provider is beyond the scope of this document, but completely possible and not difficult at all. Just set your <provider> in persistence.xml and you should be good to go. This is fine more most cases, Toplink not being one of them (though OpenEJB 3.1 now has support for Toplink as your persistence provider). Click here for instructions on how to configure Toplink as your OpenEJB persistence provider.

I can go on forever about the benefits of OpenEJB, though I'm rather going to leave the discovery of them up to you.

Enjoy, and be sure to check out the many OpenEJB example code provided on their site!

Links
OpenEJB home: http://openejb.apache.org/
Download OpenEJB: http://openejb.apache.org/download.html
Mailing Lists: http://openejb.apache.org/mailing-lists.html

Example Sources
test/openejb/TestStatelessBean.java
/*
* Test Stateless Bean
*/
package test.openejb;

import javax.ejb.Stateless;

@Stateless
public class TestStatelessBean implements TestStatelessLocal
{
public void testMethod()
{
System.out.println("Beautiful!");
}
}

test/openejb/TestStatelessLocal.java
package test.openejb;

import javax.ejb.Local;

@Local
public interface TestStatelessLocal {

void testMethod();

}

META-INF/ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar/>

test/openejb/TestStatelessBeanTest.java
/*
* OpenEJB JUnit demonstration test
*/
package test.openejb;

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class TestStatelessBeanTest
{
TestStatelessLocal testStateless;

public TestStatelessBeanTest()
{
}

@BeforeClass
public static void setUpClass() throws Exception
{
}

@AfterClass
public static void tearDownClass() throws Exception
{
}

@Before
public void setUp() throws Exception
{
Properties properties = new Properties();
properties.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.openejb.client.LocalInitialContextFactory");
InitialContext initialContext = new InitialContext(properties);
testStateless =
(TestStatelessLocal) initialContext.lookup("TestStatelessBeanLocal");
}

@Test
public void testTestMethod()
{
testStateless.testMethod();
}
}

6 comments:

Boniface said...

Thanks so much Quintin;

You have saved my day. I followed your instructions and it worked. I will surely spread the good news to my collegues. Again thanks.

Joshua.

mattger said...

Great post Quintin. Really demonstrates how simple it is to unit test ur ejbs using embedded openejb

kampret77 said...

Halo Quintin,

I have following problem.

I want to inject let say a string into my bean attribute tagged as resource.

OpenEjb allow you to inject the bean using ejb-jar.xml or env-entries.properties but both of them are located under /src/main which means that will be included in jar and executed in life system.

I want to do unit testing but the injected value is not recognized if i put env-entries under src/test/resources/META-INF.

second is, OpenEjb still executed method that i tagged @postconstruct. So I dont have any change to inject the mocked value into my session bean. Basically I put all initialization in @postcontruct method with hope that I dont have to call it and I can inject the mocked value.

Do you have any idea to solve it?

best regards
sanga

Quintin said...

kampret77,

Let me first make sure I understand you correctly.

You want to inject mock values into your EJB for purpose of unit testing. In other words, you want to initialize certain fields in your EJB, but ONLY when doing unit testing and not when the system is running live.

This all really depends on what IDE or build system you are using. If using ant/maven you can have your META-INF directory built differently depending on whether you're testing or building.

Netbeans/Eclipse can achieve the same effect, but would require a fair amount of hacking.

Beyond this I think if it is possible to specify an alternative path to ejb-jar.xml or env-entries.properties that might help as well, since you can get the InitialContext inside the test itself, and therefore can tell it to load the correct entries file. Whether this is possible or not I'm not sure though.

If you can explain a bit more about your design and why you need to use injection to initialize the class when doing unit testing, it might help to find a solution to the problem at hand.

Quintin said...

I have a possible solution to your problem for Netbeans. If you use another IDE/environment, either let me know and I can advise or adapt appropriately.

Also, this solution is based on my understanding of your problem. If I misunderstood, please describe your problem in more detail.

The solution is quite lengthy due to my explanations of all steps, and therefore I made a new blog post about it. Click here to read OpenEJB Resource injection for Unit Testing.

kampret77 said...

greetings,

Happy to see your replay :D You understand me 100% so I have nothing left to explain.

well ideally I REALLY happy if i can make it in eclipse because i need just to run the unit test by mouse clicking (or through GUI). Actually you give me alread an idea/concept how to do it in IDE.

But I use maven to build in the entire software development so that I think it is "much make sense" to do it with maven. Would you mind explaining me how to make it with maven.

I have even a "bad idea" which is to include all the everything inside the src/test/resources into the "war" including "persistence.xml" while executing ONLY the UNIT TEST by "mvn test".

You are right about the idea to "ask InitalContext" to load all the setting, but once again, I am happy if someone guide me with small how to so that i can tell my "InitialContext" to load all the env-entries.properties or ejb-env.jar. I will continue tomorrow (it is alread 24:00 in Berlin ). I guess it is the same with how with put the database setting inside the properties file, isn't it?

:) it is getting much more interesting.

Some remarks, actually I miss Ejb3Unit because I like unit testing so much and Ejb3Unit offers want I want. But they stop developing it, really sad.

And one thing left :D How can I disable all method annotated with @PostConstruct so that it is not called by the container.

public class UserBean {

@Resource
String myCollege;


@postConstruct
public void myInit(){
this.myCollege = ... (get from something else)
}
}

Event though I can inject the String myCollage but if myInit is still called, the injected value will be over-write and it is impossible to do unit testing.


best regards
sanga lawalata.