j2ee‎ > ‎

EJB 3.0 Compatibility and Migration

posted Jun 20, 2010, 7:26 PM by Kuwon Kang

EJB 3.0 Compatibility and Migration

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 namedMigrationBean. The bean invokes a remote EJB 2.x stateless session bean named EJB2xBeanMigrationBean 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;

}

Here is the first part of the 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 toEJB2xHome 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 usePortableRemoteObject.narrow(). The PortableRemoteObject.narrow() method is used to cast to the correct type the Objectreturned 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 EJB2xRemotereference. 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);
}
}

In the last part of the class, 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;
}

Here is the 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 @LocalHomeannotation 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:

  1. If you haven't already done so, download the Java EE 5 SDK from the Java EE Downloads Page. Also be sure to have an installed version of the Java Platform Standard Edition (Java SE) 5 SDK.
  2. Download the sample package for the tip and extract its contents. You should now see the newly extracted directory as <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: Migrationand Adapted.
  3. To build and run the EJB 3.0-to-EJB 2.x compatibility example, change to the 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.homelocation attribute in the build.xml file in the selected directory to the directory in which you installed the Java EE 5 application server.
  4. Start the application server by entering the following command:<appserv_install>/bin/asadmin start-domain domain1

    where <appsrv_install> is where you installed the Java EE 5 application server.
  5. Enter the command: 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 bean

    You should see the following in the output for the EJB 2.x-to-EJB 3.0 compatibility example:

    runjavaclient:
    [java] Adapted id = duke

    The 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.

Comments