Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Migrating from EJB 1.1 to 2.0

 
 

Articles Index



In previous articles, we've described the new features of the Enterprise JavaBeans (EJB) 2.0 specification and the changes those new features bring to the architecture. This article focuses on how enterprise bean developers and application developers can best migrate their application components from the earlier EJB 1.1 architecture to the 2.0 architecture. When appropriate, the article describes how a particular functional area was handled in the EJB 1.1 architecture. It then illustrates how developers can use the new features of the EJB 2.0 architecture to achieve the same functionality.

In brief, the EJB 2.0 specification introduces these new features and functionality to the architecture:

  • Message-driven beans
  • Improvements in container-managed persistence
  • Container-managed relationships for entity beans with container-managed persistence
  • Local interfaces
  • Enterprise JavaBeans Query Language (EJB QL)

You should refer to the earlier articles for more information on these changes. You should also refer to the Java 2 Enterprise Edition (J2EE) tutorial. See the following:

Using the Query Language

The EJB 1.1 specification left the manner and language for forming and expressing queries for finder methods to each individual application server. While many application server vendors let developers form queries using SQL, others use their own proprietary language specific to their particular application server product.

In addition, each application server provides its own tool for expressing a query. Since the developer must know the database table and column names to correctly form an SQL query, application server tools which rely on SQL provide a mapping between the database names and SQL statements to help the developer.

Because each application server handles queries differently, when you migrate from one application server to another you have to use the new server's tool to rewrite or reform your existing queries. For example, suppose you use the J2EE reference implementation deploytool to write SQL queries for your bean's finder methods. If you later bring your application to another application server, you must use that server's tool to rewrite the SQL for the finder methods.

Each application server also may support a different physical representation of the query. For example, the query may or may not be stored in the deployment descriptor. If kept in the deployment descriptor, it may be identified with a tag unique to that particular server. These factors combine to reduce the portability of your enterprise bean application among different application servers.

The EJB 2.0 specification, by introducing a query language called EJB Query Language, or EJB QL, attempts to correct many of these inconsistencies and shortcomings. EJB QL is based on SQL92. It defines query methods--finder and select methods--specifically for entity beans with container-managed persistence. EJB QL's principal advantage over SQL is its portability across EJB containers and its ability to navigate entity bean relationships.

EJB QL allows querying or navigation over entity bean relationships. That is, a query can begin with one entity bean and, from there, navigate to related beans. For example, the query can start with an Order bean and then navigate to the Order's line items. It can also navigate to the products referenced by the individual line items, and so forth. However, for this navigation to work, the bean relationships must be expressed as container-managed relationships in the deployment descriptor. (Container-managed relationships are another new feature of the EJB 2.0 architecture. See Defining Entity Bean Relationships .)

With EJB QL, you can specify the query earlier in the application development process, and that query is kept in the deployment descriptor. Because it is stored in the deployment descriptor, you have two options for writing the query: you can use the application server's query-related tools or you can write the query by editing the deployment descriptor by hand. Once you've specified the EJB QL query in the deployment descriptor, the container then generates the query language implementation for the query.

With EJB QL, it's best to start by first knowing what your existing query (or new query) should do. Then, using the EJB QL syntax, set up the query in the deployment descriptor (either by manually editing the descriptor or using a tool such as the J2EE reference implementation deploytool).

For example, suppose you wanted to form an EJB QL query to traverse from an Order entity bean to its associated LineItem entity beans and select the line items associated to a particular order. You might write the query as follows:

Select l.lineItemId from Order o, in (o, lineItems) as l

In this EJB QL query, the relationship between the Order and LineItem entity beans is expressed with the clause (o, lineItems).

Refer to the EJB 2.0 specification and the J2EE Tutorial for the complete EJB QL syntax.

Keep in mind that, because many application server vendors provide tools for writing 1.1-level queries and generating queries from the underlying database tables, it is expected that these same vendors will include equivalent tools for writing 2.0-level queries. Also, while EJB QL offers certain advantages, you should note that the language itself is currently not quite as rich as SQL. In particular, EJB QL cannot form some of the sophisticated queries of which SQL is capable. For example, EJB QL lacks an "ORDER BY" clause. However, it is very likely that application server query tools will use their own means to handle these differences between the languages.

Using Local Interfaces

In the EJB 1.1 architecture, session and entity beans have one type of interface, a remote interface, through which they can be accessed by clients and other application components. The remote interface is designed such that a bean instance has remote capabilities--the bean inherits from RMI and can interact with distributed clients across the network.

Because the remote interface exposes an enterprise bean's methods across the network tier, there is a certain amount of performance overhead when clients invoke a method on the bean. This overhead occurs regardless of whether an application's components are located on the same Virtual Machine (VM) or not. In addition, this remote capability requires that method invocations pass objects by value, potentially adding further overhead. As a result, EJB 1.1 applications had to be designed to take into account these performance limitations. For example, because objects must be passed by value between bean instances, an application might be designed to avoid passing large amounts of data with every method call.

With EJB 2.0, session beans and entity beans can expose their methods to clients through two types of interfaces: a remote interface and a local interface. The 2.0 remote interface is identical to the remote interface used in the 1.1 architecture--the bean inherits from RMI, exposes its methods across the network tier, and has the same capability to interact with distributed clients.

However, the local interfaces for session and entity beans provide support for light-weight access from enterprise beans that are local clients; that is, clients colocated in the same EJB container. The EJB 2.0 specification further requires that enterprise beans that use local interfaces be within the same application. That is, the deployment descriptors for an application's enterprise beans using local interfaces must be contained within one ejb-jar file.

The local interface is a standard Java interface. It does not inherit from RMI. An enterprise bean uses the local interface to expose its methods to other beans that reside within the same container. By using a local interface, a bean may be more tightly coupled with its clients and may be directly accessed without the overhead of a remote method call.

In addition, local interfaces permit values to be passed between beans with pass by reference semantics. Because you are now passing a reference to an object, rather than the object itself, this reduces the overhead incurred when passing objects with large amounts of data.

Setting up a session or entity bean to use a local interface rather than a remote interface is simple. The local interface through which the bean's methods are exposed to clients extends EJBLocalObject rather than EJBObject. Similarly, the bean's home interface extends EJBLocalHome rather than EJBHome. The implementation class extends the same EntityBean or SessionBean interface. (A bean that wants to be remotable in 2.0 extends EJBObject in its remote interface and EJBHome in its home interface, just as it did in 1.1.)

Design Implications

When designing your application, you need to keep in mind the performance and complexity trade-offs between using local and remote interfaces. Keep in mind that there are constraints when moving from a remote mode to a local interface mode, and it can cause your application to behave differently.

For one thing, pass by reference semantics works differently than pass by value semantics, and using pass by reference may result in unintended changes to the passed value or object. For example, when using a remote interface, bean A invokes a method on bean B and passes it object X by value. Bean B acts on and changes the state of object X but, unless bean B explicitly passes the new state back to bean A, when control returns to bean A, the state of object X is unchanged.

However, when using a local interface mode for the same scenario, bean A invokes a method on bean B and passes it object X by reference. Bean A passes only a reference to object X and not the actual object values. As a result, there is now only one copy of object X. If bean B changes the state of object X, then, when the method returns, bean A sees the new state of object X.

An application developed for EJB 1.1 may not be expecting the state of an object to change when it invokes a method on a bean. Changing from a remote mode to a local mode may thus cause different behaviors to occur when invoking an enterprise bean's method. You need to keep this in mind when designing your bean methods or converting from a remote mode to a local mode.

Using 2.0 Container-Managed Persistence

The EJB 2.0 specification has expanded container-managed persistence (CMP) to allow multiple entity beans to have relationships among themselves. This is referred to as container-managed relationships (CMR). The container manages the relationships and the referential integrity of the relationships.

The EJB 1.1 specification presented a more limited CMP model. The 1.1 architecture limited CMP to data access that is independent of the database or resource manager type. It allowed you to expose only an entity bean's instance state through its remote interface--there's no means to expose bean relationships. The 1.1 version of CMP depends on mapping the instance variables of an entity bean class to the data items representing their state in the database or resource manager. The CMP instance fields are specified in the deployment descriptor, and, when the bean is deployed, the deployer uses tools to generate code that implements the mapping of the instance fields to the data items.

For example, in 1.1 you might have an entity bean using CMP with two container-managed fields. The bean declares the fields as follows:

public String account;
public double amount;

In the deployment descriptor, these two fields appear as CMP-field elements for the entity bean. At deployment, tools generate code for the container to use to retrieve values from these fields and to update these fields.

It is not required that you use the enhanced CMP capabilities of the 2.0 architecture immediately. You can use the other new features of the architecture and still retain your 1.1 CMP implementation approach, since the 1.1 version of CMP will continue to be supported at this time.

Implementing 2.0 CMP

Local interfaces provide the foundation for container-managed relationships among entity beans and session beans, because the bean uses the local interface to maintain its references to other beans. Thus, the first step for developers who want to use the expanded CMP and CMR capabilities of 2.0 is to implement their enterprise beans using local interfaces. That is, a bean with CMP must implement a local interface and a local home interface. (The same bean may also have a remote interface in addition to a local interface.)

You must also change the way you code the bean's implementation class. According to the 2.0 specification, the implementation class for an entity bean that uses CMP is now an abstract class. For example, you might have the following implementation for an account entity bean using 2.0 CMP:

public abstract class AccountBean implements EntityBean {

The implementation class for an entity or session bean that does not use CMP is the same as it was under the 1.1 specification. For example, an account entity bean that uses bean-managed persistence in the 2.0 architecture might be declared as follows:

public class AccountBean implements EntityBean {

Defining Persistent Fields

The 2.0 specification lets you designate an entity bean's instance variables as container-managed persistent fields or container-managed relationship fields. You define these fields in the deployment descriptor. Container-managed persistent fields are marked with the element cmp-field, while container-managed relationship fields are marked with the element cmr-field.

In the implementation class, note that you do not declare the CMP and CMR fields as public variables. Instead, you define get and set methods in the entity bean to retrieve and set the values of these CMP and CMR fields. In this sense, beans using the 2.0 CMP follow the JavaBeans model: instead of accessing instance variables directly, clients use the entity bean's get and set methods to retrieve and set these instance variables. Keep in mind that the get and set methods only pertain to variables that have been designated as CMP or CMR fields.

For example, the same entity bean with account and amount CMP fields written for the 2.0 architecture would have no instance variable declarations for these two fields. Instead, it would have get and set methods such as the following:

public class AccountBean implements EntityBean {
	...
	public abstract String getAccount();
	public abstract void setAccount(String acct);
	public abstract double getAmount();
	public abstract void setAmount(double amt);
	...


Defining Entity Bean Relationships

As noted previously, the EJB 1.1 architecture does not support container-managed relationships between entity beans. The EJB 2.0 architecture does support both one-to-one and one-to-many container-managed relationships. Relationships are expressed using CMR fields, and these fields are marked as such in the deployment descriptor. You set up the CMR fields in the deployment descriptor using the appropriate deployment tool for your application server.

Similar to container-managed persistent fields, the bean does not declare the container-managed relationship fields as instance variables. Instead, the bean provides get and set methods for these fields.

One-to-one relationships are represented using the object type of the entity bean to which the relationship refers. For example, suppose the account bean maintains a one-to-one relationship to a customer, represented by a Customer bean. AccountBean might have the following abstract get and set access methods:

public class AccountBean implements EntityBean {
     ...
     public abstract Customer getCustomer();
     public abstract void setCustomer
	     (Customer cust);
     ...

Likewise, the customer bean declares abstract get and set accessor methods to the account bean, as follows:

public class CustomerBean implements EntityBean {
     ...
     public abstract Account getAccount();
     public abstract void setAccount
	     (Account acct);
     ...

With one-to-many relationships, the entity bean uses a Java Collection to represent the "many" side. Suppose the database maintains the transactions that occur for each account. The account bean might have a one-to-many relationship to this set of transactions, represented by a Transaction bean. It would include get and set methods as follows:

public class AccountBean implements EntityBean {
      ...
      public abstract Collection getTransactions();
      public abstract void setTransactions
               (Collection trans);
      ...

The EJB container maintains the referential integrity of the entity bean relationships. For example, in a one-to-one relationship, if you change one side of the relationship, the container automatically drops the old relationship and replaces it with the newly formed one-to-one relationship.

Using Message-Driven Beans

Message-driven beans are another new feature introduced by the EJB 2.0 architecture. Message-driven beans are transaction-aware components that process asynchronous messages delivered through the Java Message Service (JMS). The JMS API is an integral part of the J2EE 1.3 platform.

Asynchronous messaging allows applications to communicate by exchanging messages so that senders are independent of receivers. The sender sends its message and does not have to wait for the receiver to receive or process that message. This differs from synchronous communication, which requires the component that is invoking a method on another component to wait or block until the processing completes and control returns to the caller component.

Message-driven beans are message listeners that consume messages from a JMS destination. (Currently, message-driven beans only work with JMS messages.) A JMS destination may be a queue or a topic. When the destination is a queue, there is only one message producer or sender and one message consumer. When the destination is a topic, a message producer publishes messages to the topic, and any number of consumers may consume the topic's messages.

Message-driven beans are much like stateless session beans. They have the same lifecycle as stateless session beans, and they do not have a remote or local interface nor a home interface. The message-driven bean's sole responsibility is processing messages, while the container automatically manages the bean's entire environment. A message-driven bean is essentially application code that is invoked when a message arrives at a particular JMS destination. With this type of messaging, a component acts as a message producer and sends a message that is delivered to a JMS destination. The JMS destination includes a message selector filter that uses a property in the message to associate certain messages with a particular message-driven bean. The container activates an instance of the correct type of message-driven bean from its pool of message-driven beans, and the bean instance consumes the message from the JMS destination. Because a message-driven bean is stateless, any instance of the matching type of message-driven bean can process any message.

The advantage of message-driven beans is that they allow a loose coupling between the message producer and the message consumer, thus reducing the dependencies between separate components. In addition, the EJB container handles the setup tasks required for asynchronous messaging, such as registering the bean as a message listener. A component other than a message-driven bean would otherwise have to perform these tasks.

Implementing a message-driven bean is fairly straightforward. A message-driven bean extends two interfaces: the javax.ebj.MessageDrivenBean interface and the javax.jms.MessageListener interface. The bean also implements four methods. The container uses three of these methods (ejbCreate, ejbRemove, and setMessageDrivenContext) to control the lifecyle of the message-driven bean.

The onMessage method is the principal method of interest to the developer. It contains the business logic that the bean executes on receipt of a message. A developer decides how a message-driven bean should handle a particular message and codes this logic into the onMessage method. The message-driven bean might simply pass the message to another enterprise bean component through a synchronous method invocation, or the message-driven bean might send the message to another JMS destination, or it might perform some business logic to handle the message itself.

The onMessage method has one parameter, the message itself, and does not include a throws clause, so no application exceptions may be thrown during processing. The message parameter may be of any valid JMS message type. The method tests if the message is the expected type, such as a JMS TextMessage, and then casts the message to that type.

You can provide an empty implementation of the ejbCreate method. However, if the message-driven bean sends messages or receives synchronous communication from another destination, then you use the ejbCreate method to look up the JMS connection factories and destinations and to create the JMS connection. The implementation of the ejbRemove method can also be empty. However, if the ejbCreate method creates a JMS connection, you should use the ejbRemove method to close that connection. The setMessageDrivenContext method can be used for transaction management.

Because JMS is a full-fledged transaction resource manager, JMS work, such as message consumption, and other transaction work can be grouped into one transaction. This means that you can choose to make a message-driven bean part of a transaction or not. The deployment descriptor transaction attribute, which for a message-driven bean can be either Required or NotSupported, determines whether or not the bean participates in a transaction. (If the message-driven bean participates in a transaction, you must also be using container-managed transaction demarcation.)

When a message-driven bean's transaction attribute is set to Required, then the message delivery from the JMS destination to the message-driven bean is part of the subsequent transactional work undertaken by the bean. By having the message-driven bean be part of a transaction, you ensure that message delivery takes place. If the subsequent transactional work that the message-driven bean starts does not go to completion, then, when the container rolls back that transactional work it also puts the message back in its destination so that it can be picked up later by another message-driven bean instance.

If the message-driven bean's transactional attribute is NotSupported, then it consumes the message outside of any subsequent transactional work. Should that transaction not complete, then the message is still considered consumed and it will be lost.

Conclusion

This article has described the design issues that developers need to consider when migrating applications from an EJB 1.1 architecture to an EJB 2.0 architecture. While the biggest change affects the handling of container-managed persistence and the use of container-managed relationships, it is possible to incrementally adopt the new features of the architecture. Typically, you might begin by selectively using local interfaces between enterprise beans within the same application, or you might change your queries to use EJB QL.

For those beans with local interfaces, you can later add the CMR and CMP capabilities. Likewise, it's best to identify where asynchronous communication would be advantageous to your application, or where it would be beneficial to ensure that messages were received as part of a transaction. For these areas, rewriting your code to implement message handling with message-driven beans makes sense. Lastly, using EJB QL makes your application more portable across platforms, and will most likely be easily accomplished with your application server tools.

About the Author

Beth Stearns is the principal partner of ComputerEase Publishing, a computer consulting firm she founded in 1982. Her client list includes Sun Microsystems, Inc., Silicon Graphics, Inc., Oracle Corporation, and Xerox Corporation. Among her publications are the "Java Native Interface" chapter in "The Java Tutorial Continued" book in the Addison Wesley Java series, "The EJB Programming Guide" for Inprise Corporation, and "Understanding EDT", a guide to Digital Equipment Corporation's text editor. Most recently, she co-authored with Vlada Matena of the Addison Wesley Java series book, "Applying Enterprise JavaBeans: Component-Based Development for the J2EE Platform."

Have a question about programming? Use Java Online Support.