You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
22 KiB
439 lines
22 KiB
<?xml version="1.0" encoding="UTF-8"?> |
|
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" |
|
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"> |
|
|
|
<chapter id="ejb"> |
|
<title>Enterprise JavaBeans (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> class is to load an |
|
<classname>ApplicationContext</classname> for use by an EJB, and 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>
|
|
|