Tuesday Aug 21, 2007
Tuesday Aug 21, 2007
By Ken Saks
The Enterprise JavaBeans (EJB) 3.0 specification introduced a new, vastly simpler, API for implementing and accessing session beans. Many developers have questions about how applications written to this new API can interact with legacy EJB applications, or how legacy applications can interact with the new API.
It would be impractical to require developers to entirely rewrite all existing EJB applications in order to take advantage of any of the new capabilities. In some cases, developers plan to upgrade their applications, but would prefer to do it using a phased approach. In other cases they prefer to write new EJB 3.0 applications, but would still like to take advantage of their existing application's services. The EJB 3.0 specification makes both of these strategies possible. This tip explains how.
A sample application package accompanies the tip. The code examples in the tip are taken from the source code of the sample (which is included in the package). The sample uses the Java EE 5 SDK. You can download the Java EE 5 SDK from the Java EE Downloads page
EJB 3.0-to-EJB 2.x Compatibility
Let's first look at an example of an application written to the
simplified EJB 3.0 API calling a legacy EJB application. In the
source code for the sample that accompanies this tip you'll find
an EJB 3.0 stateless session bean named MigrationBean. The bean
invokes a remote EJB 2.x stateless session bean named EJB2xBean.
MigrationBean could either be an entirely new bean or an
existing bean that was recoded to use the EJB 3.0 API. For
simplicity both beans in this example are packaged in the same
archive, but that is not required. Here are the EJB 2.x home and
remote interfaces for EJB2xBean:
public interface EJB2xHome extends EJBHome { public EJB2xRemote create() throws CreateException, RemoteException; } public interface EJB2xRemote extends EJBObject { public void foo() throws RemoteException; } MigrationBean class: @Stateless public class MigrationBean implements MigrationRemote { // Inject a reference to the Home interface // of the EJB 2.x bean. @EJB private EJB2xHome ejb2xHome; private EJB2xRemote ejb2x;
MigrationBean uses the @EJB annotation to inject a reference to
EJB2xHome. The @EJB annotation was introduced as part of the
EJB 3.0 specification. However, it was designed to be used with
the older-style EJB 2.x home view in addition to the EJB 3.0
business interface view. Whenever the EJB container instantiates
MigrationBean, it injects a reference to EJB2xHome in the
ejb2xHome data member.
An important benefit of using the @EJB annotation with a
2.x home interface is that it frees you from having to use
PortableRemoteObject.narrow(). The PortableRemoteObject.narrow()
method is used to cast to the correct type the Object returned
when you lookup the home interface. In EJB 3.0 the injected home
reference can be used without any special casting.
The MigrationBean class defines a @PostConstruct method that
uses the home reference to create an EJB2xRemote reference. The
class then stores the result in another data member.
@PostConstruct private void initialization() { try { ejb2x = ejb2xHome.create(); } catch(Exception e) { throw new EJBException(e); } } MigrationBean invokes the EJB2x business method within its own business method. public void bar() { try { // Call operation on legacy EJB. ejb2x.foo(); } catch(Exception e) { throw new EJBException(e); } } All standard EJB services such as security and transaction propagation work as expected between the two beans.
Note that using an @EJB annotation to access EJB 2.x beans is
not limited to EJB components. You can use the same approach to
access a legacy bean from other types of components in
a Java EE 5 application, such as a servlet or an application
client. Also, even though this example used a remote EJB 2.x
bean, you can use the approach to access a local EJB 2.x bean as
well.
EJB 2.x-to-EJB 3.0 Compatibility
In the first example, you saw how an EJB component written using the new EJB 3.0 API could be a client of a legacy EJB component. Now let's look at the reverse case. Here you'll see how a legacy component that is coded using the EJB 2.x client API can access a session bean that is implemented using the EJB 3.0 simplified API. The idea here is to use the new EJB without making any changes to the existing EJB client code.
In this second example, a stateful session bean named
AdaptedBean is implemented using the simplified EJB 3.0 API. The
bean exposes a 2.x remote home interface to a standalone Java
client. Here are the home and remote interfaces for AdaptedBean:
public interface AdaptedHome extends EJBHome { public AdaptedRemote create(String id) throws CreateException, RemoteException; } public interface AdaptedRemote extends EJBObject { public String getId() throws RemoteException; } AdaptedBean class : @Stateful @RemoteHome(AdaptedHome.class) public class AdaptedBean { private String myId = "unknown"; @Init public void create(String id) { myId = id; } public String getId() { return myId; } }
Notice that the bean class is implemented using the POJO-like
simplifications of EJB 3.0. However, it uses the
@RemoteHome annotation to exposes an EJB 2.x style remote home
interface. This annotation tells the EJB container that the
bean has a home interface named AdaptedHome and a remote
interface named AdaptedRemote. You don't need a separate
annotation to specify the AdaptedRemote interface. That's
because the container can derive it from the signature of the
AdaptedHome's create() method. If the bean exposed a local home
interface, it would use the @LocalHome annotation instead.
The other important annotation in the class is @Init. The
EJB 2.x view always defines a create method, so you must define
a method on the bean class to handle the create operation. The
@Init annotation tells the EJB container which method that is.
The method can have any name, but its method parameters must
match the create method in the corresponding home interface.
Here is the standalone client code for accessing AdaptedBean:
try { InitialContext ic = new InitialContext(); Object o = ic.lookup(AdaptedHome.class.getName()); AdaptedHome adaptedHome = (AdaptedHome) PortableRemoteObject.narrow(o, AdaptedHome.class); AdaptedRemote adapted = adaptedHome.create("duke"); System.out.println("Adapted id = " + adapted.getId()); } catch(Exception e) { ... } Notice that the client code uses the standard EJB 2.x client programming model. It does not need to know that the target bean is implemented with EJB 3.0.
Running the Sample Code
A sample package accompanies this tip. To install and run the sample:
<sample_install_dir>/ttfeb2007ejbmigration, where
<sample_install_dir> is the directory where you installed the
sample package. For example, if you extracted the contents to
C:\ on a Windows machine, then your newly created directory
should be at C:\ttfeb2007ejbmigration. Below this directory you
should see two subdirectories: Migration and Adapted.Migration directory. To build and run
the EJB 2.x-to-EJB 3.0 compatibility example, change to the
Adapted directory. Then set the javaee.home location
attribute in the build.xml file in the selected directory to
the directory in which you installed the Java EE 5
application server. <appserv_install>/bin/asadmin start-domain domain1 <appsrv_install> is where you installed the Java EE 5 application server. ant all
This builds the sample application then deploys and executes it.
You should see the following in the output for the EJB 3.0-to-EJB 2.x compatibility example :
runappclient: [exec] Successfully called EJB 3.0 beanYou should see the following in the output for the EJB 2.x-to-EJB 3.0 compatibility example:
runjavaclient: [java] Adapted id = dukeThe application is then automatically undeployed and its build resources erased.
Summary
The EJB 3.0 specification greatly simplifies the session bean API. But it does so with the understanding that there is a large investment in EJB applications built with earlier versions of the specification. For this reason, a goal of EJB 3.0 is to ensure smooth interoperability between new and existing applications. If you are a developer using EJB 3.0, you have a lot flexibility in deciding whether to migrate legacy applications to the new API or to take advantage of those legacy applications from new components.
About the Author
Ken Saks is the lead architect for the EJB 3.0 container in the Java EE 5 SDK. He has been a member of the Enterprise Java group at Sun Microsystems since 1999.