Developing enterprise bean clients

A client of an enterprise bean is an application—a stand-alone application, servlet, or applet—or another enterprise bean. In all cases, the client must do the following things to use an enterprise bean:

The following sections describe the client application SortClient.java, that calls the sample SortBean session bean. SortBean is a stateless session bean that implements a merge/sort algorithm. Here is the code of SortClient:

// SortClient.java
...

public class SortClient {
  ...
  public static void main(String[] args) throws Exception {
    javax.naming.Context context;
    { // get a JNDI context using the Naming service
      context = new javax.naming.InitialContext();
    }
    Object objref = context.lookup("sort");
    SortHome home = (SortHome) javax.rmi.PortableRemoteObject.narrow(objref,
	    SortHome.class);
    Sort sort = home.create();
    ... //do the sort and merge work
    sort.remove();
  }
}


Locating the home interface

SortClient imports the required JNDI classes and the SortBean home and remote interfaces. The client uses the JNDI API to locate an enterprise bean's home interface. First the client must obtain a JNDI initial naming context. The code for SortClient instantiates a new javax.naming.Context object, which is called InitialContext. The client then uses the context lookup() method to resolve the name to a home interface.

The context's lookup() method returns an object of type java.lang.Object. Your code must cast this returned object to the expected type. The SortClient code shows a portion of the client code for the sort example. The main() routine begins by using the JNDI naming service and its context lookup() method to locate the home interface. You pass the name of the remote interface, which in this case is sort, to the context.lookup() method. Note that the program eventually casts the results of the context.lookup() method to SortHome, the type of the home interface.


Getting the remote interface

Once you have the home interface of an enterprise bean, you need a reference to the bean's remote interface. To do this, use the home interface's create or finder methods. Which method to use depends on the type of the enterprise bean and the methods the bean provider has defined in the home interface.

Session beans

If the enterprise bean is a session bean, the client uses a create method to return the remote interface. Session beans don't have finder methods. If the session bean is stateless, it will have just one create() method, so that is the one the client must call to obtain the remote interface. The default create() method has no parameters. So for the SortClient code sample, the call to the get the remote interface looks like this:
Sort sort = home.create();

The cart example discussed in "Developing session beans," on the other hand, uses a stateful session bean, and its home interface, CartHome, implements more than one create() method. One of its create() methods takes three parameters, which together identify the purchaser of the cart contents, and returns a reference to the Cart remote interface. The CartClient sets values for the three parameters—cardHolderName, creditCardNumber, and expirationDate—then calls the create() method. Here's the code:

Cart cart;
{
  String cardHolderName = "Jack B. Quick";
  String creditCardNumber = "1234-5678-9012-3456";
  Date expirationDate = new GregorianCalendar(2001, Calendar.JULY, 1).getTime();
  cart = home.create(cardHolderName, creditCardNumber, expirationDate);
}

Entity beans

If it's an entity bean, use either a create or a finder method to obtain the remote interface. Because an entity object represents some underlying data stored in a database, and that data is persistent, entity beans usually exist for a long time. Therefore, the client most often needs to simply find the entity bean that represents that data rather than create a new entity object, which would create and store new data in the underlying database.

A client uses a find operation to locate an existing entity object, such as a specific row within a relational database table. That is, find operations locate data entities that have previously been inserted into data storage. The data might have been added to the data store by an entity bean, or it might have been added outside of the EJB context, such as directly from within the database management system (DBMS). Or, in the case of legacy systems, the data might have existed prior to the installation of the EJB container.

A client uses an entity bean object's create() method to create a new data entity that will be stored in the underlying database. An entity bean's create() method inserts the entity state into the database, initializing the entity's variables according to the values in the create() method's parameters.

Each entity bean instance must have a primary key that uniquely identifies it. An entity bean instance can also have secondary keys that can be used to locate a particular entity object.

Finder methods and the primary key class

The default finder method for an entity bean is findByPrimaryKey(), which locates the entity object using its primary key value. This is its signature:
<remote interface> findByPrimaryKey(<key type> primaryKey)
Each entity bean must implement a findByPrimaryKey() method. The primaryKey parameter is a separate primary key class that is defined in the deployment descriptor. The key type is the type for the primary key, and it must be a legal value type in RMI-IIOP. The primary key class can be any class, such as a Java class or a class you've written yourself.

For example, suppose you have an Account entity bean for which you've defined the primary key class AccountPK. AccountPK, a String type, holds the identifier for the Account bean. To obtain a reference to a specific Account entity bean instance, set the AccountPK to the account identifier and call the findByPrimaryKey() method as shown here:

AccountPK accountPK = new AccountPK("1234-56-789");
Account source = accountHome.findByPrimaryKey(accountPK);

Bean providers can define additional finder methods that a client can use.

Create and remove methods

A client can also create entity beans using create methods defined in the home interface. When a client invokes a create method for an entity bean, the new instance of the entity object is saved in the data store. The new entity object always has a primary key value that is its identifier. Its state can be initialized to values passed as parameters to the create method.

Keep in mind that an entity bean exists for as long as data is present in the database. The life of the entity bean isn't bound by the client's session. The entity bean can be removed by calling one of the bean's remove methods. These methods remove the bean and the underlying representation of the entity data from the database. It's also possible to directly delete an entity object, such as by deleting a database record using the DBMS or with a legacy application.


Calling methods

Once the client has a reference to the bean's remote interface, it can invoke the methods defined in the remote interface for the bean. The client is most interested in the methods that embody the bean's business logic.

For example, the following is some code from a client that accesses the cart session bean. The code shown here begins from the point where it has created a new session bean instance for a card holder and retried a Cart reference to the remote interface. The client is ready to invoke the bean methods:

...
Cart cart;
{
  ...
  // obtain a reference to the bean's remote interface
  cart = home.create(cardHolderName, creditCardNumber, expirationDate);
}

// create a new book object
Book knuthBook = new Book("The Art of Computer Programming", 49.95f);

// add the new book item to the cart
cart.addItem(knuthBook);
...

// list the items currently in the cart
summarize(cart);
cart.removeItem(knuthBook);
...
First the client creates a new book object, setting its title and price parameters. Next it invokes the enterprise bean business method addItem() to add the book object to a shopping cart. The CartBean session bean defines the addItem() method, and the the Cart remote interface makes it public. The client adds other items (these aren't shown here), then calls its own summarize() method to list the items in the shopping cart. This is followed by the remove() method to remove the bean instance. Note that a client calls the enterprise bean methods in the same way that it invokes any method, such as its own summarize() method.


Removing bean instances

The remove() method operates differently for session beans than it does for entity beans. Because a session object exists for one client and isn't persistent, a client of a session bean should call the remove() method when it's finished with a session object. Two remove() methods are available to the client: the client can remove the session object with the javax.ejb.EJBObject.remove() method, or the client can remove the session handle with the javax.ejb.EJBHome.remove(Handle handle) method. For more information on bean handles, see "Referencing a bean with its handle."

While it isn't required that a client remove a session object, it's good programming practice. If a client doesn't remove a stateful session bean object, the container will eventually remove the object after a certain time, specified by a timeout value. The timeout value is a deployment property. A client can also keep a handle to the session for future reference, however.

Clients of entity beans don't have this problem as entity beans are associated with a client only for the duration of a transaction and the container is in charge of their life cycles, including their activation and passivation. A client of an entity bean calls the bean's remove() method only when the entity object is to be deleted from the underlying database.


Referencing a bean with its handle

A handle is an another way to reference an enterprise bean. A handle is a serializable reference to a bean. You can obtain a handle from the bean's remote interface. Once you have the handle, you can write it to a file (or other persistent storage). Later, you can retrieve the handle from storage and use it to reestablish a reference to the enterprise bean.

You can use the remote interface handle to recreate only the reference to the bean, however. You can't use it to recreate the bean itself. If another process has removed the bean, or the system removed the bean instance, then an exception is thrown when the client tries to use the handle to reestablish its reference to the bean.

When you aren't sure that the bean instance will still be in existence, rather than using a handle to the remote interface, you can store the bean's home handle and recreate the bean object later by invoking the bean's create or finder method.

After the client creates a bean instance, it can use the getHandle() method to obtain a handle to this instance. Once it has the handle, it can write it to a serialized file. Later, the client program can read the serialized file, casting the object that it reads in to a Handle type. Then, it calls the getEJBObject() method on the handle to obtain the bean reference, narrowing the results of getEJBObject() to the correct type for the bean.

For example, the CartClient program might do the following to use a handle to the CartBean session bean:

import java.io;
import javax.ejb.Handle;
...
Cart cart;
...
cart = home.create(cardHolderName, creditCardNumber, expirationDate);

// call getHandle() on the cart object to get its handle
cartHandle = cart.getHandle();

// write the handle to serialized file
FileOutputStream f = new FileOutputStream ("carthandle.ser");
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(myHandle);
o.flush();
o.close();
...

// read handle from file at later time
FileInputStream fi = new FileInputStream ("carthandle.ser");
ObjectInputStream oi = new ObjectInputStream(fi);

//read the object from the file and cast it to a Handle
cartHandle = (Handle)oi.readObject();
oi.close();
...

// Use the handle to reference the bean instance
Cart cart = (Cart) javax.rmi.PortableRemoteObject.narrow(cartHandle.getEJBObject(), 
    Cart class);
...
When it's finished with the session bean handle, the client can remove it by calling the javax.ejb.EJBHome.remove(Handle handle) method.


Managing transactions

A client program can manage its own transactions rather than letting the enterprise bean (or container) manage the transactions. A client that manages its own transactions does so in exactly the same manner as a session bean that manages its own transactions.

When a client manages its own transactions, it's responsible for delimiting the transaction boundaries. That is, it must explicitly start the transaction and end (commit or roll back) the transaction.

A client uses the javax.transaction.UserTransaction interface to manage its own transactions. It must first obtain a reference to the UserTransaction interface, using JNDI to do so. Once it has the UserTransaction context, the client uses the UserTransaction.begin() method to start the transaction, followed later by the UserTransaction.commit() method to commit and end the transaction (or UserTransaction.rollback() to rollback and end the transaction). In between, the client accesses EJB objects and so on.

The code shown here demonstrates how a client would manage its own transactions; the code that pertains specifically to client-managed transactions are highlighted in bold:

...
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
...
public class clientTransaction {
  public static void main (String[] argv) {
    InitialContext initContext = new InitialContext();
    UserTransaction ut = null;
    ut = (UserTransaction)initContext.lookup("java:comp/UserTransaction");
		
    // start a transaction
    ut.begin();
		
    // do some transaction work
    ...
		
    // commit or rollback the transaction
    ut.commit(); // or ut.rollback();
    ...
	}
}

For more information about transactions, see the chapter "Managing transactions."


Discovering bean information

Information about an enterprise bean is referred to as metadata. A client can obtain metadata about a bean using the enterprise bean's home interface getMetaData() method.

The getMetaData() method is most often used by development environments and tool builders that need to discover information about an enterprise bean, such as for linking together beans that have already been installed. Scripting clients might also want to obtain metadata on the bean.

Once the client retrieves the home interface reference, it can call its getEJBMetaData() method. Then, the client can call the EJBMetaData interface methods to extract such information as this:

Here's the EJBMetaData interface in its entirety:
package javax.ejb;

public interface EJBMetaData {
  EJBHome getEJBHome();
  Class getHome InterfaceClass();
  Class getRemoteInterfaceClass();
  Class getPrimaryKeyClass();
  boolean isSession();
  boolean isStatelessSession();
}


Creating a client with JBuilder

You can use JBuilder to give you a head start on creating your client. JBuilder has a EJB Test Client wizard that is intended to create a simple client application to test your enterprise bean. You can also use it to get started building your actual client application. Inform the wizard of the name of one of enterprise beans the client will access, and the wizard writes the code that gets a naming context, locates the bean's home interface and secures a reference to its remote interface.

Your client is likely to call multiple beans, however, so you'll have to perform these steps in your client code for other beans it accesses. And you'll add the calls that access the business logic of the enterprise beans to your client code yourself.

For more information on using the EJB Test Client wizard, see "Creating a test client application."