| | + title | Ref. /chapter[1]/title[1] |
| Original | Traduction | Accessing and implementing EJBs |
|
| + para | Ref. /chapter[1]/para[1] |
| Original | Traduction | 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 | Ref. /chapter[1]/para[2] |
| Original | Traduction | 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 java object) variants, without the client code client code having to
be changed. |
|
| + para | Ref. /chapter[1]/para[3] |
| Original | Traduction | 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. |
| sect1
| | + title | Ref. /chapter[1]/sect1[1]/title[1] |
| Original | Traduction | Accessing EJBs |
| sect2
| | + title | Ref. /chapter[1]/sect1[1]/sect2[1]/title[1] |
| Original | Traduction | Concepts |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[1]/para[1] |
| Original | Traduction | 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 | Ref. /chapter[1]/sect1[1]/sect2[1]/para[2] |
| Original | Traduction | 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 | Ref. /chapter[1]/sect1[1]/sect2[1]/itemizedlist[1]/listitem[1]/para[1] |
| Original | Traduction | Typically code using EJBs depends on Service Locator or
Business Delegate singletons, making it hard to test |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[1]/itemizedlist[1]/listitem[2]/para[1] |
| Original | Traduction | 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 | Ref. /chapter[1]/sect1[1]/sect2[1]/itemizedlist[1]/listitem[3]/para[1] |
| Original | Traduction | 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 | Ref. /chapter[1]/sect1[1]/sect2[1]/para[3] |
| Original | Traduction | The Spring approach is to allow the creation and use of proxy
objects, normally configured inside a Spring ApplicationContext or
BeanFactory, which act as code-less business delegates. You do not need
to write another Service Locator, another JNDI lookup, or duplicate
methods in a hand-coded Business Delegate unless youre adding real
value. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[1]/sect2[2]/title[1] |
| Original | Traduction | Accessing local SLSBs |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[1] |
| Original | Traduction | Assume that we have a web controller that needs to use a local
EJB. Well follow best practice and use the EJB Business Methods
Interface pattern, so that the EJBs local interface extends a non
EJB-specific business methods interface. Lets call this business
methods interface MyComponent. |
|
| + programlisting | Ref. /chapter[1]/sect1[1]/sect2[2]/programlisting[1] |
| Original | Traduction | public interface MyComponent {
...
} |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[2] |
| Original | Traduction | (One of the main reasons to 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
java object) implementation of the service if it makes sense to do so)
Of course well also need to implement the local home interface and
provide a bean implementation class that implements SessionBean and the
MyComponent business methods interface. Now the only Java coding well
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
will save the reference as an instance variable in the
controller: |
|
| + programlisting | Ref. /chapter[1]/sect1[1]/sect2[2]/programlisting[2] |
| Original | Traduction | private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
} |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[3] |
| Original | Traduction | 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 ApplicationContext or BeanFactory, we can in the
same context configure a
LocalStatelessSessionProxyFactoryBean instance, which
will be EJB proxy object. The configuration of the proxy, and setting of
the myComponent property of the controller is done
with a configuration entry such as: |
|
| + programlisting | Ref. /chapter[1]/sect1[1]/sect2[2]/programlisting[3] |
| Original | Traduction | <bean id="myComponent"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName"><value>myComponent</value></property>
<property name="businessInterface"><value>com.mycom.MyComponent</value></property>
</bean>
<bean id="myController" class = "com.mycom.myController">
<property name="myComponent"><ref bean="myComponent"/></property>
</bean> |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[4] |
| Original | Traduction | Theres a lot of magic happening behind the scenes, courtesy of
the Spring AOP framework, although you arent 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 theres
only a single JNDI lookup. Each time the EJB is invoked, the proxy
invokes the create() method on the local EJB and invokes the
corresponding business method on the EJB. |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[5] |
| Original | Traduction | The myController bean definition sets the
myController property of the controller class to this
proxy. |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[6] |
| Original | Traduction | 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 myComponent bean definition without changing a line of Java code.
Additionally, we havent had to write a single line of JNDI lookup or
other EJB plumbing code as part of our application. |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[7] |
| Original | Traduction | 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 undetectable in typical
use. Remember that we dont want to make fine-grained calls to EJBs
anyway, as theres a cost associated with the EJB infrastructure in the
application server. |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[8] |
| Original | Traduction | 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 XML ApplicationContext 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 init method of this class and 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
lazy-init attribute. |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[2]/para[9] |
| Original | Traduction | 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
LocalSlsbInvokerInterceptor. |
|
| | sect2
| | + title | Ref. /chapter[1]/sect1[1]/sect2[3]/title[1] |
| Original | Traduction | Accessing remote SLSBs |
|
| + para | Ref. /chapter[1]/sect1[1]/sect2[3]/para[1] |
| Original | Traduction | Accessing remote EJBs is essentially identical to accessing local
EJBs, except that the
SimpleRemoteStatelessSessionProxyFactoryBean 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 | Ref. /chapter[1]/sect1[1]/sect2[3]/para[2] |
| Original | Traduction | 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 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
RemoteException in your Business Method Interface and
implementing EJB code, have a remote interface which 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 will be re-thrown as the non-checked
RemoteAccessException class, which is a subclass of
RuntimeException. 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
RemoteExceptions in your business interface. |
|
| |
| | sect1
| | + title | Ref. /chapter[1]/sect1[2]/title[1] |
| Original | Traduction | Using Spring convenience EJB implementation classes |
|
| + para | Ref. /chapter[1]/sect1[2]/para[1] |
| Original | Traduction | Spring also 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 | Ref. /chapter[1]/sect1[2]/para[2] |
| Original | Traduction | To implement a Stateless or Stateful session bean, or Message Driven
bean, you derive your implementation class from
AbstractStatelessSessionBean,
AbstractStatefulSessionBean, and
AbstractMessageDrivenBean/AbstractJmsMessageDrivenBean,
respectively. |
|
| + para | Ref. /chapter[1]/sect1[2]/para[3] |
| Original | Traduction | Consider an example Stateless Session bean which actually delegates
the implementation to a plain java service object. We have the business
interface: |
|
| + programlisting | Ref. /chapter[1]/sect1[2]/programlisting[1] |
| Original | Traduction | public interface MyComponent {
public void myMethod(...);
...
} |
|
| + para | Ref. /chapter[1]/sect1[2]/para[4] |
| Original | Traduction | We have the plain java implementation object: | + programlisting | Ref. /chapter[1]/sect1[2]/para[4]/programlisting[1] |
| Original | Traduction | public class MyComponentImpl implements MyComponent {
public String myMethod(...) {
...
}
...
} |
|
|
|
| + para | Ref. /chapter[1]/sect1[2]/para[5] |
| Original | Traduction | And finally the Stateless Session Bean itself: |
|
| + programlisting | Ref. /chapter[1]/sect1[2]/programlisting[2] |
| Original | Traduction | public class MyComponentEJB implements extends AbstractStatelessSessionBean
implements MyComponent {
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 myMethod(...) {
return _myComp.myMethod(...);
}
...
} |
|
| + para | Ref. /chapter[1]/sect1[2]/para[6] |
| Original | Traduction | The Spring EJB support base classes will by default create and load
a BeanFactory (or in this case, its ApplicationContext subclass) 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
BeanFactoryLocator. The actual implementation of
BeanFactoryLocator used by default is
ContextJndiBeanFactoryLocator, which creates the
ApplicationContext from a resource locations specified as a JNDI
environment variable (in the case of the EJB classes, at
java:comp/env/ejb/BeanFactoryPath). If there is a need
to change the BeanFactory/ApplicationContext loading strategy, the default
BeanFactoryLocator implementation used may be overridden
by calling the setBeanFactoryLocator() method, either
in setSessionContext(), or in the actual constructor of
the EJB. Please see the JavaDocs for more details. |
|
| + para | Ref. /chapter[1]/sect1[2]/para[7] |
| Original | Traduction | 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 BeanFactory/ApplicationContext instance (which is the
normal case) will have to manually call
unloadBeanFactory() and
loadBeanFactory from ejbPassivate
and ejbActivate, respectively, to unload and reload the
BeanFactory on passivation and activation, since it can not be saved by
the EJB container. |
|
| + para | Ref. /chapter[1]/sect1[2]/para[8] |
| Original | Traduction | The default usage of
ContextJndiBeanFactoryLocator to load an
ApplicationContext for the use of the EJB is adequate for some situations.
However, it is problematic when the ApplicationContext is loading a number
of beans, or the initialization of those beans is time consuming or memory
intensive (such as a Hibernate SessionFactory initialization, for
example), since every EJB will have their own copy. In this case, the user
may want to override the default
ContextJndiBeanFactoryLocator usage and use another
BeanFactoryLocator variant, such as
ContextSingletonBeanFactoryLocatore,
which can load and use a shared BeanFactory or ApplicationContext to be
used by multiple EJBs or other clients. Doing this is relatively simple,
by adding code similar to this to the EJB: |
|
| + programlisting | Ref. /chapter[1]/sect1[2]/programlisting[3] |
| Original | Traduction | /**
* 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);
} |
|
| + para | Ref. /chapter[1]/sect1[2]/para[9] |
| Original | Traduction | Please see the respective JavaDocs for
BeanFactoryLocator and
ContextSingletonBeanFactoryLocatore
for more information on their usage. |
|
| |
|