In-Depth
Migrate J2EE Applications for EJB 3.0
The EJB 3.0 programming model has been simplified significantly. Learn migration approaches that help ferret out difficult issues you're likely to face when migrating J2EE apps.
- By Debu Panda
- July 21, 2005
The programming model for Enterprise JavaBeans (EJBs) has been simplified dramatically in EJB 3.0 and is being hailed by Java developers as the new standard of server-side business logic programming. Meanwhile, the existence of thousands of J2EE applications written with earlier versions of the EJB API has raised concerns about both the interoperability of EJB 3.0 with these applications and migrating the applications to use EJB 3.0.
Major application server vendors that already provide EJB 3.0 features will continue to support EJB 2.x in the new EJB 3.0 container. This support means that applications written with EJB 2.x will continue to run without any change whatsoever. However, some organizations will certainly want to migrate their applications to use the EJB 3.0 API to improve maintainability by simplifying their code and take advantage of the performance and scalability benefits of the persistence API.
Let's look at some of the issues relevant to migrating applications to EJB 3.0. The topics discussed here compose a subset of all possible issues a migration effort will encounter, and because the spec is not finalized other issues may still arise.
The main goals of EJB 3.0 are to simplify the programming model and to define a persistence API for the Java platform. Some general changes to the EJB spec that will simplify life for EJB developers are: EJB artifacts are Plain Old Java Objects (POJOs), XML descriptors are no longer necessary, annotations may be used instead, defaults are assumed whenever possible, unnecessary artifacts and life-cycle methods are optional, and the client view is simplified by dependency injection.
Standardization of the POJO persistence model (such as the one used by Oracle TopLink and JBoss Hibernate) within the context of J2EE finally provides users with the inheritance and polymorphism that have until now been available only outside the container or in proprietary products. Furthermore, the POJO entity beans can now be used and tested both inside and outside the EJB container.
Migrate Session Beans
The main changes in EJB 3.0 session beans simplify development by making beans POJOs that use annotations instead of XML descriptors and dependency injection instead of complex JNDI lookup. Hence, it is very easy to migrate the session beans to use the EJB 3.0 programming model. The changes in EJB 3.0 sessions fall into two categories: the server side and the client side. By client side we mean using resources, EJBs, and so on from other EJBs. Using EJBs from other types of clients outside the EJB container will be standardized with the J2EE 5.0 specification.
Some application servers allow migration of server-side components without affecting the clients, thus allowing incremental migration of applications.
There are some changes that need to be made to EJB 2.x session beans to migrate to EJB 3.0. In EJB 3.0 the remote and local interfaces do not have to implement javax.ejb.EJBObject or javax.ejb.EJBLocalObject. The component interface can become a business interface, and @Remote annotations can be used to mark remote interfaces.
Let's look at two examples of the same EJB 2.x interface, before and after migration to EJB 3.0. RemoteExceptions are no longer thrown by the methods on the EJB 2.x remote interface:
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface HelloWorld
extends EJBObject {
void sayHello(String name)
throws RemoteException;
}
and the EJB 3.0 remote interface:
import javax.ejb.Remote;
@Remote
public interface HelloWorld {
void sayHello(String name);
}
Bean classes do not need to implement javax.ejb.SessionBean—rather, their business interfaces. Life-cycle methods that are not required do not need to be present on the bean class, and callbacks may be indicated by annotations. Here is the EJB 2.x stateless session bean:
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloWorldBean
implements SessionBean {
public void ejbCreate() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
public void setSessionContext(
SessionContext ctx) {}
public void sayHello(
String name) {
System.out.println(
"Hello " + name);
}
}
and here is the stateless session bean migrated to EJB 3.0:
import javax.ejb.Stateless;
@Stateless
public class HelloWorldBean
implements HelloWorld {
public void sayHello(
String name) {
System.out.println(
"Hello " + name);
}
}
Stateful session bean ejbCreate() methods become business methods used at initialization time. Removal methods are annotated with @Remove. Optionally, you can migrate session bean code to use annotations for transactions and security.
EJB 3.0 simplifies the use of resources and EJB references by making use of the principle of dependency injection. Injection of resources or references can occur through either annotation of the injection target or specification of the target in the ejb-jar.xml descriptor. Table 1 lists some of the differences.
For example, if you are using the CartEJB in another EJB named OrderBean, with EJB 2.x you have to make a reference to the CartEJB in the deployment descriptor using ejb-ref:
<ejb-ref-name>MyCart
</ejb-ref-name>
<ejb-ref-type>Session
</ejb-ref-type>
<home>CartHome</home>
<remote>Cart</remote>
Do a lookup of the home interface for the CartEJB, using JNDI, and create an instance of the CartEJB:
Object homeObject =
context.lookup(
"java:comp/env/MyCart");
CartHome home = (
CartHome)PortableRemoteObject.
narrow(homeObject,
CartHome.class);
Cart cart =(
Cart)PortableRemoteObject.
narrow(home.create(),
Cart.class);
cart.addItem("Item1");
After migration to the EJB 3.0 injection pattern, this code can become much simpler and would look like this:
@EJB Cart cart;
public void addItems() {
cart.addItem("Item1");
}
A session bean can also be used as a façade for CMP entity beans, giving rise to a migration strategy for moving CMP entity beans to the EJB 3.0 persistence API.
More Migrations
Migrating Message-Driven Beans (MDBs) to use EJB 3.0 is by far the easiest migration task. In EJB 3.0, MDBs do not have to implement the javax.ejb.MessageDriven interface but can instead be annotated with @MessageDriven. Resources and EJB references can be injected into MDBs in the same way as session beans. Similarly, you can inject the context (MessageDrivenContext for MDB) into the message-driven bean. Table 2 summarizes the changes in MDB between EJB 2.x and EJB 3.0.
Persistence is one of the greatest challenges facing Java developers, compounded further by the lack of a standard persistence API for the J2EE platform. J2EE applications typically use one of these persistence choices: EJB 2.x CMP entity beans; a POJO persistence framework such as Oracle TopLink, JBoss Hibernate, or another custom O/R mapping framework; Data Access Objects with JDBC; and Java Data Objects (JDO).
Of these options, migrating EJB 2.x CMP entity beans presents the most interesting case because the change in programming model brings the EJB 3.0 persistence API to the same level of "POJOness" as other persistence solutions, which is worth taking some time to explore.
EJB 2.x CMP entity beans represent coarse-grained data objects and follow the distributed component model that includes security, transaction, and concurrency management. Because of these facilities they also carry the container management costs associated with it. EJB 3.0 entities are lightweight objects that can model fine-grained data. The difference between the 2.x and 3.0 models extends beyond simply converting the classes but should be considered at the modeling domain level. This difference does not mean that they cannot be converted, but that they should be migrated with some forethought and consideration of how they might best be remodeled.
Let's take a detailed look at some of the issues you will encounter as you migrate an EJB 2.x CMP entity to use the new EJB persistence API.
Remodel your entities. With EJB 3.0 entities becoming POJOs, it is probably the best idea to remodel your entities and leverage richer OO modeling benefits such as inheritance, polymorphism, and so on. The details of remodeling of entity beans vary from one application to another and are beyond the scope of this article.
Data transfer objects. The Data Transfer Object (DTO) pattern is a common pattern that allows data from entities to be accessible directly to nonlocal clients outside a transaction. It involved creating simple container objects to hold the entity data and transferring them to any tier as necessary. The EJB 3.0 specification makes the DTO pattern unnecessary because entities can already be shipped wherever they are required. The EntityManager API that is used for CRUD operations for entities allows detachment and merging of detached objects. An object can be detached from persistence storage and can be sent to the client, and then the changes can be merged/synchronized using the EntityManager.merge() method after the client sends it back to the server making updates to the object locally.
If DTOs already exist in an application, it is quite likely that they are the shortest path to making the EJB 2.x entities POJOs. The DTO becomes the candidate for the new entity bean if you simply add the logic into it and annotate/map it accordingly.
More Issues
Migration of component interfaces. Business interfaces are optional for EJB 3.0 entity beans, and if they exist, they are only regular Java interfaces (POJIs). You can either convert existing local or remote component interfaces to regular Java interfaces by no longer extending the javax.ejb.EJBObject or EJBLocalObject interfaces, or discard them entirely.
Conversion to POJO. EJB 3.0 entity beans no longer expect that the bean class implements the javax.ejb.EntityBean interface. They may extend any class and implement virtually any interface. The EntityBean interface can be removed from the implements clause. You have to convert the entity class and the get/set access methods from abstract to concrete. The previously virtual container-managed fields (cmp-fields) should have concrete implementations added to them.
EntityContext. Lightweight entities now inherit their context from the calling method and have no context of their own. You must remove the EntityContext and replace use of the context by any methods with suitable alternatives. For example, the getPrimaryKey() method would be replaced with a method on the bean or business interface that exposes the primary key to the user.
Home interfaces. The typed local and remote home interfaces in EJB 2.x are replaced by functionality in the EntityManager. Local and remote home interfaces should no longer be used, and clients that look them up should be converted to call the EntityManager directly to obtain or access the bean.
There are four different types of methods in the home interface: create methods, remove methods, home methods, and finders. The create methods can be converted easily into constructors that initialize the state information on the instance. The remove methods should be redirected to the EntityManager. The home methods may remain as they are because they can be invoked on any instance, regardless of the state of the instance. I'll discuss Finder methods later.
Optionally, you can keep the local home interface that clients use as a helper class for finders and factory style creation to minimize client code change and produce a nice clean usage pattern for your entity bean.
Migration of life-cycle methods. EJB 2.x has a strict requirement to implement the EntityBean interface that defined the complete suite of life-cycle methods. Some of these life-cycle events no longer apply. Table 3 suggests strategies for migrating existing implementations of these methods.
Migration of O/R mapping. Before EJB 3.0, O/R mappings were always in the domain of the vendor and, as such, were typically stored in a vendor-specific deployment descriptor. Now that the O/R mappings have been integrated into the EJB standard, they can be defined either as annotations on the bean class or in a standard XML file. Most vendors should be providing ways to translate their mappings into the standard EJB mappings, either through a migration tool or from their graphical mapping editors.
Exception handling. Exceptions are in a different Java package and are not checked; the same CreateException, RemoveException, and FinderException catch phrases do not apply.
Even More Issues
Migration of finder methods. In EJB 2.x, the finder methods were defined in a home interface, and the EJB QL query for the finder was specified in the deployment descriptor. There are a few options for migrating EJB 2.x entity bean finders to EJB 3.0. The finder method can become a method in any class, and the EJB QL can be incorporated into either a named query or a dynamic query. Here is a finder method example defined in EJB 2.x in the deployment descriptor:
<query>
<query-method>
<method-name>findAllByName
</method-name>
<method-params>
<method-param>String
</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(c) FROM
Customer c WHERE c.name LIKE
?1
</ejb-ql>
</query>
A named query would look like this:
public Collection findAllByName(
String nameString) {
return getEntityManager()
.createNamedQuery(
"findAllByName")
.setParameter(
"custName", nameString)
.getResultList();
}
This example assumes that a NamedQuery annotation is defined this way:
@NamedQuery(
name="findAllByName",
queryString=
"SELECT OBJECT(c) FROM
Customer c WHERE c.name
LIKE :custName")
Container-managed relationships. In EJB 2.x, the container was responsible for updating the other side of a relationship when one side was updated. EJB 3.0 opted to require the bean to manage its own relationships (to allow out-of-container entity testing and the like). Therefore, you must add management code to beans that have existing container-managed relationships, which is typically a one-line code change:
public addOrder(Order order) {
getOrders().add(order);
}
Adding the management code would require the backpointer assignment:
public addOrder(Order order) {
getOrders().add(order);
order.setCustomer(this);
}
Migrating CMP clients. In EJB 2.x, local and/or remote clients accessed entity beans. A well-documented best practice was to keep the entity beans local and wrap them in a session façade. In EJB 3.0, entities, being regular Java objects, are always local and can never be remote (RMI) objects. They are obtained and queried for through the EntityManager API. Client code (a session bean in the case of a session façade) must change its use of entity home or component-specific methods to use the EntityManager to access the entities on which it operates.
Local and remote entity references used to be required declarations in the deployment descriptor. In the client code the home would be looked up in JNDI, and home operations such as creates and finds could then be performed. Here's an example of a client lookup:
public void createNewCustomer(
String name, String city)
throws CreateException,
NamingException,
RemoteException {
InitialContext ctx = new
InitialContext();
CustomerHome home =
(CustomerHome) ctx.lookup(
"java:comp/env/ejb/CustomerHome"
);
CustomerLocal customer = null;
customer = home.create(
name, city);
}
The migrated version of this code would look like this:
@Resource private EntityManager
em;
public void createNewCustomer(
String name, String city) {
Customer cust = new
Customer();
cust.setName(name);
cust.setCity(city);
em.persist(cust);
}
It is clear from the preceding discussion that migrating EJB 2.x entity beans to EJB 3.0 is the most complex task, will have an impact on clients, and needs careful planning.
Migrating POJO applications. Some frameworks have been persisting Java objects to relational databases for a long time, and vast numbers of applications are written by use of O/R frameworks. Applications that are currently using a POJO persistence framework are well positioned to migrate to the EJB persistence API because the domain objects are already Java objects. Further, the persistence frameworks' transaction mechanisms and session-level APIs are similar to those of the new EJB persistence API.
The result is that very little code rework is required. If you are currently evaluating a persistence framework for your J2EE applications, probably using a POJO persistence framework is the best choice because it can easily get you to the EJB 3.0 Persistence API.
Get to It
Change the session APIs used in the persistence framework to EntityManager APIs.
Change proprietary O/R XML to O/R mapping annotations or O/R XML defined by the EJB 3.0 persistence API.
The technical details of migration of POJO persistence to EJB 3.0 are covered in the article, "Prepare for the New EJB 3.0 Persistence API" (FTPOnline, June 2005)—see Resources online at www.javapro.com.
Migration of J2EE applications to EJB 3.0 is certainly not rocket science; however, like any other migration effort, it takes education, resources, and planning. The first step is to become aware of where the platform is moving and understand where it plans to end up. Once you understand the technology, you should undertake planning to decide the best strategy for getting there.
The prudent approach to migration is to migrate a subsection of the application before applying the practices to the whole. This strategy will ferret out many of the difficult migration issues and allow for experimentation to discover which approach might be best suited to the application. Features such as interoperability between EJB 2.x and EJB 3.0 components are critical when you're pursuing this kind of strategy. You can start trying out EJB 3.0 with an early implementation, and get ready for migrating to EJB 3.0.