Read Digital Edition


ADS BY GOOGLE
Top Three Links You Must Click On


Spring and EJB 3.0 in Harmony
In search of the best of both worlds

The new EJB 3.0 specification supports some notion of dependency injection via annotations. As an avid Spring user, I'm used to configuring fine-grained beans with Spring bean factories and XML. How does EJB 3.0 compare? More importantly, can we use EJB 3.0 POJOs and Spring POJOs side-by-side in applications? In this article, I'll try to answer those questions based on my own investigations and experiences. As it turns out, using a versatile application server like the JBoss Application Server (AS), Spring and EJB 3.0 POJOs can co-exist in harmony in your applications.

EJB3 Dependency Injection
In EJB 3.0 it's fairly easy to inject Java EE components into an EJB object using various annotations provided by the specification. @Resource is available to inject things like datasources and JMS destinations. @EJB is there to get references to other EJBs. @PersistenceContext can get you a reference to the EntityManager service. You can use these annotations to inject directly into the field of your EJB, or you can apply them on setter methods. Here are some examples:

@Stateful
public class ShoppingCartBean implements ShoppingCart {
    @Resource(name="DefaultDS") private DataSource ds;
    @EJB private CreditCardProcessor ccp;

    EntityManager em;

    @PersistenceContext(unitName="orderdb")
    public void setEntityManager(EntityManager em) {
      this.em = em;
...
}

For developers who don't want to use annotations to inject components, EJB 3.0 provides an XML alternative to injection annotations:

<ejb-ref>
    <ejb-ref-name>ejb/CreditCardProcessor</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref/type>
    <local>com.acme.CreditCardProcessor</local>
    <ejb-link>CreditCardEJB</ejb-link>
    <inject-target>ccp</inject-target>
</ejb-ref>

Great! So we have two ways of injecting dependencies in EJB 3.0. However, one big problem with EJB 3.0 dependency injection is that there's no way of configuring, defining, and injecting plain Java beans. You can only inject Java EE component types and <env-entry> only lets you inject primitive types. Sure, EJBs look a lot like POJOs and JBoss has the concept of a @Service bean, which is a singleton, but do we really have to define and inject these component objects that have to run in containers? And what about beans from third-party libraries and pre-existing code. There must be something better! Could it be JNDI registration? Maybe, but who will instantiate and construct our beans? Really this is what Spring's dependency injection is all about. But how do we get Spring's beans into an EJB3 container?

Spring and EJB3 - Synergy or Superfluous?
To integrate Spring beans with EJBs, the application server plays the central role since it can deploy both components. JBoss AS seems to be the best platform here because it was the first application server to support EJB 3.0 and it is an Open Source product. More importantly, JBoss AS's microkernel architecture is flexible enough to create any package/component type that can be deployed in the JBoss runtime. JBoss AOP and interceptor technology could be used to glue custom behavior into POJOs and/or EJBs.

So I decided to write a Spring deployer for JBoss AS. In the rest of the article, I'll discuss exactly what I did and how you can use the deployer. As it turns out, there really is synergy between Spring and JBoss EJB 3.0. Developers can have the best of both worlds.

JBoss AS Deployers
The first challenge is to package Spring into a deployable unit to eliminate the bootstrapping code that's required in most Spring applications. The deployer should work with a custom Spring archive type in much the same way Java EE has .EARs and .WARs. You can define new archive types in JBoss by writing a JBoss Deployer.

When JBoss boots up, it scans the deploy directory for available packaged components that are JBoss services or your packaged applications. JBoss Deployers are responsible for managing these packaged components. Their purpose is to understand deployment descriptors and archive formats so they can be deployed into the JBoss application server at runtime. Deployers are also responsible for creating classloaders for packaged component and managing hot deployment. There's a specific deployer service for each component type in JBoss: enterprise archives (.ear), Web archives (.war), EJB archives (.jar), datasources (-ds.xml), Hibernate archives (.har), etc. I wanted to be able to define a Spring archive (.spring) that would look like this:

myapp.spring/
    org/acme/MyBean.class
    META-INF/spring.xml

It would really look like an EJB jar, but instead of a ejb-jar.xml deployment descriptor, one would be specific to Spring. The JBoss Spring deployer recognizes the myapp.spring deployment, parses the deployment descriptor to create a Spring bean factory, and registers that Spring bean factory under some JNDI name so that it can be looked up and used by other applications. And if this archive was removed, JBoss should destroy the bean factory and unregister it from JNDI. Basically, the goal is to let Spring bean factories be hot-deployable in a JBoss environment.

JBoss has an abstract API for writing new deployers and it was very simple to write a Spring-based archive type. JBoss's deployer API provides a simple abstract class - org.jboss.deployment.SubDeployerSupport - that I used to implement my Spring Deployer. One simply has to implement or override a few methods to get things working. Let's look at those methods:

   protected void initializeMainDeployer() {
     setSuffixes(new String[]{".spring", "-spring.xml"});
     setRelativeOrder(350);
   }

   /* @return True if this deployer
   * can deploy the given DeploymentInfo.
   * @jmx:managed-operation
   */
   public boolean accepts(DeploymentInfo di) {
     String urlStr = di.url.toString();
     return urlStr.endsWith(".spring") || urlStr.endsWith(".spring/") ||
     urlStr.endsWith("-spring.xml");
   }

This code is trivial but it's the core of determining if a deployed component is a Spring component. Each JBoss deployer is asked if it accepts a given archive for deployment. This code specifies that the Spring Deployer excepts file names that end with .spring or -spring.xml. JBoss has a default ordering schema when it deploys packages. Since deployers are themselves packaged components, they tell the JBoss runtime what relative order their component type should be deployed in as shown in Listing 1.

As I said before, JBoss deployers support the hot redeployment of your custom component type at runtime. To enable this, you have to give JBoss a URL to watch to see if the timestamp on this file changes at runtime. Our deployer will support raw Spring XML files, .spring JAR archives, and exploded .spring archives. The init() method above sets up the watch URL as shown in Listing 2.

Deployers create() method calls BeanFactoryLoader's create method, which is responsible for managing our different Spring bean factories. When instantiating beans in a new bean factory we make sure that newly created beans are loaded by a global scope classloader. Normal JNDI bindings require a serializable object, which is unusable for our Spring bean factory. JBoss provides a JNDI ObjectFactory implementation that lets us store objects in a non-serializable form in a local JNDI context. The binding will only be valid as long as our bean factory is deployed.

Since we'd like to hot deploy many different Spring archives, each of them must be uniquely represented in our environment. For that purpose, JNDI will be used as a registry for these deployments. But where can we get our local JNDI name? By default, this JNDI name is obtained from the archive's file name: <name>.spring or <name>-spring.xml. But since it's also possible for each bean factory to have parent bean factory, there should be a way to define this parent's name too. This can be done in Spring's beans xml descriptor in description tag as shown in Listing 3.

We are parsing this description tag and looking for a regular expression pattern of 'BeanFactory=(<name>)' and 'ParentBeanFactory=(<name>)' so see Listing 4.

Every time a Spring component is undeployed, we get the corresponding Bean factory through the URL string key in our bean factory map. We remove the map entry, destroy the bean factory singletons, and unbind it from JNDI. When undeploying components be careful of the bean factory hierarchy; don't undeploy some other component's parent bean factory. When shutting down, JBossAS undeploys components in reverse order, which is the right behavior for our child-parent bean factory hierarchy.

About Ales Justin
Ales Justin is a Java developer at Genera Lynx d.o.o. He's also a JBoss contributor and committer.

In order to post a comment you need to be registered and logged in.

Register | Sign-in

Reader Feedback: Page 1 of 1

The new EJB 3.0 specification supports some notion of dependency injection via annotations. As an avid Spring user, I'm used to configuring fine-grained beans with Spring bean factories and XML. How does EJB 3.0 compare? More importantly, can we use EJB 3.0 POJOs and Spring POJOs side-by-side in applications? In this article, I'll try to answer those questions based on my own investigations and experiences. As it turns out, using a versatile application server like the JBoss Application Server (AS), Spring and EJB 3.0 POJOs can co-exist in harmony in your applications.


  Subscribe to our RSS feeds now and receive the next article instantly!
In It? Reprint It! Contact advertising(at)sys-con.com to order your reprints!
Subscribe to the World's Most Powerful Newsletters

ADS BY GOOGLE
IBM has acquired Guardium, a seven-year-old subsidiary of Israel’s Log-On Software transplanted to M...
But on the web, access to services is implicit in the fact that the business is offering the service...
Oracle has offered to cordon off MySQL inside a combined Oracle-Sun to get the European Commission t...
Gartner told Reuters that it overestimated how many PCs Acer shipped in the last seven quarters by a...
Intel has put out its promised beta SDK for Windows (C and C++) and Moblin (C) developers working on...
Behaving like it’s got a future, Sun Monday put out what it calls a significant new version of Virtu...
InformationWeek stumbled on a Microsoft patent application dating back to 2006 deceptively titled “M...
Berlin-based ThinPrint AG, the printer virtualization house, thinks it’s got a cloud solution for th...
The second set of charges filed last week against Indian outsourcer Satyam Computer Services founder...
Singed by user reaction to its plans to up the price of its support contracts, SAP Tuesday postponed...
Apparently Google Gears ain’t gonna stick around that long. Google Apps will eventually get their of...
Gartner thinks the server business has stopped sliding into the abyss. Third-quarter sales weren’t a...
Office Web Apps, Microsoft’s answer to Google Apps, are supposed to be out sometime in June along wi...
Gartner is buying ~$40 million-a-year AMR Research Inc for close to $64 million in cash. AMD special...
Oracle seems to have divided the open source ranks over the MySQL delay it’s having closing its acqu...
The Korean government is going to sink around $172 million into cloud computing next year under a st...
We hear – well, you know how people talk – that Oracle has been quietly meeting with the European Co...
In response to Opera’s complaints Microsoft has reportedly modified the proposed ballot screen that’...
CA is looking for talent in EMEA: associate account managers, directors of solution sales, senior so...
Microsoft has sold the Folio and NXT businesses it got when it bought Fast Search and Transfer, the ...