Developing enterprise beans

This chapter explains the basic tasks required to develop and package the most common types of enterprise beans. Specifically, this chapter focuses on creating stateless session beans and entity beans that use container-managed persistence (CMP); in the discussion of stateless session beans, important information about stateful beans is also provided. For information on developing entity beans that use bean-managed persistence (BMP), see Developing entity beans with BMP.

The information in this chapter is not exhaustive; however, it includes the information you need to develop basic enterprise beans. For information on developing more complicated enterprise beans, consult a commercially available book on enterprise bean development. The example enterprise beans discussed in this chapter and the example Java applications and servlets that use them are described in Information about the examples described in the documentation.

This chapter describes the requirements for building each of the major components of an enterprise bean. If you do not intend to use one of the commercially available integrated development environments (IDE), such as IBM's VisualAge for Java, you must build each of these components manually (by using tools in the Java Development Kit and WebSphere). Manually developing enterprise beans is much more difficult and error-prone than developing them in an IDE. Therefore, it is strongly recommended that you choose an IDE with which you are comfortable.


Developing entity beans with CMP

In an entity bean with CMP, the container handles the interactions between the entity bean and the data source. In an entity bean with BMP, the entity bean must contain all of the code required for the interactions between the entity bean and the data source. For this reason, developing an entity bean with CMP is simpler than developing an entity bean with BMP.

This section examines the development of entity beans with CMP. While much of the information in this section also applies to entity beans with BMP, there are some major differences between the two types. For information on the tasks required to develop an entity bean with BMP, see Developing entity beans with BMP.

Every entity bean must contain the following basic parts:

Writing the enterprise bean class (entity with CMP)
In a CMP entity bean, the bean class defines and implements the business methods of the enterprise bean, defines and implements the methods used to create instances of the enterprise bean, and implements the methods used by the container to inform the instances of the enterprise bean of significant events in the instance's life cycle. Enterprise bean clients never access the bean class directly; instead, the classes that implement the home and remote interfaces are used to indirectly invoke the methods defined in the bean class.

By convention, the enterprise bean class is named NameBean, where Name is the name you assign to the enterprise bean. The enterprise bean class for the example Account enterprise bean is named AccountBean. Every entity bean class with CMP must meet the following requirements:

Note:

The enterprise bean class can implement the enterprise bean's remote interface, but this is not recommended. If the enterprise bean class implements the remote interface, it is possible to inadvertently pass the this variable as a method argument.

An enterprise bean class cannot implement two different interfaces if the methods in the interfaces have the same name, even if the methods have different signatures, due to the Java-IDL mapping specification. Errors can occur when the enterprise bean is deployed.

Figure 8 shows the main parts of the enterprise bean class for the example Account enterprise bean. (Emphasized code is in bold type.) The sections that follow discuss these parts in greater detail.

Figure 8. Code example: The AccountBean class

...
import java.util.Properties;
import javax.ejb.*;
import java.lang.*;
public class AccountBean implements EntityBean { 
     // Set instance variables here
     ...
     // Implement methods here
     ...
}

Defining variables
An entity bean class can contain both persistent and nonpersistent instance variables; however, static variables are not supported in enterprise beans unless they are also final (that is, they are constants). Static variables are not supported because there is no way to guarantee that they remain consistent across enterprise bean instances.

Container-managed fields (which are persistent variables) are stored in a database. Container-managed fields must be public.

Nonpersistent variables are not stored in a database and are temporary. Nonpersistent variables must be used with caution and must not be used to maintain the state of an EJB client between method invocations. This restriction is necessary because nonpersistent variables cannot be relied on to remain the same between method invocations outside of a transaction because other EJB clients can change these variables, or they can be lost when the entity bean is passivated.

The AccountBean class contains three container-managed fields (shown in Figure 9):

Figure 9. Code example: The variables of the AccountBean class

...
public class AccountBean implements EntityBean { 
     private EntityContext entityContext = null;
     private ListResourceBundle bundle = 
          ResourceBundle.getBundle(
                    "com.ibm.ejs.doc.account.AccountResourceBundle");
     public long accountId = 0;
     public int type = 1;
     public float balance = 0.0f;
     ...
}

The deployment descriptor is used to identify container-managed fields in entity beans with CMP. In an entity bean with CMP, each container-managed field must be initialized by each ejbCreate method (see Implementing the ejbCreate and ejbPostCreate methods).

A subset of the container-managed fields is used to define the primary key class associated with each instance of an enterprise bean. As is shown in Writing the primary key class (entity with CMP), the accountId variable defines the primary key for the Account enterprise bean. The AccountBean class contains two nonpersistent variables:

Implementing the business methods
The business methods of an entity bean class define the ways in which the data encapsulated in the class can be manipulated. The business methods implemented in the enterprise bean class cannot be directly invoked by an EJB client. Instead, the EJB client invokes the corresponding methods defined in the enterprise bean's remote interface, by using an EJB object associated with an instance of the enterprise bean, and the container invokes the corresponding methods in the instance of the enterprise bean.

Therefore, for every business method implemented in the enterprise bean class, a corresponding method must be defined in the enterprise bean's remote interface. The enterprise bean's remote interface is implemented by the container in the EJB object class when the enterprise bean is deployed.

Figure 10 shows the business methods for the AccountBean class. These methods are used to add a specified amount to an account balance and return the new balance (add), to return the current balance of an account (getBalance), to set the balance of an account (setBalance), and to subtract a specified amount from an account balance and return the new balance (subtract). The subtract method throws the user-defined exception com.ibm.ejs.doc.account.InsufficientFundsException if a client attempts to subtract more money from an account than is contained in the account balance. The subtract method in the Account bean's remote interface must also throw this exception as shown in Figure 15. User-defined exception classes for enterprise beans are created as are any other user-defined exception class. The message content for the InsufficientFundsException exception is obtained from the AccountResourceBundle class file by invoking the getMessage method on the bundle object.

Note:
If an enterprise bean container catches a system exception from the business method of an enterprise bean, and the method is running within a container-managed transaction, the container rolls back the transaction before passing the exception on to the client. However, if the business method is throwing an application exception, then the transaction is not rolled back (it is committed), unless the application has called setRollbackOnly function. In this case, the transaction is rolled back before the exception is re-thrown.

Figure 10. Code example: The business methods of the AccountBean class

...
public class AccountBean implements EntityBean { 
     ...
     public long accountId = 0;
     public int type = 1;
     public float balance = 0.0f;
     ...
     public float add(float amount) {
          balance += amount;
          return balance;
     }
     ...
     public float getBalance() {
          return balance; 
     }
     ...
     public void setBalance(float amount) { 
          balance = amount; 
     }
     ...
     public float subtract(float amount) throws InsufficientFundsException {
          if(balance < amount) {
               throw new InsufficientFundsException(
                    bundle.getMessage("insufficientFunds"));
          }
          balance -= amount;
          return balance;
     }
     ...
}

Standard application exceptions for entity beans

Version 1.1 of the EJB specification defines several standard application exceptions for use by enterprise beans. All of these exceptions are subclasses of the javax.ejb.EJBException class. For entity beans with both container- and bean-managed persistence, the EJB specification defines the following application exceptions:

Application programmers can use the generic EJBException class or one of the provided subclassed exceptions, or programmers can define their own exceptions by subclassing any of this family of exceptions. All of these exceptions inherit from the javax.ejb.RuntimeException class and do not have to be explicitly declared in throws clauses.

Each exception is discussed in more detail within the relevant section; for more information on:

Note:
Version 1.0 of the EJB specification used the java.rmi.RemoteException class to capture application-specific exceptions; the EJBException class and its subclasses are new in the 1.1 version of the specification. Therefore, using the RemoteException class is now deprecated in favor of the more precise exception classes. Older applications that use the RemoteException class can still run, but enterprise beans compliant with version 1.1 of the specification must use the new exception classes.

Implementing the ejbCreate and ejbPostCreate methods
You must define and implement an ejbCreate method for each way in which you want a new instance of an enterprise bean to be created. For each ejbCreate method, you must also define a corresponding ejbPostCreate method. Each ejbCreate and ejbPostCreate method must correspond to a create method in the home interface.

Like the business methods of the bean class, the ejbCreate and ejbPostCreate methods cannot be invoked directly by the client. Instead, the client invokes the create method of the enterprise bean's home interface by using the EJB home object, and the container invokes the ejbCreate method followed by the ejbPostCreate method. If the ejbCreate and ejbPostCreate methods are executed successfully, an EJB object is created and the persistent data associated with that object is inserted into the data source.

For an entity bean with CMP, the container handles the required interaction between the entity bean instance and the data source between calls to the ejbCreate and ejbPostCreate methods. For an entity bean with BMP, the ejbCreate method must contain the code to directly handle this interaction. For more information on entity beans with BMP, see Developing entity beans with BMP.

Each ejbCreate method in an entity bean with CMP must meet the following requirements:

Each ejbPostCreate method must be public, return void, and have the same arguments as the matching ejbCreate method. If necessary, both the ejbCreate method and the ejbPostCreate method can throw the javax.ejb.EJBException exception or one of the creation-related subclasses, the CreateException or the DuplicateKeyException exceptions. The DuplicateKeyException class is a subclass of the CreateException class. Throwing the java.rmi.RemoteException exception is deprecated; see Standard application exceptions for entity beans for more information.

Figure 11 shows two sets of ejbCreate and ejbPostCreate methods required for the example AccountBean class. The first set of ejbCreate and ejbPostCreate methods are wrappers that call the second set of methods and set the type variable to 1 (corresponding to a savings account) and the balance variable to 0 (zero dollars).

Figure 11. Code example: The ejbCreate and ejbPostCreate methods of the AccountBean class

...
public class AccountBean implements EntityBean { 
     ...
     public long accountId = 0;
     public int type = 1;
     public float balance = 0.0f;
     ...
     public Integer ejbCreate(AccountKey key) {
          ejbCreate(key, 1, 0.0f);
     }
     ...
     public Integer ejbCreate(AccountKey key, int type, float initialBalance) 
     throws EJBException {
          accountId = key.accountId;
          type = type;
          balance = initialBalance;
     }
     ...
     public void ejbPostCreate(AccountKey key) 
     throws EJBException {
           ejbPostCreate(key, 1, 0);
     }
     ...
     public void ejbPostCreate(AccountKey key, int type, float initialBalance) { }
     ...
}

Implementing the EntityBean interface
Each entity bean class must implement the methods inherited from the javax.ejb.EntityBean interface. The container invokes these methods to inform the bean instance of significant events in the instance's life cycle. (For more information, see Entity bean life cycle.) All of these methods must be public and return void; they can throw the javax.ejb.EJBException exception or, in the case of the ejbRemove method, the javax.ejb.RemoveException exception. Throwing the java.rmi.RemoteException exception is deprecated; see Standard application exceptions for entity beans for more information.

In entity beans with CMP, the container handles the required data source interaction for these methods. In entity beans with BMP, these methods must directly handle the required data source interaction. For more information on entity beans with BMP, see More-advanced programming concepts for enterprise beans.

These methods have several possible uses, including the following:

As shown in Figure 12, except for the setEntityContext and unsetEntityContext methods, all of these methods are empty in the AccountBean class because no additional action is required by the bean for the particular life cycle states associated with the these methods. The setEntityContext and unsetEntityContext methods are used in a conventional way to set the value of the entityContext variable.

Figure 12. Code example: Implementing the EntityBean interface in the AccountBean class

...
public class AccountBean implements EntityBean { 
     private EntityContext entityContext = null;
     ...
     public void ejbActivate() throws EJBException { }
     ...
     public void ejbLoad () throws EJBException { }
     ...
     public void ejbPassivate() throws EJBException { }
     ...
     public void ejbRemove() throws EJBException { }
     ...
     public void ejbStore () throws EJBException { }
     ...
     public void setEntityContext(EntityContext ctx) throws EJBException {
          entityContext = ctx; 
     }
     ...
     public void unsetEntityContext() throws EJBException {
          entityContext = null; 
     }
}

Writing the home interface (entity with CMP)
An entity bean's home interface defines the methods used by clients to create new instances of the bean, find and remove existing instances, and obtain metadata about an instance. The home interface is defined by the enterprise bean developer and implemented in the EJB home class created by the container during enterprise bean deployment.

The container makes the home interface accessible to enterprise bean clients through the Java Naming and Directory Interface (JNDI). JNDI is independent of any specific naming and directory service and allows Java-based applications to access any naming and directory service in a standard way.

By convention, the home interface is named NameHome, where Name is the name you assign to the enterprise bean. For example, the Account enterprise bean's home interface is named AccountHome. Every home interface must meet the following requirements:

Figure 13 shows the relevant parts of the definition of the home interface (AccountHome) for the example Account bean. This interface defines two abstract create methods: the first creates an Account object by using an associated AccountKey object, the second creates an Account object by using an associated AccountKey object and specifying an account type and an initial balance. The interface defines the required findByPrimaryKey method and a findLargeAccounts method, which returns a collection of accounts containing balances greater than a specified amount.

Figure 13. Code example: The AccountHome home interface

...
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public interface  AccountHome extends EJBHome {
     ...
     Account create (AccountKey id) throws CreateException, RemoteException;
     ...
     Account create(AccountKey id, int type, float initialBalance) 
          throws CreateException, RemoteException;
     ...
     Account findByPrimaryKey (AccountKey id) 
          RemoteException, FinderException;
     ...
     Enumeration findLargeAccounts(float amount) 
           throws RemoteException, FinderException;
}

Defining create methods
A create method is used by a client to create an enterprise bean instance and insert the data associated with that instance into the data source. Each create method must be named create and it must have the same number and types of arguments as a corresponding ejbCreate method in the enterprise bean class. (The ejbCreate method must itself have a corresponding ejbPostCreate method.)

Each create method must meet the following requirements:

Defining finder methods
A finder method is used to find one or more existing entity EJB objects. Each finder method must be named findName, where Name further describes the finder method's purpose.

At minimum, each home interface must define the findByPrimaryKey method that enables a client to locate an EJB object by using the primary key only. The findByPrimaryKey method has one argument, an object of the bean's primary key class, and returns the type of the bean's remote interface.

Every other finder method must meet the following requirements:

While every entity bean must contain the default finder method, you can write additional finder methods if needed. For example, the Account bean's home interface defines the findLargeAccounts method to find objects that encapsulate accounts with balances of more than a specified amount, as shown in Figure 14. Because this finder method can be expected to return a reference to more than one EJB object, its return type is Enumeration.

Figure 14. Code example: The findLargeAccounts method

Enumeration findLargeAccounts(float amount) 
           throws RemoteException, FinderException;

Every EJB server can implement the findByPrimaryKey method. During enterprise bean deployment, the container generates the code required to search the database for the appropriate enterprise bean instance.

However, for each additional finder method that you define in the home interface, the enterprise bean deployer must associate finder logic with that finder method. This logic is used by the EJB server during deployment to generate the code required to implement the finder method.

The EJB Specification does not define the format of the finder logic, so the format can vary according to the EJB server you are using. For more information on creating finder logic, see Creating finder logic in the EJB server.

Writing the remote interface (entity with CMP)
An entity bean's remote interface provides access to the business methods available in the bean class. It also provides methods to remove an EJB object associated with a bean instance and to obtain the bean instance's home interface, object handle, and primary key. The remote interface is defined by the enterprise bean developer and implemented in the EJB object class created by the container during enterprise bean deployment.

By convention, the remote interface is named Name, where Name is the name you assign to the enterprise bean. For example, the Account enterprise bean's remote interface is named Account. Every remote interface must meet the following requirements:

Figure 15 shows the relevant parts of the definition of the remote interface (Account) for the example Account enterprise bean. This interface defines four methods for displaying and manipulating the account balance that exactly match the business methods implemented in the AccountBean class. All of the business methods in the remote interface throw the java.rmi.RemoteException exception class. In addition, the subtract method must throw the user-defined exception com.ibm.ejs.doc.account.InsufficientFundsException because the corresponding method in the bean class throws this exception. Furthermore, any client that calls this method must either handle the exception or pass it on by throwing it.

Figure 15. Code example: The Account remote interface

...
import java.rmi.*;
import javax.ejb.*;
public interface Account extends EJBObject 
{
     ...
     float add(float amount) throws RemoteException;
     ...
     float getBalance() throws RemoteException;
     ...
     void setBalance(float amount) throws RemoteException;
     ...
     float subtract(float amount) throws InsufficientFundsException, 
          RemoteException;
}

Writing the primary key class (entity with CMP)
Within a container, every entity EJB object has a unique identity that is defined by using a combination of the object's home interface name and its primary key, the latter of which is assigned to the object at creation. If two EJB objects have the same identity, they are considered identical.

Primary keys are specified in two ways:

The primary key class is used to manage an EJB object's primary key. By convention, the primary key class is named NameKey, where Name is the name of the enterprise bean. For example, the Account enterprise bean's primary key class is named AccountKey. The primary key class must meet the following requirements:

Note:
The primary key class of a CMP entity bean must override the equals method and the hashCode method inherited from the java.lang.Object class.

Figure 16 shows a composite primary key class for an example enterprise bean, Item. In effect, this class acts as a wrapper around the string variables productId and vendorId. The hashCode method for the ItemKey class invokes the corresponding hashCode method in the java.lang.String class after creating a temporary string object by using the value of the productId variable. In addition to the default constructor, the ItemKey class also defines a constructor that sets the value of the primary key variables to the specified strings.

Figure 16. Code example: The ItemKey primary key class

...
import java.io.*;
// Composite primary key class
public class ItemKey implements java.io.Serializable { 
                           
     public String productId; 
     public String vendorId; 
     // Constructors
     public ItemKey() { }; 
     public ItemKey(String productId, String vendorId) { 
          this.productId = productId; 
          this.vendorId = vendorId; 
     } 
       
     public String getProductId() { 
          return productId; 
     } 
     public String getVendorId() { 
            return vendorId; 
      } 
 ...
     // EJB server-specific method  
      public boolean equals(Object other) { 
              if (other instanceof ItemKey) { 
                   return (productId.equals(((ItemKey) 
                              other).productId) 
                         && vendorId.equals(((ItemKey) 
                              other).vendorId)); 
               } 
               else 
                   return false; 
       } 
     ...
     // EJB server-specific method
      public int hashCode() { 
                return (new productId.hashCode()); 
       } 
 }

A primary key class can also be used to encapsulate a primary key that is not known ahead of time -- for instance, if the entity bean is intended to work with several persistent data stores, each of which requires a different primary key structure. The entity bean's primary key type is derived from the primary key type used by the underlying database that stores the entity objects; it does not necessarily have to be known to the enterprise bean developer.

To specify an unknown primary key, do the following:

When the primary key selection is deferred to deployment, client applications cannot use methods that rely on knowledge of the primary key type. In addition, applications cannot always depend on methods that return the type of the primary key (such as the EntityContext.getPrimaryKey method) because the return type is determined at deployment.

Interacting with databases

This section contains general information and tips on enterprise beans and database access.

Shared database access corresponds to Option C caching. Option A and Option C caching are also known as commit option A and commit option C, respectively.


Developing session beans

In their basic makeup, session beans are similar to entity beans. However, their purposes are very different.

From a component perspective, one of the biggest differences between the two types of enterprise beans is that session beans do not have a primary key class and the session bean's home interface does not define finder methods. Session enterprise beans do not require primary keys and finder methods because session EJB objects are created, associated with a specific client, and then removed as needed, whereas entity EJB objects represent permanent data in a data source and can be uniquely identified with a primary key. Because the data for session beans is never permanently stored, the session bean class does not have methods for storing data to and loading data from a data source.

Every session bean must contain the following basic parts:

Writing the enterprise bean class (session)
A session bean class defines and implements the business methods of the enterprise bean, implements the methods used by the container during the creation of enterprise bean instances, and implements the methods used by the container to inform the enterprise bean instance of significant events in the instance's life cycle. By convention, the enterprise bean class is named NameBean, where Name is the name you assign to the enterprise bean. The enterprise bean class for the example Transfer enterprise bean is named TransferBean. Every session bean class must meet the following requirements:
Note:
Version 1.0 of the EJB specification allowed the methods in the session bean class to throw the java.rmi.RemoteException exception to indicate a non-application exception. This practice is deprecated in version 1.1 of the specification. A session bean compliant with version 1.1 of the specification should throw the javax.ejb.EJBException exception (a subclass of the java.lang.RuntimeException class) or another RuntimeException exception instead. Because the javax.ejb.EJBException class is a subclass of the java.lang.RuntimeException, EJBException exceptions do not need to be explicitly listed in the throws clause of methods.
A session bean can be either stateful or stateless. In a stateless session bean, none of the methods depend on the values of variables set by any other method, except for the ejbCreate method, which sets the initial (identical) state of each bean instance. In a stateful enterprise bean, one or more methods depend on the values of variables set by some other method. As in entity beans, static variables are not supported in session beans unless they are also final. Stateful session beans possibly need to synchronize their conversational state with the transactional context in which they operate. For example, a stateful session bean possibly needs to reset the value of some of its variables if a transaction is rolled back or it possibly needs to change these variables if a transaction successfully completes.

If a bean needs to synchronize its conversational state with the transactional context, the bean class must implement the javax.ejb.SessionSynchronization interface. This interface contains methods to notify the session bean when a transaction begins, when it is about to complete, and when it has completed. The enterprise bean developer can use these methods to synchronize the state of the session enterprise bean instance with ongoing transactions.

The enterprise bean class can implement the enterprise bean's remote interface, but this is not recommended. If the enterprise bean class implements the remote interface, it is possible to inadvertently pass the this variable as a method argument.

Figure 17 shows the main parts of the enterprise bean class for the example Transfer bean. The sections that follow discuss these parts in greater detail.

The Transfer bean is stateless. If the Transfer bean's transferFunds method were dependent on the value of the balance variable returned by the getBalance method, the TransferBean would be stateful.

Figure 17. Code example: The TransferBean class

...
import java.rmi.RemoteException;
import java.util.Properties;
import java.util.ResurceBundle;
import java.util.ListResourceBundle;
import javax.ejb.*;
import java.lang.*;
import javax.naming.*;
import com.ibm.ejs.doc.account.*;
...
public class TransferBean implements SessionBean {
     ...
     private SessionContext mySessionCtx = null;
     private InitialContext initialContext = null;
     private AccountHome accountHome = null;
     private Account fromAccount = null;
     private Account toAccount = null;
     ...
     public void ejbActivate() throws EJBException { }
     ...
     public void ejbCreate() throws EJBException {
          ...
     }
     ...
     public void ejbPassivate() throws EJBException { }
     ...
     public void ejbRemove() throws EJBException { }
     ...
     public float getBalance(long acctId) throws FinderException, 
          EJBException {
          ...
     }
     ...
     public void setSessionContext(javax.ejb.SessionContext ctx) 
          throws EJBException {
          ...
     }
     ...
     public void transferFunds(long fromAcctId, long toAcctId, float amount) 
          throws EJBException {
          ...
     }
}

Implementing the business methods
The business methods of a session bean class define the ways in which an EJB client can manipulate the enterprise bean. The business methods implemented in the enterprise bean class cannot be directly invoked by an EJB client. Instead, the EJB client invokes the corresponding methods defined in the enterprise bean's remote interface, by using an EJB object associated with an instance of the enterprise bean, and the container invokes the corresponding methods in the enterprise bean instance.

Therefore, for every business method defined in the enterprise bean's remote interface, a corresponding method must be implemented in the enterprise bean class. The enterprise bean's remote interface is implemented by the container in the EJBObject class when the enterprise bean is deployed.

Figure 18 shows the business methods for the TransferBean class. The getBalance method is used to get the balance for an account. It first locates the appropriate Account EJB object and then calls that object's getBalance method.

The transferFunds method is used to transfer a specified amount between two accounts (encapsulated in two Account entity EJB objects). After locating the appropriate Account EJB objects by using the findByPrimaryKey method, the transferFunds method calls the add method on one account and the subtract method on the other. Like all finder methods, findByPrimaryKey can throw both the FinderException and RemoteException exceptions. The try/catch blocks are set up around invocations of the findByPrimaryKey method to handle the entry of invalid account IDs by users. If the session bean user enters an invalid account ID, the findByPrimaryKey method cannot locate an EJB object, and the finder method throws the FinderException exception. This exception is caught and converted into a new FinderException exception containing information on the invalid account ID.

To call the findByPrimaryKey method, both business methods need to be able to access the EJB home object that implements the AccountHome interface discussed in Writing the home interface (entity with CMP). Obtaining the EJB home object is discussed in Implementing the ejbCreate methods.

Figure 18. Code example: The business methods of the TransferBean class

public class TransferBean implements SessionBean { 
    ...
   private Account fromAccount = null;
   private Account toAccount = null;
   ...
   public float getBalance(long acctId) throws FinderException, EJBException {
        AccountKey key = new AccountKey(acctId);
        try {
             fromAccount = accountHome.findByPrimaryKey(key);
        } catch(FinderException ex) {
             throw new FinderException("Account " + acctId 
                        + " does not exist.");
        } catch(RemoteException ex) {
             throw new FinderException("Account " + acctId 
                        + " could not be found.");
        }
        return fromAccount.getBalance();
   }
   ...
   public void transferFunds(long fromAcctId, long toAcctId, float amount) 
        throws EJBException, InsufficientFundsException, FinderException {
        AccountKey fromKey = new AccountKey(fromAcctId);
        AccountKey toKey = new AccountKey(toAcctId);
        try {
             fromAccount = accountHome.findByPrimaryKey(fromKey);
        } catch(FinderException ex) {
             throw new FinderException("Account " + fromAcctId  
                       + " does not exist.");
        } catch(RemoteException ex) {
             throw new FinderException("Account " + acctId 
                       + " could not be found.");
        }
        try {
             toAccount = accountHome.findByPrimaryKey(toKey);
        } catch(FinderException ex) {
             throw new FinderException("Account " + toAcctId 
                       + " does not exist.");
        } catch(RemoteException ex) {
             throw new FinderException("Account " + acctId 
                       + " could not be found.");
        }
        try {
             toAccount.add(amount);
             fromAccount.subtract(amount);
        } catch(InsufficientFundsException ex) {
              mySessionCtx.setRollbackOnly();
              throw new InsufficientFundsException("Insufficient funds in  " 
                       + fromAcctId);
        }
   }
}

Implementing the ejbCreate methods
You must define and implement an ejbCreate method for each way in which you want an enterprise bean to be instantiated.

Each ejbCreate method must correspond to a create method in the enterprise bean's home interface. (Note that there is no ejbPostCreate method in a session bean as there is in an entity bean.) Unlike the business methods of the enterprise bean class, the ejbCreate methods cannot be invoked directly by the client. Instead, the client invokes the create method in the bean instance's home interface, and the container invokes the ejbCreate method. If an ejbCreate method is executed successfully, an EJB object is created.

An ejbCreate method for a session bean must meet the following requirements:

The throws clause can define arbitrary application exceptions. The javax.ejb.EJBException or another runtime exception can be used to indicate non-application exceptions.

An ejbCreate method for an entity bean must meet the following requirements:

The throws clause can define arbitrary application exceptions. The javax.ejb.EJBException or another runtime exception can be used to indicate non-application exceptions. Figure 19 shows the ejbCreate method required by the example TransferBean class. The Transfer bean's ejbCreate method obtains a reference to the Account bean's home object. This reference is required by the Transfer bean's business methods. Getting a reference to an enterprise bean's home interface is a two-step process:

  1. Construct an InitialContext object by setting the required property values. For the example Transfer bean, these property values are defined in the environment variables of the Transfer bean's deployment descriptor.

  2. Use the InitialContext object to create and get a reference to the home object. For the example Transfer bean, the JNDI name of the Account bean is stored in an environment variable in the Transfer bean's deployment descriptor.

Creating the InitialContext object

When a container invokes the Transfer bean's ejbCreate method, the enterprise bean's initialContext object is constructed by creating a Properties variable (env) that requires the following values:

The values of these properties are discussed in more detail in Creating and getting a reference to a bean's EJB object.

Figure 19. Code example: Creating the InitialContext object in the ejbCreate method of the TransferBean class

...
public class TransferBean implements SessionBean {
     private static final String INITIAL_NAMING_FACTORY_SYSPROP = 
               javax.naming.Context.INITIAL_CONTEXT_FACTORY;
     private static final String PROVIDER_URL_SYSPROP = 
               javax.naming.Context.PROVIDER_URL;
     ...
     private String nameService = null;
     ...
     private String providerURL = null;
     ...
     private InitialContext initialContext = null;
     ...
     public void ejbCreate() throws EJBException {
          // Get the initial context
          try {
               Properties env  = System.getProperties();
               ...
               env.put( PROVIDER_URL_SYSPROP, getProviderUrl() );
               env.put( INITIAL_CONTEXT_FACTORY_SYSPROP, getNamingFactory() );
               initialContext = new InitialContext( env );
          } catch(Exception ex) {
               ...
          }
          ...
          // Look up the home interface using the JNDI name
          ...
}
Although the example Transfer bean stores some locale specific variables in a resource bundle class, like the example Account bean, it also relies on the values of environment variables stored in its deployment descriptor. Each of these InitialContext Properties values is obtained from an environment variable contained in the Transfer bean's deployment descriptor. A private get method that corresponds to the property variable is used to get each of the values (getNamingFactory and getProviderURL); these methods must be written by the enterprise bean developer. The following environment variables must be set to the appropriate values in the deployment descriptor of the Transfer bean.

Figure 20 illustrates the relevant parts of the getProviderURL method that is used to get the PROVIDER_URL property value. The javax.ejb.SessionContext variable (mySessionCtx) is used to get the Transfer bean's environment in the deployment descriptor by invoking the getEnvironment method. The object returned by the getEnvironment method can then be used to get the value of a specific environment variable by invoking the getProperty method.

Figure 20. Code example: The getProviderURL method

...
public class TransferBean implements SessionBean {
      private SessionContext mySessionCtx = null;
     ...
     private String getProviderURL() throws RemoteException {
          //get the provider URL property either from
          //the EJB properties or, if it isn't there
          //use "iiop:///", which causes a default to the local host
          ...
          String pr = mySessionCtx.getEnvironment().getProperty(
                    PROVIDER_URL_SYSPROP);
          if (pr == null)
               pr = "iiop:///";
          return pr;
     }
     ...
}

Getting the reference to the home object

An enterprise bean is accessed by looking up the class implementing its home interface by name through JNDI. Methods on the home interface provide access to an instance of the class implementing the remote interface.

After constructing the InitialContext object, the ejbCreate method performs a JNDI lookup using the JNDI name of the Account enterprise bean. Like the PROVIDER_URL and INITIAL_CONTEXT_FACTORY properties, this name is also retrieved from an environment variable contained in the Transfer bean's deployment descriptor (by invoking a private method named getHomeName). The lookup method returns an object of type java.lang.Object.

The returned object is narrowed by using the static method javax.rmi.PortableRemoteObject.narrow to obtain a reference to the EJB home object for the specified enterprise bean. The parameters of the narrow method are the object to be narrowed and the class of the object to be created as a result of the narrowing. For a more thorough discussion of the code required to locate an enterprise bean in JNDI and then narrow it to get an EJB home object, see Creating and getting a reference to a bean's EJB object.

Figure 21. Code example: Creating the AccountHome object in the ejbCreate method of the TransferBean class

...
public class TransferBean implements SessionBean {
     ...
     private String accountName = null;
     ...
     private InitialContext initialContext = null;
     ...
     public void ejbCreate() throws EJBException {
          // Get the initial context
          ...
          // Look up the home interface using the JNDI name
          try {
               java.lang.Object ejbHome = initialContext.lookup(accountName);               
               accountHome = (AccountHome)javax.rmi.PortableRemoteObject.narrow(
                         ejbHome,  AccountHome.class);
          } catch (NamingException e) { // Error getting the home interface
               ...
          }
          ...
     }
     ...
}

Looking up an enterprise bean's environment naming context

The enterprise bean's environment is implemented by the container. It enables the bean's business logic to be customized without the need to access or change the bean's source code. The container provides an implementation of the JNDI naming context that stores the enterprise bean environment. Business methods access the environment by using the JNDI interfaces. The deployment descriptor provides the environment entries that the enterprise bean expects at runtime.

Each enterprise bean defines its own environment entries, which are shared between all of its instances (that is, all instances with the same home). Environment entries are not shared between enterprise beans.

An enterprise bean's environment entries are stored directly in the environment naming context (or one of its subcontexts). To retrieve its environment naming context, an enterprise bean instance creates an InitialContext object by using the constructor with no arguments. It then looks up the environment naming via the InitialContext object under the name java:comp/env.

The enterprise bean in Figure 22 changes an account number by looking up an environment entry to find the new account number.

Figure 22. Code example: Looking up an enterprise bean's environment naming context

public class AccountService implements SessionBean {
...
     public void changeAccountNumber(int accountNumber, ... )
          throws InvalidAccountNumberException{
               ....
               // Obtain the bean's environment naming context
               Context initialContext = new InitialContext();
               Context myEnvironment = (Context)initialContext.lookup("java:comp/env);
            ...
            // Obtain new account number from environment
            Integer newNumber = (Integer)myEnvironment.lookup("newAccountNumber");
           ... }
}

Implementing the SessionBean interface
Every session bean class must implement the methods inherited from the javax.ejb.SessionBean interface. The container invokes these methods to inform the enterprise bean instance of significant events in the instance's life cycle. All of these methods must be public, must return void, and can throw the javax.ejb.EJBException. (Throwing the java.rmi.RemoteException exception is deprecated; see *** for more information.) As shown in Figure 23, except for the setSessionContext method, all of these methods in the TransferBean class are empty because no additional action is required by the bean for the particular life cycle states associated with the these methods. The setSessionContext method is used in a conventional way to set the value of the mySessionCtx variable.

Figure 23. Code example: Implementing the SessionBean interface in the TransferBean class

...
public class TransferBean implements SessionBean { 
     private SessionContext mySessionCtx = null;
     ...
     public void ejbActivate() throws EJBException { }
     ...
     public void ejbPassivate() throws EJBException { }
     ...
     public void ejbRemove() throws EJBException { }
     ...
     public void setSessionContext(SessionContext ctx) throwEJBException { 
          mySessionCtx = ctx; 
     }
     ...
}

Writing the home interface (session)
A session bean's home interface defines the methods used by clients to create and remove instances of the enterprise bean and obtain metadata about an instance. The home interface is defined by the enterprise bean developer and implemented in the EJB home class created by the container during enterprise bean deployment. The container makes the home interface accessible to clients through JNDI.

By convention, the home interface is named NameHome, where Name is the name you assign to the enterprise bean. For example, the Transfer enterprise bean's home interface is named TransferHome. Every session bean's home interface must meet the following requirements:

Figure 24 shows the relevant parts of the definition of the home interface (TransferHome) for the example Transfer bean.

Figure 24. Code example: The TransferHome home interface

...
import javax.ejb.*;
import java.rmi.*;
public interface TransferHome extends EJBHome {
     Transfer create() throws CreateException, RemoteException;  
} 
A create method is used by a client to create an enterprise bean instance. A stateful session bean can contain multiple create methods; however, a stateless session bean can contain only one create method with no arguments. This restriction on stateless session beans ensures that every instance of a stateless session bean is the same as every other instance of the same type. (For example, every Transfer bean instance is the same as every other Transfer bean instance.)

Each create method must be named create and have the same number and types of arguments as a corresponding ejbCreate method in the EJB object class. The return types of the create method and its corresponding ejbCreate method are always different. Each create method must meet the following requirements:

Writing the remote interface (session)
A session bean's remote interface provides access to the business methods available in the enterprise bean class. It also provides methods to remove an enterprise bean instance and to obtain the enterprise bean's home interface and handle. The remote interface is defined by the enterprise bean developer and implemented in the EJB object class created by the container during enterprise bean deployment.

By convention, the remote interface is named Name, where Name is the name you assign to the enterprise bean. For example, the Transfer enterprise bean's remote interface is named Transfer. Every remote interface must meet the following requirements:

Figure 25 shows the relevant parts of the definition of the remote interface (Transfer) for the example Transfer bean. This interface defines the methods for transferring funds between two Account bean instances and for getting the balance of an Account bean instance.

Figure 25. Code example: The Transfer remote interface

...
import javax.ejb.*;
import java.rmi.*;
import com.ibm.ejs.doc.account.*;
public interface Transfer extends EJBObject {
     ...
     float getBalance(long acctId) throws FinderException, RemoteException;
     ...
     void transferFunds(long fromAcctId, long toAcctId, float amount) 
          throws InsufficientFundsException, RemoteException;
}

Implementing interfaces common to multiple types of enterprise beans

Enterprise beans must implement the interfaces described here in the appropriate enterprise bean component.

Methods inherited from javax.ejb.EJBObject
The remote interface inherits the following methods from the javax.ejb.EJBObject interface, which are implemented by the container during deployment:

These methods have the following syntax:


public abstract EJBHome getEJBHome();
public abstract Handle getHandle();
public abstract Object getPrimaryKey();
public abstract boolean isIdentical(EJBObject obj);
public abstract void remove();

These methods are implemented by the container in the EJB object class.

The javax.ejb.EJBHome interface
The home interface inherits two remove methods and the getEJBMetaData method from the javax.ejb.EJBHome interface. Just like the methods defined directly in the home interface, these inherited methods are also implemented in the EJB home class created by the container during deployment.

The remove methods are used to remove an existing EJB object (and its associated data in the database) either by specifying the EJB object's handle or its primary key. (The remove method that takes a primaryKey variable can be used only in entity beans.) The getEJBMetaData method is used to obtain metadata about the enterprise bean and is mainly intended for use by development tools.

These methods have the following syntax:


public abstract EJBMetaData getEJBMetaData();
public abstract void remove(Handle handle);
public abstract void remove(Object primaryKey);

The javax.ejb.EJBHome interface also contains a method to get a handle to the home interface. It has the following syntax:

public abstract HomeHandle getHomeHandle();

The java.io.Serializable and java.rmi.Remote interfaces
To be valid for use in a remote method invocation (RMI), a method's arguments and return value must be one of the following types:

If you attempt to use a parameter that is not valid, the java.rmi.RemoteException exception is thrown. Note that the following atypical types are not valid:


Using threads and reentrancy in enterprise beans

An enterprise bean must not contain code to start new threads (nor can methods be defined with the keyword synchronized). Session beans can never be reentrant; that is, they cannot call another bean that invokes a method on the calling bean. Entity beans can be reentrant, but building reentrant entity beans is not recommended and is not documented here.

The EJB server enforces single-threaded access to all enterprise beans. Illegal callbacks result in a java.rmi.RemoteException exception being thrown to the EJB client.


Creating an EJB module for enterprise beans

There are two tasks involved in preparing an enterprise bean for deployment:

If you develop enterprise beans in an IDE, these tasks are handled from within the tool that you use. If you do not develop enterprise beans in an IDE, you must handle each of these tasks by using tools contained in the Java Software Development Kit (SDK) and WebSphere Application Server. For more information on the tools used to create an EJB module in the EJB server programming environment, see Tools for developing and deploying enterprise beans.

Making bean components part of a Java package
You determine the best way to allocate your enterprise beans to Java packages. A Java package can contain one or more enterprise beans. The example Account and Transfer beans are stored in separate packages. All of the Java source files that make up the Account bean contain the following package statement:
package com.ibm.ejs.doc.account;

All of the Java source files that make up the Transfer bean contain the following package statement:

package com.ibm.ejs.doc.transfer;

Creating an EJB module and deployment descriptor

An EJB module contains one or more deployable enterprise beans. It also contains a deployment descriptor that provides information about each enterprise bean and instructions for the container on how to handle all enterprise beans in the module. The deployment descriptor is stored in an XML file.

During creation of the EJB module, you specify the files for each enterprise bean to be included in the module. These files include:

You also specify other information about the bean, such as references to other enterprise beans, resource connection factories, and security roles. After defining the enterprise beans to be included in the module, you specify application assembly instructions that apply to the module as a whole. Both bean and module information are used to create a deployment descriptor. See The deployment descriptor for a list of deployment descriptor settings and attributes.