How to use EJB with Spring framework?

1) Introduction

Enterprise Java Beans (EJB) can be used extensively in Spring’s environment. In this article, we will know about the transparent support available in Spring for using the Stateless and the Stateful Session Beans in Spring.The pre-requisite for this article is some basic knowledge in Spring which can be got by reading the article in javabeat Introduction to Spring Web Framework.

If you are beginner for spring framework, please read our article on introduction to spring framework, spring aop, spring mvc and list of spring articles. You can find the list of recommended books for spring. Javabeat covers extensive articles on the spring framework. If you are interested in receiving the updates, please subscribe here.

2) API Support for EJB in Spring

Let us get into the supporting classes/interfaces available in the Spring Distribution for providing integration support of Enterprise Beans. Let us look at the important classes available in the following packages,

  • org.springframework.ejb.support
  • org.springframework.ejb.access

2.1) Supporting Base classes

This package contains the base supporting classes for creating an enterprise bean. For example, in order to create stateless session bean we can make use of the class 'AbstractStatelessSessionBean'. Similarly, the classes AbstractStatefulSessionBean and AbstractJmsMessageDrivenBean correspond to Stateful Session Bean and Message Driven Bean respectively.
These classes generally form the Facade for the clients. It means that the core logic for doing an operation will be implementated as a POJO (Plain Old Java Object) and the Enterprise Beans merely serve as a facade and delegate the control to POJOs. One of the major disadvantages that the developers would have felt while using the Enterprise Bean running in a EJB Container is that it will be harder for them to perform unit testing. With the use of Spring’s approach, unit testing becomes much simplier since the implementation classes are POJO.
The common super-class for all Enterprise Beans is the AbstractEnterpriseBean which contains a well-defined strategy for loading beans from the Application context. The default strategy is to use the ContextJndiBeanFactoryLocator that will create a BeanFactory object with the information taken from one or more classpath locations as specified by the default environment variable,
Consider the following default Bean Factory locating strategy,

<session>

    ...

    <env-entry>
        <env-entry-name>ejb/BeanFactoryPath</env-entry-name>
        <env-entry-type>java.lang.String</env-entry-type>
        <env-entry-value>applicationContext.xml</env-entry-value>
    </env-entry>

    ...

</session>

We can see from the above code snippet that the default environment variable corresponds to the default bean factory locator key and in our case it happens to be 'ejb/BeanFactoryPath'. This always can be overridden programmatically by calling the method as follows,

String beanFactoryLocatorKey = "applicationSpecificBeanFactoryLocator";
bean.setBeanFactoryLocatorKey(beanFactoryLocatorKey);

The default BeanFactory Locator is the ContextJndiBeanFactoryLocator and this can also be overridden programmatically by calling the following method,

BeanFactoryLocator myBeanFactoryLocator = ...;
Bean.setBeanFactoryLocator(myBeanFactoryLocator);

2.2) Accessing Enterprise Beans

The mode of accessing a Stateful Session Bean is different from the way a Stateless Session Bean is accessed. Let us look into the support for accessing a Stateless Session Bean (both local and remote).
For accessing a Local Stateless Session Bean, we need to use the supporting class LocalStatelessSessionProxyFactoryBean. The configuration for accessing a local stateless session bean is as follows,

<bean id="myBean">

    <property name="jndiName">
        <value>ejb/myBeanService</value>
    </property>

    <property name="businessInterface">
        <value>MyComponentService</value>
    </property>

</bean>

There are two properties for configuring a local stateless session bean using Proxy mechanism. The first property 'jndiName' is the Jndi name of the bean which can be used by the Jndi Bean Factory object to perform lookup operation. The second property 'businessInterface' defines the business interface for which the implementation will be generated by the Spring container and delegation will be made to the actual implementation.
Similarly, for accessing the Remote Stateless Session Bean, the class SimpleRemoteStatelessSessionProxyFactoryBean has to be used instead of LocalStatelessSessionProxyFactoryBean. The properties 'jndiName' and 'businessInterface' will still be applicable.
There is no direct support in Spring for accessing the Stateful Session Beans using Proxy mechanisms. However, a convenient way is provided in the form of JndiObjectFactoryBean.
Let us consider the following configuration,

<bean id="myServiceHome">

    <property name="jndiName">
        <value>ejb/myService</value>
    </property>

</bean>

In the above Xml snippet, we have configured an instance of a EJB Home interface with the help of JndiObjectFactoryBean. In a traditional EJB Applicaition, we have to manually do a JNDI lookup using the InitialContext to get a reference to the EJB Home object. But in Spring, the lookup operation is made easy merely by putting some configuration information,
We would have to use the following approach to get a reference to the Stateful Session Bean,

ApplicationContext context = .... ; // Initialize the Application context.
MyServiceHome myServiceHome = (MyServiceHome)context.getBean("myServiceHome");

MyService myService = myServiceHome.create();
// Perform the required business logic using this bean.
.
.
.

3) Creating a Stateless Session Bean

In this section, let us see an example for creating a simple Stateless Session using the supporting Spring APIs. One of the major goals of Spring is to facilitate unit testing in a much simpler way, which means that the implementation classes should not depend on the third-party library classes. Let us develop a Hello Service Application.

3.1) The Business Interface

Let us define the business interface for our Application as follows,
HelloService.java

package javabeat.net.articles.spring.ejb.integration.stateless;

public interface HelloService {

    public String hello();

}

The interface has a single method hello() which is intended to be given implementation by some classes.

3.2) The Implementation class

HelloServiceImpl.java

package javabeat.net.articles.spring.ejb.integration.stateless;

public class HelloServiceImpl implements HelloService{

    public String hello() {
        return "Hello";
    }

}

The above class provides a simple implementation for the business interface HelloService. The implementation merely returns the string “Hello”. Note that a real implementation may contain much sophisticated logic by maintaining transaction, accessing the database, etc.. It is evident that the above business interface follows the POJO standard which means that it will be easier to perform Unit Testing in isolation by providing Mock Implementation.

3.3) The EJB Local Object

HelloServiceLocal.java

package javabeat.net.articles.spring.ejb.integration.stateless;

import javax.ejb.EJBLocalObject;

public interface HelloServiceLocal extends HelloService, EJBLocalObject{

}

Let us define a Local interface by extending the EJBLocalObject. Note that this interface also extends our business interface HelloService. This is the client-facing interface meaning that the client will invoke all the business operation through this interface.

3.4) The EJB Home Object

Home interfaces follow Factory patterns to return either Local or Remote objects. Usually clients will obtain a reference to the Home interface through JNDI, then will create the Local (or Remote) interface by using one of the create methods.
HelloServiceHome.java

package javabeat.net.articles.spring.ejb.integration.stateless;

import javax.ejb.CreateException;
import javax.ejb.EJBLocalHome;

public interface HelloServiceHome extends EJBLocalHome {

    public HelloServiceLocal create() throws CreateException;

}

The above Home interface contains a create() method that returns a HelloServiceLocal object.

3.5) The Hello Service EJB

HelloServiceEjb.java

package javabeat.net.articles.spring.ejb.integration.stateless;

import javax.ejb.CreateException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.ejb.support.AbstractStatelessSessionBean;

public class HelloServiceEjb extends AbstractStatelessSessionBean implements HelloService{

    private HelloService helloService;

    protected void onEjbCreate() throws CreateException {
        BeanFactory beanFactory = getBeanFactory();
        helloService = (HelloService)beanFactory.getBean("helloService");
    }

    public String hello() {
        return helloService.hello();
    }
}

This is the real EJB Component that needs to be deployed to the Server. Note that this class extends the AbstractStatelessSessionBean so that it contains the default Bean Factory Locator and some other stuffs. The EJB Components developed through Spring’s style will merely serve as a facade and the original implementation will be delegated to the POJO Business Implementation class.
In the above case, even though we have a method called hello() (this method must be defined since the HelloServiceEjb is implementing the HelloService interface), the implementation simply delegates the control to HelloServiceImpl.hello(). One important point to be mentioned here is the need for instantiation the HelloService object. The default Bean Factory object (Jndi Bean Factory Locator) is referenced and an instance of the implementation class is acquired by calling the getBean() method.

3.6) Bean Configuration file

spring-stateless-ejb.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
<bean id="helloService"
/>

</beans>

The preceding HelloEjbComponent instantiates an implementation of HelloService through the identifier “helloService”. This identifier should match the one that is defined in the configuration file.

3.7) Deployment Descriptor

ejb-jar.xml

<ejb-jar>

    ...

    <session>
        <ejb-name>HelloServiceEjb</ejb-name>
        <local-home>
            javabeat.net.articles.spring.ejb.integration.stateless.HelloServiceHome
        </local-home>
        <local>
            javabeat.net.articles.spring.ejb.integration.stateless.HelloServiceLocal    
        </local>
        <ejb-class>
            javabeat.net.articles.spring.ejb.integration.stateless.HelloServiceEjb
        </ejb-class>
        <session-type>Stateless</session-type>
        <transaction-type>Container</transaction-type>

        <env-entry>
            <env-entry-name>ejb/BeanFactoryPath</env-entry-name>
            <env-entry-type>java.lang.String</env-entry-type>
            <env-entry-value>spring-stateless-ejb</env-entry-value>
        </env-entry>
    </session>    

    ...

</ejb-jar>

The above deployment descriptor is similar to a traditional deployment descriptor. One thing to note is the environmental entries, that contains a key called 'ejb/BeanFactoryPath' which contains the path to the configuration file. As mentioned in the preceding section, the default Bean Factory Locator key is 'ejb/BeanFactoryPath' and this can always be overridden by specifying the property beanFactoryLocatorKey.

3.8) The Client’s Configuration file

client-spring-stateless-ejb

<beans>

    <bean id="echoService">

        <property name="jndiName">
            <value>java:comp/env/helloService</value>
        </property>
        <property name="businessInterface">
            <value>javabeat.net.articles.spring.ejb.integration.stateless.HelloService</value>
        </property>
    </bean>

</beans>

The client accessing an Enterprise Bean can be a standalone Remote client, or a Servlet or even can be another Enterprise Bean. In all these cases, the client must be aware of the factors like locating the Bean when given a Jndi name and to get a appropriate reference to the business interface.
In the above configuration file, the property 'jndiName' is used to locate the Enterprise Bean object. The business interface is HelloService which means a proxy object for HelloService interface will be returned simply because we have configured the bean to use LocalStatelessSessionProxyFactoryBean.

4) Conclusion

A major drawback in developing a J2EE Application that makes use of Enterprise Java Beans for doing a business operation is the un-necessary complications available for the clients like looking up the Home object, creating the reference etc.., and it is impossible to do Unit testing for the Enterprise Bean as these beans always reside within the Container. However with Spring’s way of accessing Enterprise Beans this has been simplified.
If you have any questions on the spring framework integration with email, please post it in the comments section. Also search in our website to find lot of other interesting articles related to the spring framework. There are some interesting articles about spring framework, interview questions, spring and hibernate integration,etc. If you are looking for the detailed knowledge, refer books for the spring framework.

If you would like to receive the future java articles from our website, please subscribe here.

Comments

comments

About Krishna Srinivasan

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.

Comments

  1. Ashay Thorat says:

    Great article. Exactly what I was looking for. Could you please share the source code if possible. So that I can clear some silly doubts in my mind.

  2. We have to port spring standalone application in WAS. How to load spring application context in WAS.-Websphere App Server

Trackbacks

  1. […] How to use EJB with spring framework? […]

  2. […] How to use EJB with Spring framework? […]

Speak Your Mind

*