436 lines
22 KiB
XML
436 lines
22 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||
|
||
<chapter id="ejb">
|
||
<title>Enterprise Java Beans (EJB) integration</title>
|
||
|
||
<section id="ejb-introduction">
|
||
<title>Introduction</title>
|
||
<para>
|
||
As a lightweight container, Spring is often considered an EJB
|
||
replacement. We do believe that for many if not most applications and use
|
||
cases, Spring as a container, combined with its rich supporting
|
||
functionality in the area of transactions, ORM and JDBC access, is a better
|
||
choice than implementing equivalent functionality via an EJB container and
|
||
EJBs.
|
||
</para>
|
||
<para>
|
||
However, it is important to note that using Spring does not prevent
|
||
you from using EJBs. In fact, Spring makes it much easier to access EJBs and
|
||
implement EJBs and functionality within them. Additionally, using Spring to
|
||
access services provided by EJBs allows the implementation of those services
|
||
to later transparently be switched between local EJB, remote EJB, or POJO
|
||
(plain old Java object) variants, without the client code having to
|
||
be changed.
|
||
</para>
|
||
<para>
|
||
In this chapter, we look at how Spring can help you access and
|
||
implement EJBs. Spring provides particular value when accessing stateless
|
||
session beans (SLSBs), so we'll begin by discussing this.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="ejb-access">
|
||
<title>Accessing EJBs</title>
|
||
|
||
<section id="ejb-access-concepts">
|
||
<title>Concepts</title>
|
||
<para>
|
||
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, 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.
|
||
</para>
|
||
<para>
|
||
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. For example:
|
||
</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>
|
||
Typically code using EJBs depends on Service Locator or
|
||
Business Delegate singletons, making it hard to test.
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
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.
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
Implementing the Business Delegate pattern typically results
|
||
in significant code duplication, where we have to write numerous
|
||
methods that simply call the same method on the EJB.
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>
|
||
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 do not need to write another Service Locator, another
|
||
JNDI lookup, or duplicate methods in a hand-coded Business Delegate unless
|
||
you are actually adding real value in such code.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="ejb-access-local">
|
||
<title>Accessing local SLSBs</title>
|
||
<para>
|
||
Assume that we have a web controller that needs to use a local
|
||
EJB. We’ll 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. Let’s call this business
|
||
methods interface <classname>MyComponent</classname>.
|
||
</para>
|
||
<programlisting language="java"><![CDATA[public interface MyComponent {
|
||
...
|
||
}]]></programlisting>
|
||
<para>
|
||
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.
|
||
Of course we’ll also need to implement the local home interface and
|
||
provide an implementation class that implements <classname>SessionBean</classname>
|
||
and the <classname>MyComponent</classname> business methods interface. Now the
|
||
only Java coding we’ll need to do to hook up our web tier controller to the
|
||
EJB implementation is to expose a setter method of type <classname>MyComponent</classname>
|
||
on the controller. This will save the reference as an instance variable in the
|
||
controller:
|
||
</para>
|
||
<programlisting language="java"><![CDATA[private MyComponent myComponent;
|
||
|
||
public void setMyComponent(MyComponent myComponent) {
|
||
this.myComponent = myComponent;
|
||
}]]></programlisting>
|
||
<para>
|
||
We can subsequently use this instance variable in any business
|
||
method in the controller. Now assuming we are obtaining our controller
|
||
object out of a Spring container, we can (in the same context) configure a
|
||
<classname>LocalStatelessSessionProxyFactoryBean</classname> instance, which
|
||
will be the EJB proxy object. The configuration of the proxy, and setting of
|
||
the <literal>myComponent</literal> property of the controller is done
|
||
with a configuration entry such as:
|
||
</para>
|
||
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
||
<para>
|
||
There’s a lot of work happening behind the scenes, courtesy of
|
||
the Spring AOP framework, although you aren’t forced to work with AOP
|
||
concepts to enjoy the results. The <literal>myComponent</literal> bean
|
||
definition creates a proxy for the EJB, which implements the business
|
||
method interface. The EJB local home is cached on startup, so there’s
|
||
only a single JNDI lookup. Each time the EJB is invoked, the proxy
|
||
invokes the <literal>classname</literal> method on the local EJB and
|
||
invokes the corresponding business method on the EJB.
|
||
</para>
|
||
<para>
|
||
The <literal>myController</literal> bean definition sets the
|
||
<literal>myComponent</literal> property of the controller class to the
|
||
EJB proxy.
|
||
</para>
|
||
<para>
|
||
Alternatively (and preferably in case of many such proxy definitions),
|
||
consider using the <literal><jee:local-slsb></literal>
|
||
configuration element in Spring's "jee" namespace:
|
||
</para>
|
||
<programlisting language="xml"><![CDATA[<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>]]></programlisting>
|
||
<para>
|
||
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. If we want to replace this EJB reference
|
||
with a POJO or a mock object or other test stub, we could simply change
|
||
the <literal>myComponent</literal> bean definition without changing a
|
||
line of Java code. Additionally, we haven’t had to write a single line of
|
||
JNDI lookup or other EJB plumbing code as part of our application.
|
||
</para>
|
||
<para>
|
||
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 typically undetectable
|
||
in typical use. Remember that we don’t want to make fine-grained calls
|
||
to EJBs anyway, as there’s a cost associated with the EJB infrastructure
|
||
in the application server.
|
||
</para>
|
||
<para>
|
||
There is one caveat with regards to the JNDI lookup. In a bean
|
||
container, this class is normally best used as a singleton (there simply
|
||
is no reason to make it a prototype). However, if that bean container
|
||
pre-instantiates singletons (as do the various XML
|
||
<classname>ApplicationContext</classname> variants)
|
||
you may have a problem if the bean container is loaded before the EJB
|
||
container loads the target EJB. That is because the JNDI lookup will be
|
||
performed in the <literal>init()</literal> method of this class and then
|
||
cached, but the EJB will not have been bound at the target location yet.
|
||
The solution is to not pre-instantiate this factory object, but allow it
|
||
to be created on first use. In the XML containers, this is controlled via
|
||
the <literal>lazy-init</literal> attribute.
|
||
</para>
|
||
<para>
|
||
Although this will not be of interest to the majority of Spring
|
||
users, those doing programmatic AOP work with EJBs may want to look at
|
||
<classname>LocalSlsbInvokerInterceptor</classname>.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="ejb-access-remote">
|
||
<title>Accessing remote SLSBs</title>
|
||
<para>
|
||
Accessing remote EJBs is essentially identical to accessing local
|
||
EJBs, except that the
|
||
<classname>SimpleRemoteStatelessSessionProxyFactoryBean</classname> or
|
||
<literal><jee:remote-slsb></literal> 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.
|
||
</para>
|
||
<para>
|
||
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 <classname>RemoteException</classname>, and client code must deal
|
||
with this, while the local interface methods don't. Client code
|
||
written for local EJBs which 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 which needs to be moved to local
|
||
EJBs, can either stay the same but do a lot of unnecessary handling of
|
||
remote exceptions, or needs to be modified to remove that code. With the
|
||
Spring remote EJB proxy, you can instead not declare any thrown
|
||
<classname>RemoteException</classname> in your Business Method Interface and
|
||
implementing EJB code, have a remote interface which is identical except
|
||
that it does throw <classname>RemoteException</classname>, 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
|
||
<classname>RemoteException</classname> class. Any actual
|
||
<classname>RemoteException</classname> that is thrown during the EJB
|
||
invocation will be re-thrown as the non-checked
|
||
<classname>RemoteAccessException</classname> class, which is a subclass of
|
||
<classname>RuntimeException</classname>. The target service can then be
|
||
switched 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; there is nothing stopping you from declaring
|
||
<classname>RemoteExceptions</classname> in your business interface.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="ejb-access-ejb2-ejb3">
|
||
<title>Accessing EJB 2.x SLSBs versus EJB 3 SLSBs</title>
|
||
<para>
|
||
Accessing EJB 2.x Session Beans and EJB 3 Session Beans via Spring
|
||
is largely transparent. Spring's EJB accessors, including the
|
||
<literal><jee:local-slsb></literal> and <literal><jee:remote-slsb></literal>
|
||
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).
|
||
</para>
|
||
<para>
|
||
Note: For EJB 3 Session Beans, you could effectively use a
|
||
<classname>JndiObjectFactoryBean</classname> / <literal><jee:jndi-lookup></literal>
|
||
as well, since fully usable component references are exposed for plain
|
||
JNDI lookups there. Defining explicit <literal><jee:local-slsb></literal>
|
||
/ <literal><jee:remote-slsb></literal> lookups simply provides
|
||
consistent and more explicit EJB access configuration.
|
||
</para>
|
||
</section>
|
||
</section>
|
||
|
||
<section id="ejb-implementation">
|
||
<title>Using Spring's EJB implementation support classes</title>
|
||
|
||
<section id="ejb-implementation-ejb2">
|
||
<title>EJB 2.x base classes</title>
|
||
<para>
|
||
Spring provides convenience classes to help you implement EJBs.
|
||
These are designed to encourage the good practice of putting business
|
||
logic behind EJBs in POJOs, leaving EJBs responsible for transaction
|
||
demarcation and (optionally) remoting.
|
||
</para>
|
||
<para>
|
||
To implement a Stateless or Stateful session bean, or a Message Driven
|
||
bean, you need only derive your implementation class from
|
||
<classname>AbstractStatelessSessionBean</classname>,
|
||
<classname>AbstractStatefulSessionBean</classname>, and
|
||
<classname>AbstractMessageDrivenBean</classname>/<classname>AbstractJmsMessageDrivenBean</classname>,
|
||
respectively.
|
||
</para>
|
||
<para>
|
||
Consider an example Stateless Session bean which actually delegates
|
||
the implementation to a plain java service object. We have the business interface:
|
||
</para>
|
||
<programlisting language="java"><![CDATA[public interface MyComponent {
|
||
public void myMethod(...);
|
||
...
|
||
}]]></programlisting>
|
||
<para>We also have the plain Java implementation object:</para>
|
||
<programlisting language="java"><![CDATA[public class MyComponentImpl implements MyComponent {
|
||
public String myMethod(...) {
|
||
...
|
||
}
|
||
...
|
||
}]]></programlisting>
|
||
<para>And finally the Stateless Session Bean itself:</para>
|
||
<programlisting language="java"><![CDATA[public class MyFacadeEJB extends AbstractStatelessSessionBean
|
||
implements MyFacadeLocal {
|
||
|
||
private MyComponent myComp;
|
||
|
||
/**
|
||
* Obtain our POJO service object from the BeanFactory/ApplicationContext
|
||
* @see org.springframework.ejb.support.AbstractStatelessSessionBean#onEjbCreate()
|
||
*/
|
||
protected void onEjbCreate() throws CreateException {
|
||
myComp = (MyComponent) getBeanFactory().getBean(
|
||
ServicesConstants.CONTEXT_MYCOMP_ID);
|
||
}
|
||
|
||
// for business method, delegate to POJO service impl.
|
||
public String myFacadeMethod(...) {
|
||
return myComp.myMethod(...);
|
||
}
|
||
...
|
||
}]]></programlisting>
|
||
<para>
|
||
The Spring EJB support base classes will by default create and load
|
||
a Spring IoC container as part of their lifecycle, which is then available
|
||
to the EJB (for example, as used in the code above to obtain the POJO
|
||
service object). The loading is done via a strategy object which is a subclass of
|
||
<classname>BeanFactoryLocator</classname>. The actual implementation of
|
||
<classname>BeanFactoryLocator</classname> used by default is
|
||
<classname>ContextJndiBeanFactoryLocator</classname>, which creates the
|
||
ApplicationContext from a resource locations specified as a JNDI
|
||
environment variable (in the case of the EJB classes, at
|
||
<literal>java:comp/env/ejb/BeanFactoryPath</literal>). If there is a need
|
||
to change the BeanFactory/ApplicationContext loading strategy, the default
|
||
<classname>BeanFactoryLocator</classname> implementation used may be overridden
|
||
by calling the <literal>setBeanFactoryLocator()</literal> method, either
|
||
in <literal>setSessionContext()</literal>, or in the actual constructor of
|
||
the EJB. Please see the Javadocs for more details.
|
||
</para>
|
||
<para>
|
||
As described in the Javadocs, Stateful Session beans expecting to be
|
||
passivated and reactivated as part of their lifecycle, and which use a
|
||
non-serializable container instance (which is the normal case) will have
|
||
to manually call <literal>unloadBeanFactory()</literal> and
|
||
<literal>loadBeanFactory</literal> from <literal>ejbPassivate</literal>
|
||
and <literal>ejbActivate</literal>, respectively, to unload and reload the
|
||
BeanFactory on passivation and activation, since it can not be saved by
|
||
the EJB container.
|
||
</para>
|
||
<para>
|
||
The default behavior of the <classname>ContextJndiBeanFactoryLocator</classname>
|
||
classes which is to load an <classname>ApplicationContext</classname> for the
|
||
use of the EJB is adequate for some situations. However, it is problematic when
|
||
the <classname>ApplicationContext</classname> is loading a number
|
||
of beans, or the initialization of those beans is time consuming or memory
|
||
intensive (such as a Hibernate <classname>SessionFactory</classname> initialization, for
|
||
example), since every EJB will have their own copy. In this case, the user
|
||
may want to override the default <classname>ContextJndiBeanFactoryLocator</classname>
|
||
usage and use another <classname>BeanFactoryLocator</classname> variant, such as the
|
||
<classname>ContextSingletonBeanFactoryLocator</classname> which can load and use a
|
||
shared container to be used by multiple EJBs or other clients. Doing this is relatively
|
||
simple, by adding code similar to this to the EJB:
|
||
</para>
|
||
<programlisting language="java"><![CDATA[ /**
|
||
* Override default BeanFactoryLocator implementation
|
||
* @see javax.ejb.SessionBean#setSessionContext(javax.ejb.SessionContext)
|
||
*/
|
||
public void setSessionContext(SessionContext sessionContext) {
|
||
super.setSessionContext(sessionContext);
|
||
setBeanFactoryLocator(ContextSingletonBeanFactoryLocator.getInstance());
|
||
setBeanFactoryLocatorKey(ServicesConstants.PRIMARY_CONTEXT_ID);
|
||
}]]></programlisting>
|
||
<para>
|
||
You would then need to create a bean definition file named <literal>beanRefContext.xml</literal>.
|
||
This file defines all bean factories (usually in the form of application contexts) that may be used
|
||
in the EJB. In many cases, this file will only contain a single bean definition such as this (where
|
||
<literal>businessApplicationContext.xml</literal> contains the bean definitions for all business
|
||
service POJOs):
|
||
</para>
|
||
<programlisting language="xml"><![CDATA[<beans>
|
||
<bean id="businessBeanFactory" class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||
<constructor-arg value="businessApplicationContext.xml" />
|
||
</bean>
|
||
</beans>]]></programlisting>
|
||
<para>
|
||
In the above example, the <literal>ServicesConstants.PRIMARY_CONTEXT_ID</literal> constant
|
||
would be defined as follows:
|
||
</para>
|
||
<programlisting language="java"><![CDATA[public static final String ServicesConstants.PRIMARY_CONTEXT_ID = "businessBeanFactory";]]></programlisting>
|
||
<para>
|
||
Please see the respective Javadocs for the <classname>BeanFactoryLocator</classname> and
|
||
<classname>ContextSingletonBeanFactoryLocator</classname> classes for more information on
|
||
their usage.
|
||
</para>
|
||
</section>
|
||
|
||
<section id="ejb-implementation-ejb3">
|
||
<title>EJB 3 injection interceptor</title>
|
||
<para>
|
||
For EJB 3 Session Beans and Message-Driven Beans, Spring provides a convenient
|
||
interceptor that resolves Spring 2.5's <literal>@Autowired</literal> annotation
|
||
in the EJB component class:
|
||
<classname>org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor</classname>.
|
||
This interceptor can be applied through an <code>@Interceptors</code> annotation
|
||
in the EJB component class, or through an <literal>interceptor-binding</literal>
|
||
XML element in the EJB deployment descriptor.
|
||
</para>
|
||
<programlisting language="java"><![CDATA[@Stateless
|
||
@Interceptors(SpringBeanAutowiringInterceptor.class)
|
||
public class MyFacadeEJB implements MyFacadeLocal {
|
||
|
||
// automatically injected with a matching Spring bean
|
||
@Autowired
|
||
private MyComponent myComp;
|
||
|
||
// for business method, delegate to POJO service impl.
|
||
public String myFacadeMethod(...) {
|
||
return myComp.myMethod(...);
|
||
}
|
||
...
|
||
}]]></programlisting>
|
||
<para>
|
||
<classname>SpringBeanAutowiringInterceptor</classname> by default obtains target
|
||
beans from a <classname>ContextSingletonBeanFactoryLocator</classname>, with the
|
||
context defined in a bean definition file named <literal>beanRefContext.xml</literal>.
|
||
By default, a single context definition is expected, which is obtained by type rather
|
||
than by name. However, if you need to choose between multiple context definitions,
|
||
a specific locator key is required. The locator key (i.e. the name of the context
|
||
definition in <literal>beanRefContext.xml</literal>) can be explicitly specified
|
||
either through overriding the <literal>getBeanFactoryLocatorKey</literal> method
|
||
in a custom <classname>SpringBeanAutowiringInterceptor</classname> subclass.
|
||
</para>
|
||
<para>
|
||
Alternatively, consider overriding <classname>SpringBeanAutowiringInterceptor</classname>'s
|
||
<literal>getBeanFactory</literal> method, e.g. obtaining a shared
|
||
<interfacename>ApplicationContext</interfacename> from a custom holder class.
|
||
</para>
|
||
</section>
|
||
|
||
</section>
|
||
|
||
</chapter>
|