Oracle Blog

Blog for kensaks

Embeddable EJB

One of the most common requests we hear from Enterprise JavaBeans developers is for improved unit testing support. The two biggest issues they raise are :

Testing Local Session Beans is difficult since they're only accessible by way of the Remote EJB tier or the web tier.

Regardless of bean type, it would be simpler to allow client code to run in the same JVM as the components under test.

Some Java EE implementations have solved these problems by adding a Java SE execution mode in which clients instantiate EJB components within the same JVM. While this is a great start, more work is needed to raise the level of portability and ensure broader support.

We've been discussing this topic extensively in the EJB 3.1 Expert Group. Here's an early look at a proposed solution we're calling Embeddable EJB :

The Embeddable EJB API allows client code to instantiate an EJB container that runs within its own JVM and classloader. The client uses a spec-defined bootstrapping API to start the container and identify the set of enterprise bean components for execution.

The embeddable EJB container provides a managed environment with support for the same basic services that exist within a Java EE runtime : injection, access to a component environment, container-managed transactions, container-managed persistence contexts, etc. In general, enterprise bean components are unaware of the kind of managed environment in which they are running. This allows maximum reusability of bean components across a wide range of testing and deployment scenarios without significant rework.

Note that this is just a normal stateless session bean. It exposes business methods through a no-interface view. The bean implementation uses injection, container-managed transactions, and Java Persistence. There is no special API it must use to be capable of embeddable execution.

Here is some test code to execute the bean in an embeddable container :

1:

2: // Instantiate an embeddable EJB container and search the

3: // JVM class path for eligible EJB modules or directories

4: EJBContainer ejbC = EJBContainer.createEJBContainer();

5:

6: // Get a naming context for session bean lookups

7: Context ctx = ejbC.getNamingContext();

8:

9: // Retrieve a reference to the AccountBean using a

10: // portable global JNDI name (more on this later!)

11: AccountBean ab = (AccountBean)

12: ctx.lookup("java:global/account/AccountBean");

13:

14: // Test the account

15:

16: Account a1 = ab.createAccount(1234);

17:

18: ...

19:

20: // Shutdown the embeddable container

21: ejbC.close();

22:

Next we prepare the following three .jars :

account.jar
An ejb-jar containing the AccountBean class and its associated persistence artifacts.

client.jar
A .jar containing the test client class.

vendor.jar
A vendor-provided .jar file containing the necessary system classes.

All that's left to do is start the JVM using a normal java command :

% java -cp account.jar:client.jar:vendor.jar com.acme.AccountTest

It's important that we support starting the JVM with a plain java command. Not only is it easily understood by developers but it ensures straightforward integration with existing test frameworks and IDEs. We also plan to support an "exploded" directory form of the ejb-jar to reduce the number of packaging steps.

There are more details to work out but we think this is a good first step. As always, let us know your feedback by leaving comments here or by sending email to jsr-318-comments@jcp.org.

> % java -cp account.jar:client.jar:vendor.jar com.acme.AccountTest
>It's important that we support starting the JVM with a plain java command.

This is a naive example, as in real life it would require tons of jars in the classpath (JUnist/TestNG, many vendor jars, internal project dependencies, etc.). So, it has to be either called by the build script, or by IDE.

> Not only is it easily understood by developers but it ensures straightforward
> integration with existing test frameworks and IDEs. We also plan to support an
> "exploded" directory form of the ejb-jar to reduce the number of packaging steps.

Without exploded deployment, this feature is almost useless, at least productivity-widely speaking. As a developer, I want to right click on my test case in the IDE and do a 'Run as Test Case'. If I have to create a JAR (or rely in my IDE to do it) in order to run the test case, it would still be annoying...

Thanks for the feedback. Valid point that many applications will require many more classpath entries. It's fine if the JVM containing the embeddable usage is called from a script or IDE. The main integration points are still just the contents of the JVM classpath and the classloader at the time createEJBContainer is called. Do you see other issues with that approach? One thing I didn't mention is we're planning to allow the application to programmatically override which modules are initialized.

It's good to hear confirmation of the importance of the exploded directory option.

Hey Ken,
My team has been doing a lot of unit testing (with JUnit) of EJB w/ embedded JBoss since EJB3 came out. It is invaluable to the development process and a huge win for EJB3 that this is possible. We can run our unit tests from Eclipse (or other IDE), during builds, or any other time we need to validate functionality.

One big thing is that you need to make sure that you can bring in other resources such as RDBMS's, message queues, transaction managers, etc. These resources are so tightly coupled with your EJBs that effective testing requires them. So while we mostly configure from annotations, some configurations, like queues and database connection information, may be in external files, and in our case is application server specific. These need to be loaded from the classpath as well to get everything working correctly.

Often, prior to making assertions on our EJB, we want to run some SQL to prime the database. This is especially useful if we find a bug in test/production and want to try to replicate it in the unit test. Rather than trying to reproduce the problem programmatically, all we need to do is export the data from the database, add it to our unit test and then do the assertions. Makes it very easy to replicate what would otherwise be tricky bugs to find.

An alternative approach to this would be something like fixtures in Ruby on Rails that get auto-loaded each time you run your test. That approach probably wouldn't work in this sort of specification though.

Thanks for the feedback Rob. It is definitely our goal that it be possible to use these resources within the embeddable container. As you point out, anything less limits the testing benefits. In some cases it will require adding some properties to an overloaded version of the createContainer() API.

hi,
I tried the example and I'm getting this error, with GlassFish v3 (build 66)

No EJBContainer provider available: no provider names had been found.
javax.ejb.EJBException: No EJBContainer provider available: no provider names had been found.
at javax.ejb.embeddable.EJBContainer.reportError(EJBContainer.java:184)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:121)
at javax.ejb.embeddable.EJBContainer.createEJBContainer(EJBContainer.java:78)
at com.abien.business.hello.HelloWorldTest.hello(HelloWorldTest.java:23)

Is the goal of this new spec (EJBContainer concept) to support testing alone or also to run the application like Spring in a non Java EE container - such as proprietary servers or tomcat etc. ? Please advise.

If it is only for testing, could we extend it so it can be embedded anywhere for production. I would rather use this than Spring.

Testing is the main target, although it could also be useful for rich client and batch applications that have more sophisticated needs for executing business logic locally. The Embeddable API is not intended as the mechanism for container pluggability. Our approach for increasing the number of server runtimes that support EJB functionality was to define EJB 3.1 Lite and require it within the Java EE Web Profile.

EJB 3.1 Lite is a subset of the EJB API that focuses on local session beans, without requiring all the legacy and heavier weight features such as Entity beans, RMI-IIOP, and message-oriented middleware. The lower implementation requirements should encourage more web application centric products to support EJB. In these cases, the programming model for the developer is merely deploying EJB components as part of a .war.

If you're specifically interested in Tomcat support for EJB, take a look at OpenEJB.

Thanks for answering my question on EJBContainer concept. You have done good work as a spec lead for EJB since you took over. Great work!

Also, I have following questions
1) WebLogic had this concept of session bean pools and configure the number of stateless or stateful session beans, including message driven beans. Does EJB standard define something like that. If not how do you configure something like that in GlassFish?

2) In EJB3, I see that session beans do not need home interface, but we all know that stateful session beans in EJB2.1 can be constructed in many ways by use o overloaded create methods. In EJB3, is the idea that if we need one interface then we put home interface in stateful session beans using @Home(TheHome.class) annotation, the container will automatically bind home object to JNDI tree and hence can use an EJB 2.1 type model.

1) Pool / Cache configuration is an area we've continued to leave as a vendor value-add rather than trying to spec it out. In GlassFish, it can be configured via the admin console for ejb-container wide settings or at the granularity of an enterprise bean via sun-ejb-jar.xml. See http://docs.sun.com/app/docs/doc/820-4343/abeed?a=view

2) The adapted home approach is intended as a way to take advantage of the newer EJB programming model on the component side without having to upgrade all existing clients at the same time. There's nothing that special about a 2.x-style create method that can't be replicated using the 3.x simplified API. The recommended approach with the EJB 3.x programming model is simply to define one or more business methods on the bean's business interface that the client uses to initialize the state of the SFSB. From the client's perspective it's essentially the same.

1) I had seen the links on glassfish for pools, but it seems like for session beans there is no way to set max-beans-in-pool, initial-capacity etc using annotation. That's what I was looking for. Not the xml descriptor. Apparently, MDB can be done as an annotation. Not session beans. Is that true? At least I dont see any examples of it

2) OK. I understand that sstateful session bean's create/initialization concept can be implemented as business methods. Agreed.

Right, we've decided not to define special annotations for glassfish-specific functionality since it creates a compile-time dependency on non-portable code. There are no GlassFish-specific annotations defined for MDB pool configuration. It's true that the standard @javax.ejb.MessageDriven annotation has some attributes than could be used to configure that kind of behavior, but the specific name/value pairs are not defined by the spec.

I have an EJB3 stateless session besn packaged in a war file in Glassfish EE 6. This works. I would like to initiate 5 stateless session beans in pool to start with. I set the name of the EJB as the "TheCalculator" using @Stateless (name="TheCalculator") annotation and also created the ejb-jar.xml file and the sun-ejb-jar.xml. Where should these files be placed? Now does it go straight under web root as META-INF/\*.xml, paraleel to WEB-INF - Please advise.