Accessing EJBs

This section covers how to access EJBs.

Concepts

To invoke a method on a local or remote stateless session bean, client code must normally perform a JNDI lookup to obtain the (local or remote) EJB Home object and then use a create method call on that object to obtain the actual (local or remote) EJB object. One or more methods are then invoked on the EJB.

To avoid repeated low-level code, many EJB applications use the Service Locator and Business Delegate patterns. These are better than spraying JNDI lookups throughout client code, but their usual implementations have significant disadvantages:

  • Typically, code that uses EJBs depends on Service Locator or Business Delegate singletons, making it hard to test.

  • In the case of the Service Locator pattern used without a Business Delegate, application code still ends up having to invoke the create() method on an EJB home and deal with the resulting exceptions. Thus, it remains tied to the EJB API and the complexity of the EJB programming model.

  • Implementing the Business Delegate pattern typically results in significant code duplication, where we have to write numerous methods that call the same method on the EJB.

The Spring approach is to allow the creation and use of proxy objects (normally configured inside a Spring container), which act as codeless business delegates. You need not write another Service Locator, another JNDI lookup, or duplicate methods in a hand-coded Business Delegate unless you actually add real value in such code.

Accessing Local SLSBs

Assume that we have a web controller that needs to use a local EJB. We follow best practice and use the EJB Business Methods Interface pattern, so that the EJB’s local interface extends a non-EJB-specific business methods interface. We call this business methods interface MyComponent. The following example shows such an interface:

public interface MyComponent {
	...
}

One of the main reasons to use the Business Methods Interface pattern is to ensure that synchronization between method signatures in local interface and bean implementation class is automatic. Another reason is that it later makes it much easier for us to switch to a POJO (plain old Java object) implementation of the service if it makes sense to do so. We also need to implement the local home interface and provide an implementation class that implements SessionBean and the MyComponent business methods interface. Now, the only Java coding we need to do to hook up our web tier controller to the EJB implementation is to expose a setter method of type MyComponent on the controller. This saves the reference as an instance variable in the controller. The following example shows how to do so:

private MyComponent myComponent;

public void setMyComponent(MyComponent myComponent) {
	this.myComponent = myComponent;
}

We can subsequently use this instance variable in any business method in the controller. Now, assuming we obtain our controller object out of a Spring container, we can (in the same context) configure a LocalStatelessSessionProxyFactoryBean instance, which is the EJB proxy object. We configure the proxy and set the myComponent property of the controller with the following configuration entry:

<bean id="myComponent"
		class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
	<property name="jndiName" value="ejb/myBean"/>
	<property name="businessInterface" value="com.mycom.MyComponent"/>
</bean>

<bean id="myController" class="com.mycom.myController">
	<property name="myComponent" ref="myComponent"/>
</bean>

A lot of work happens behind the scenes, courtesy of the Spring AOP framework, although you are not forced to work with AOP concepts to enjoy the results. The myComponent bean definition creates a proxy for the EJB, which implements the business method interface. The EJB local home is cached on startup, so there is only a single JNDI lookup. Each time the EJB is invoked, the proxy invokes the classname method on the local EJB and invokes the corresponding business method on the EJB.

The myController bean definition sets the myComponent property of the controller class to the EJB proxy.

Alternatively (and preferably in case of many such proxy definitions), consider using the <jee:local-slsb> configuration element in Spring’s “jee” namespace. The following example shows how to do so:

<jee:local-slsb id="myComponent" jndi-name="ejb/myBean"
		business-interface="com.mycom.MyComponent"/>

<bean id="myController" class="com.mycom.myController">
	<property name="myComponent" ref="myComponent"/>
</bean>

This EJB access mechanism delivers huge simplification of application code. The web tier code (or other EJB client code) has no dependence on the use of EJB. To replace this EJB reference with a POJO or a mock object or other test stub, we could change the myComponent bean definition without changing a line of Java code. Additionally, we have not had to write a single line of JNDI lookup or other EJB plumbing code as part of our application.

Benchmarks and experience in real applications indicate that the performance overhead of this approach (which involves reflective invocation of the target EJB) is minimal and is undetectable in typical use. Remember that we do not want to make fine-grained calls to EJBs anyway, as there is a cost associated with the EJB infrastructure in the application server.

There is one caveat with regards to the JNDI lookup. In a bean container, this class is normally best used as a singleton (there is no reason to make it a prototype). However, if that bean container pre-instantiates singletons (as do the various XML ApplicationContext variants), you can have a problem if the bean container is loaded before the EJB container loads the target EJB. That is because the JNDI lookup is performed in the init() method of this class and then cached, but the EJB has not been bound at the target location yet. The solution is to not pre-instantiate this factory object but to let it be created on first use. In the XML containers, you can control this by using the lazy-init attribute.

Although not of interest to the majority of Spring users, those doing programmatic AOP work with EJBs may want to look at LocalSlsbInvokerInterceptor.

Accessing Remote SLSBs

Accessing remote EJBs is essentially identical to accessing local EJBs, except that the SimpleRemoteStatelessSessionProxyFactoryBean or <jee:remote-slsb> configuration element is used. Of course, with or without Spring, remote invocation semantics apply: A call to a method on an object in another VM in another computer does sometimes have to be treated differently in terms of usage scenarios and failure handling.

Spring’s EJB client support adds one more advantage over the non-Spring approach. Normally, it is problematic for EJB client code to be easily switched back and forth between calling EJBs locally or remotely. This is because the remote interface methods must declare that they throw RemoteException, and client code must deal with this, while the local interface methods need not. Client code written for local EJBs that needs to be moved to remote EJBs typically has to be modified to add handling for the remote exceptions, and client code written for remote EJBs that needs to be moved to local EJBs can either stay the same but do a lot of unnecessary handling of remote exceptions or be modified to remove that code. With the Spring remote EJB proxy, you can instead not declare any thrown RemoteException in your Business Method Interface and implementing EJB code, have a remote interface that is identical (except that it does throw RemoteException), and rely on the proxy to dynamically treat the two interfaces as if they were the same. That is, client code does not have to deal with the checked RemoteException class. Any actual RemoteException that is thrown during the EJB invocation is re-thrown as the non-checked RemoteAccessException class, which is a subclass of RuntimeException. You can then switch the target service at will between a local EJB or remote EJB (or even plain Java object) implementation, without the client code knowing or caring. Of course, this is optional: Nothing stops you from declaring RemoteException in your business interface.

Accessing EJB 2.x SLSBs Versus EJB 3 SLSBs

Accessing EJB 2.x Session Beans and EJB 3 Session Beans through Spring is largely transparent. Spring’s EJB accessors, including the <jee:local-slsb> and <jee:remote-slsb> facilities, transparently adapt to the actual component at runtime. They handle a home interface if found (EJB 2.x style) or perform straight component invocations if no home interface is available (EJB 3 style).

Note: For EJB 3 Session Beans, you can effectively use a JndiObjectFactoryBean / <jee:jndi-lookup> as well, since fully usable component references are exposed for plain JNDI lookups there. Defining explicit <jee:local-slsb> or <jee:remote-slsb> lookups provides consistent and more explicit EJB access configuration.