Developing session beans

JBuilder's EJB tools can greatly simplify the creation of enterprise beans and their supporting interfaces. You should understand what the requirements are for these classes and interfaces, however, so you can modify the files JBuilder produces and so you understand what JBuilder is doing for you. The next few chapters can help you gain that understanding.

A session bean usually exists for the lifetime of a single client session. The methods of a session bean perform a set of tasks or processes for the client that uses the bean. Session beans persist only for the life of the connection with the client. In a sense, the session bean represents the client in the EJB server. It usually provides a service to the client. Unless you need to work with persistent data that exists in a data store, you are usually working with session beans.


Types of session beans

There are two types of session beans: those that can maintain state information between method calls, which are called stateful beans, and those that can't, which are called stateless beans.

Stateful session beans

Stateful session beans are objects used by a single client and they maintain state on behalf of that client. For example, consider a shopping cart session bean. As the shopper in an online store selects items to purchase, the items are added to the "shopping cart" by storing the selected items in a list within the shopping cart session bean object. When the shopper is ready to purchase the items, the list is used to calculate the total cost.

Stateless session bean

Stateless session beans don't maintain state for any specific client. Therefore, they can be used by many clients. For example, consider a sort session bean that contains a sortList() business method. The client would invoke sortList(), passing it an unsorted list of items. sortList() would then pass back to the client a sorted list.


Writing the session bean class

To create a session bean class,

JBuilder's Enterprise JavaBean wizard can start these tasks for you, including creating the home and remote interfaces. It creates a class that extends the SessionBean interface and writes empty implementations of the SessionBean methods. You fill in the implementations if your bean requires them. The next section explains what these methods are and how they are used.

Extending the SessionBean interface

The SessionBean interface defines the methods all session beans must implement. It extends the EnterpriseBean interface.
package javax.ejb;
public interface Session extends EnterpriseBean {
    void setSessionContext(SessionContext sessionContext)
        throws EJBException, RemoteException;
    void ejbRemove() throws EJBException, RemoteException;
    void ejbActivate() throws EJBException, RemoteException;
    void ejbPassivate() throws EJBException, RemoteException;
}
The methods of the SessionBean interface are closely associated with the life cycle of a session bean. This table explains their purpose:

Method Description
setSessionContext() Sets a session context. The bean's container calls this method to associate a session bean instance with its context. The session context interface provides methods to access the runtime properties of the context in which a session runs. Usually a session bean retains its context in a data field.
ejbRemove() Notifies a session object that it is about to be removed. The container calls this method whenever it removes a stateful session bean as a result of the client calling a remove() method of the remote or home interface.
ejbActivate() Notifies a stateful session object that is has been activated.
ejbPassivate() Notifies a stateful session object that it is about to be deactivated by the container.

The ejbActivate() and ejbPassivate() methods allow a stateful session bean to manage resources. For more information, see Stateful session beans.

Writing the business methods

Within your enterprise bean class, write full implementations of the business methods your bean needs using JBuilder's code editor. To make these methods available to a client, you must also declare them in the bean's remote interface exactly as they are declared in the bean class. You can use JBuilder's Bean designer to perform that task for you. See the "Exposing business methods through the remote interface" topic in the "Developing enterprise beans with JBuilder" chapter.

Adding one or more ejbCreate() methods

If you use the Enterprise JavaBean wizard to begin your enterprise bean, you'll see that the wizard adds an ejbCreate() method to the bean class that takes no parameters. You can add additional ejbCreate() methods that do include parameters. While stateless session beans never need more than a parameterless ejbCreate() method because they don't retain any state, stateful session beans often need one or more ejbCreate() methods that have parameters. As you write additional ejbCreate() methods with parameters, keep these rules in mind:

This is the signature for all ejbCreate() methods of a session bean:

public void ejbCreate( <zero or more parameters> ) {
    // implementation
}
The ejbCreate() method need not throw an exception, although it can throw application-specific exceptions and other exceptions, such javax.ejb.CreateException. The Enterprise JavaBean wizard generates an ejbCreate() method that throws javax.ejb.CreateException.

How JBuilder can help you create a session bean

Using JBuilder's Enterprise JavaBean wizard, you can begin creating a session bean by selecting either the Stateless Session Bean or Stateful Session Bean option on the wizard's second page:

Not only does the Enterprise JavaBean wizard create your enterprise bean class, it also creates the bean's home and remote interfaces as it creates the bean class. This way, you are assured the create() method of the home interface returns the remote interface while the ejbCreate() method always returns void.

After you write the business methods in your bean class, you can use the Bean designer to specify which of those you want defined in the bean's remote interface. A client application can access only those methods defined in the remote interface. Once you specify which methods you want a client to be able to call, the Bean designer defines the methods in the remote interface for you.

If you already have a complete enterprise bean class, but don't have home and remote interfaces for it, you can use JBuilder's EJB Interfaces wizard to create them. The method signatures will comply with EJB 1.1 specifications in the home and remote interfaces without you having to worry about making them correct.

For information about using JBuilder's EJB tools to create session beans, see the "Developing enterprise beans with JBuilder" chapter.


The life of a session bean

Stateful and stateless session beans have different life cycles. You should understand what happens during the life cycle of each.

Stateless beans

The life of a stateless session bean begins when the client calls the create() method of the session bean's home interface. The container creates a new instance of the session bean and returns an object reference to the client.

During the creation process, the container invokes the setSessionContext() method of the SessionBean interface and calls the ejbCreate() method of the session bean implementation. The new bean object joins a pool of stateless bean objects that are ready for use by clients. Because stateless session objects don't maintain client-specific state, the container can assign any bean object to handle an incoming method call. When the container removes an object from the session bean object pool, it calls the ejbRemove() method of the bean object.

Note that calling the create() or remove() methods of the home/remote interfaces doesn't add or remove a stateless session bean object to or from the stateless session bean pool. The container controls the life cycle of stateless beans.

Stateful beans

The life of a stateful session bean begins when the client calls the create() method of the session bean's home interface. The container creates a new instance of the session bean, initializes it, and returns an object reference to the client.

During the creation process, the container invokes the setSessionContext() method of the SessionBean interface and calls the ejbCreate() method of the session bean implementation. As a bean provider, you can use these methods to initialize the session bean.

The state of the session bean is now method ready, which means it can perform nontransaction operations or be included in a transaction for transaction operations. The bean remains in the method-ready state until one of three things happens:

When a client calls the remove() method of the remote or home interface, the container invokes the corresponding ejbRemove() method on the session bean object. As a bean provider, you put any application-specific cleanup code in this method. After ejbRemove() completes, the bean object is no longer usable. If the client attempts to call a method in the bean object, the container throws the java.rmi.NoSuchObjectException.

The container can deactivate the session bean instance. Usually this occurs for resource management reasons such as when a session object is idle for a while or if the container requires more memory. The container deactivates the bean by calling the bean's ejbPassivate() method. When a bean instance is deactivated, which is called passivation, the container stores reference information and the state of the session object on disk and frees the memory allocated to the bean. You can add code to ejbPassivate() if you have some task you want to execute just before passivation occurs.

The container activates the bean object again by calling the ejbActivate() method. This occurs when the client calls a method on the session bean that is passivated. During activation, the container recreates the session object in memory and restores its state. If you want something to happen immediately after the bean becomes active again, add your code to the ejbActivate() method.

The method-ready in transaction state

When a client calls a method on a session bean object in a transactional context, the container starts a new transaction or includes the bean object in an existing one. The bean enters the method-ready in transaction state. There are points in a transaction's life cycle, called transaction synchronization points, where a session bean object can be notified of upcoming transaction events and the object can take some action beforehand if necessary.
The SessionSynchronization interface
A session bean can implement the SessionSynchronization interface if it wants to be notified about the state of any transaction in which it is involved. Only stateful session beans using container-managed transactions can implement SessionSynchronization; its use is optional. The methods of SessionSynchronization are callbacks made by the container into the bean, and they mark points within the transaction. Here is the SessionSynchronization interface:
public interface javax.ejb.SessionSynchronization
{
    public abstract void afterBegin() throws RemoteException;
    public abstract void beforeCompletion() throws RemoteException;
    public abstract void afterCompletion(boolean completionStatus) throws 
	    RemoteException;
}
The Enterprise JavaBean wizard can add these methods to your bean class. Using the wizard, check the Session Synchronization check box, and the wizard declares the three methods with empty bodies in your bean class:

The following table briefly describes each method:
Method Description
afterBegin() Notifies the bean instance that it is about to be used in a transaction. Any code you write within afterBegin() occurs within the scope of the transaction.
beforeCompletion() Notifies the bean instance that the transaction is about to commit. If the bean has any cached values, use beforeCompletion() to write them to the database. If necessary, a session bean could use the beforeCompletion() method to force the transaction to roll back by calling the setRollbackOnly() method of the SessionContext interface.
afterCompletion() Notifies the bean instance that the transaction has completed. If the transaction was committed, the parameter completionStatus is set to true. If the transaction was rolled back, the parameter is set to false.

This is how the SessionSynchronization methods are used: The client calls a transactional business method defined in the remote interface, which puts the bean object in the transaction-ready state. The container calls the afterBegin() method in the bean object. Later, if the transaction is committed, the container calls beforeCompletion(), and then, if the commit was successful, the afterCompletion(true) method. If the transaction was rolled back or otherwise failed to commit, the container calls afterCompletion(false). The session bean object is now in method-ready state again.

For more information about using session beans in transactions, see the "Managing transactions" chapter.


A shopping cart session bean

This example demonstrates the use of a stateful session enterprise bean, CartBean, that becomes a virtual shopping cart for an online store. Shoppers select items and put them in their virtual shopping carts. They can leave the site briefly, return, and add more things to their carts. Whenever they want, shoppers can view the items in their cart. When they are ready, they buy the items in the cart.

You can find complete code for the cart example in the /Inprise/AppServer/examples/ejb/cart directory.

Examining the files of the cart example

The cart example consists of a number of different files. This section focuses on those files that you might write yourself, or that illustrate interesting things about session beans. Some of the files in the cart directory are generated files (stubs, skeletons, and other CORBA code) that are not discussed here.

These are the key files:

The CartBean session bean

This section provides the details for how you might implement a CartBean session bean. You would begin by using the Enterprise JavaBean wizard. On the second page of the wizard, type in the name of the class as CartBean and select the Stateful Session Bean option and click Next. Accept the suggested home interface name, the remote interface name, and the bean home name, and choose Finish. The wizard creates a skeleton bean class for you:
package shoppingcart;
import java.rmi.*;
import javax.ejb.*;

public class CartBean implements SessionBean {

  private SessionContext sessionContext;
  
  public void ejbCreate() throws CreateException {
  }
  
  public void ejbRemove() throws RemoteException {
  }
  
  public void ejbActivate() throws RemoteException {
  }
  
  public void ejbPassivate() throws RemoteException {
  }
  
  public void setSessionContext(SessionContext context) throws RemoteException {
    sessionContext = context;
  }
}
A session bean class must be defined as public. It cannot be defined as final or abstract. The bean class must implement the SessionBean interface.

Session beans are Java objects, so they can have instance variables. CartBean has four instance variables you add to the class. The four variables are declared as private:

private Vector _items = new Vector();
private String _cardHolderName;
private String _creditCardNumber;
private Date _expirationDate;
Place these declarations after the sessionContext variable the Enterprise JavaBean wizard added to the class.

The _items variable holds the items owned by the cart object. It is a vector, a collection of items. The remaining three instance variables store the credit card information of the online shopper.

Required methods

A session bean must implement the four methods that are defined by the SessionBean interface. The EJB container invokes these methods on the bean instance at specific points in a session bean's life cycle. At a minimum, the bean provider must implement these methods with empty bodies. The bean provider can add additional code to these methods if it is needed. This CartBean session bean adds no code to these methods. These are the four methods:
  public void ejbRemove() {}
  public void ejbActivate() {}
  public void ejbPassivate() {}
  public void setSessionContext(SessionContext context) {}
The Enterprise JavaBean wizard adds all four of these methods. In the setSessionContext() method, the wizard assigns the value of the context parameter to the sessionContext instance variable.

The container calls the setSessionContext() method to associate the bean instance with its session context. The bean can choose to retain this session context reference as part of its conversational state, but it's not required to do so. If you used the Enterprise JavaBean wizard, the session context reference is held. The session bean can use the session context to get information about itself, such as environment variables or its home interface.

The container calls the ejbPassivate() method on the bean instance when it needs to place the bean instance into a passive state. The container writes the bean's current state to secondary storage when it passivates the bean. It restores this state when it later activates the bean. Because the container calls the ejbPassivate() method just before it actual passivates the bean instance, you, as the bean provider, can add code to this method to do any special variable caching you want. Similarly, the container calls the ejbActivate() method on the bean instance just prior to returning the bean instance to an active state from a passive state. When it activates the bean, it restores all persisted state values. You can choose to add code to the ejbActivate() method. The CartBean leaves these implementations empty.

While a session bean isn't required to implement a constructor, it must implement at least one ejbCreate() method. This method serves as a constructor to create a new bean instance. A stateful session can implement more than one ejbCreate() method. Each ejbCreate() method would differ only in their parameters.

The CartBean example declares one ejbCreate() method that takes three parameters:

public void ejbCreate(String cardHolderName, String creditCardNumber,
      Date expirationDate) throws CreateException {
      _cardHolderName = cardHolderName;
      _creditCardNumber = creditCardNumber;
}
The container calls the ejbRemove() method just prior to removing the bean instance. You can add some application-specific code that would execute before the bean is removed, but it isn't required. The CartBean example leaves the body of the ejbRemove() method empty.

Adding the business methods

There are a few rules that your business methods must follow: In the CartBean example, five business methods are implemented. The signatures (method name, number of parameters, parameter types, and return type) of the session bean class methods must match those of the remote interface. To ensure that happens, you can use the Bean designer to move the business methods you add to the bean class to the bean's remote interface.

To help you follow the flow of the program, each business method includes a line of code that displays the method name and what it is doing.

The addItem() method adds an item to the vector that holds the list of items in the cart:

public void addItem(Item item) {
    System.out.println("\taddItem(" + item.getTitle() + "): " + this);
    _items.addElement(item);
}
The removeItem() method is more complicated. The method loops through the elements in the item list and checks if the class and the title of the item to be removed match the class and title of one in the list. This method verifies that you are removing the item you really want removed. If no matching item is found, the method throws an ItemNotFoundException.
public void removeItem(Item item) throws ItemNotFoundException {
    System.out.println("\tremoveItem(" + item.getTitle() + "): " + this);
    Enumeration elements = _items.elements();
    while(elements.hasMoreElements()) {
      Item current = (Item) elements.nextElement();
      // items are equal if they have the same class and title
      if(item.getClass().equals(current.getClass()) &&
         item.getTitle().equals(current.getTitle())) {
         _items.removeElement(current);
         return;
    }
}
The getTotalPrice() method initializes the total price to zero, then loops through the item list, adding the price of each element to the total price. It returns the total price rounded to the nearest penny.
public float getTotalPrice() {
    System.out.println("\tgetTotalPrice(): " + this);
    float totalPrice = 0f;
    Enumeration elements = _items.elements();
    while(elements.hasMoreElements()) {
      Item current = (Item) elements.nextElement();
      totalPrice += current.getPrice();
    }
    // round to the nearest lower penny
    return (long) (totalPrice * 100) / 100f;
}
All data types passed between a client and a server must be serializable. That is, they must implement the java.io.Serializable interface. In the CartBean example, the bean returns a list of items to the client. If there were no serializable restriction, you could use _items.elements() to return the contents of the item vector. But _items.elements() returns a Java Enumeration object, which is not serializable. To avoid this problem, the program implements a class called com.inprise.ejb.util.VectorEnumeration(_items). This class takes a vector and returns an actual enumeration, which is serializable, for the contents of that vector. The CartBean object passes this serializable vector to the client, and receives a serializable vector passed from the client side. The getContents() method does the conversion between a Java Enumeration and a serializable VectorEnumeration.
public java.util.Enumeration getContents() {
    System.out.println("\tgetContents(): " + this);
    return new com.inprise.ejb.util.VectorEnumeration(_items);
}
The purchase() method should do the following:
  1. Get the current time.

  2. Compare the expiration date of the credit card with the current time. If the expiration date is prior to the current time, the method throws the CardExpiredException application exception.

  3. Complete the purchasing process, including updating inventory, posting the charge to the credit card company, and initiating shipment of the item. (None of this has actually been implemented in the CartBean example.) If an error occurs at any point, the purchase process does not complete and the method throws a PurchaseProblemException exception.
public void purchase() throws PurchaseProblemException {
    System.out.println("\tpurchase(): " + this);
    // make sure the credit card has not expired
    Date today = Calendar.getInstance().getTime();
    if(_expirationDate.before(today)) {
       throw new CardExpiredException("Expiration date: " + _expirationDate);
    }
    // complete purchasing process
    // throw PurchaseProblemException if an error occurs
}
CartBean includes a toString() method to print out the CartBean and the name of the card holder.
  // method to print out CartBean and the name of card holder
  public String toString() {
    return "CartBean[name=" + _cardHolderName + "]";
  }

Item class

The CartBean example uses an Item class. Item is public and it extends java.io.Serializable; serialized data can be passed on the wire:
package shoppingcart;
public class Item implements java.io.Serializable {
  private String _title;
  private float _price;
  public Item(String title, float price) {
    _title = title;
    _price = price;
  }
  public String getTitle() {
    return _title;
  }
  public float getPrice() {
    return _price;
  }
}

Exceptions

There are three exception classes in the CartBean example. All are extensions of the Java class Exception:
public class ItemNotFoundException extends Exception {
  public ItemNotFoundException(String message) {
      super(message);
  }
}
public class PurchaseProblemException extends Exception {
  public PurchaseProblemException(String message) {
    super(message);
  }
}
public class CardExpiredException extends Exception {
  public CardExpiredException(String message) {
    super(message);
  }
}

Required interfaces

Enterprise beans always have two interfaces: a home and a remote interface. In this example the CartBean session bean has a public EJB remote interface called Cart, and a home interface called CartHome.

When you use the Enterprise JavaBean wizard, the home and remote interfaces are created at the same time the bean class is. If you already have a session bean, but not the interfaces, use the EJB Interfaces wizard. To use the wizard, display the source code of the your enterprise bean in the code editor and choose Wizards|EJB Interfaces. Respond to the wizard's prompts and when you are done, the wizard creates a home and a remote interface for your enterprise bean.

For more information about using the Enterprise JavaBean wizard and the EJB Interfaces wizard, see the "Developing enterprise beans with JBuilder" chapter

The home interface

Like all other home interfaces, CartHome extends the EJBHome interface. While the home interface can potentially perform two actions, creating bean instances and finding bean instances, session beans need only to create a bean instance. Session beans always cease to exist when a client's session ends. Therefore, there would be no need to find a CartBean instance when a shopper enters an online store, for example, because a CartBean instance doesn't exist. Only home interfaces for entity beans include find operations, because entity beans are used by multiple clients and persist as long as the data entities exists. Here is the CartHome interface:
// CartHome.java
public interface CartHome extends javax.ejb.EJBHome {
  Cart create(String cardHolderName, String creditCardNumber,
              java.util.Date expirationDate)
    throws java.rmi.RemoteException,javax.ejb.CreateException;
}
The CartHome interface is very simple, defining a solitary create() method. Because this is a stateful session bean, there can be more than one create() method. In this example, create() method in the CartHome interface takes three parameters: cardHolderName, creditCardNumber, and expirationDate.

The client invokes the create() method to request a shopping cart and the container creates one specifically for that user. The client can use the shopping cart intermittently, but the session bean remains active for that one client until the user exits and the session bean instance is removed.

A stateful session bean maintains state across method calls, regardless of whether those methods are within the context of a transaction. The state is the data carried by an bean object. The data remains associated with the bean object for the lifetime of the object. When the session is over, the container flushes the state of session bean from memory.

The create() method follows the rules defined in the EJB specification: it throws an RMI remote exception, java.rmi.RemoteException, and it throws an EJB create exception, javax.ejb.CreateException. The signature of the create() method matches that of the ejbCreate() method in the session bean class it terms of the number and type of parameters. The return value of create() is a CartBean remote interface. This is because the CartHome interface functions as a factory for CartBean. (The return value for the matching ejbCreate() method in the CartBean class is void.)

The remote interface

The CartBean session bean has a remote interface Cart that extends the EJBObject interface. EJBObject is a base interface for all remote interfaces. It defines the methods that enable you to The Cart remote interface defines five business methods in addition to the methods it inherits from EJBObject. These business methods are the methods implemented in the CartBean session bean class. The Cart remote interface merely exposes these methods to clients. A client can call only the methods of an enterprise bean that the remote interface exposes. These are the exposed business methods:

The Cart deployment descriptor

According to the EJB 1.1 specification, the deployment descriptor must be an XML file. The XML file follows the Document Type Definition (DTD) approved by Sun Microsystems. A deployment descriptor contains a set of properties that describe how the container will deploy the enterprise bean or application.

As you use JBuilder's Enterprise JavaBean wizard to create a session bean, JBuilder also creates a deployment descriptor for you. You can then use the Deployment Descriptor editor to customize it to your needs.

The deployment descriptor includes a set of tags and attributes whose values indicate the properties of the bean. For example, some of the tags for the CartBean example are as follows:

Here is the deployment descriptor file for the cart session bean:
<ejb-jar>
  <enterprise-beans>
    <session>
      <description>
        XML deployment descriptor cart session bean
      <description>
      <ejb-name>cart</ejb-name>
     <home>CartHome</home>
       <remote>Cart</remote>
       <ejb-class>CartBean</ejb-class>
       <session-type>Stateful</session-type>
       <transaction-type>Container</transaction-type>
    <session>
  <enterprise-beans>
  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>cart</ejb-name>
        <method-name>*</method-name>
      </method>
      <trans-attribute>NotSupported</trans-attribute>
    </container-transaction>
    <container-transaction>
      <method>
        <ejb-name>cart</ejb-name>
        <method-name>purchase</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>
The CartClient program is the client application that uses the CartBean enterprise bean. Its main() routine includes elements that all enterprise bean client application must implement. It demonstrates how to
public static void main(String] args) throws Exception {
// get a JNDI context using the Naming service
javax.naming.Context context = new javax.naming.InitialContext();

Object objref = context.lookup("cart");
CartHome home = (CartHome)javax.rmi.PortableRemoteObject.narrow(objref, CartHome.class);
Cart cart;
{
    String cardHolderName = "Jack B. Quick";
    String creditCardNumber = "1234-5678-9012-3456";
    Date expirationDate = new GregorianCalendar(2001, Calendar.JULY,1).getTime();
    cart = home home.create(cardHolderName, creditCardNumber, expirationDate);
}
Book knuthBook = new Book("The Art of Computer Programming", 49.95f);
cart.addItem(knuthBook);
   ... // create compact disc and add it to cart, then list cart contents
summarize(cart)
cart.removeItem(knuthBook);
   ... // add a different book and summarize cart contents
try {
    cart.purchase();
    }
    catch(PurchaseProblemException e) {
        System.out.println("Could not purchase the items:\n\t" + e);
    }
cart.remove();
}
The main() routine begins by a JNDI context to look up objects. It constructs an initial context (a Java naming context). This is standard JNDI code.

main() looks up the CartHome object called cart. Looking up a name with JNDI invoking a call from the client to the CosNaming service to look up the name in CosNaming. The CosNaming service returns an object reference to the client. In this example, it returns a CORBA object reference. The program must perform a PortableRemoteObject.narrow() operation on the object reference and cast the returned object to the type CartHome, and assign it to the variable home. This call is typical for distributed applications. The call uses CORBA and IIOP to do the following:

The program declares a reference to the remote cart object, initializes three create() parameter variables, and creates a new cart remote object.

The program creates two shopping cart items, a book and a compact disc, and adds these items to the shopping cart using the cart's addItem() method.

The routine then lists the items current in the cart by calling the summarize() function. summarize() retrieves the elements or items in the cart using the cart's getContents() method, which returns a Java Enumeration. It then uses the Java Enumeration interface methods to read each element in the Enumeration, extracting the title and price for each one. Here is the summarize() function code:

static void summarize(Cart cart) throws Exception {
    System.out.println("======== Cart Summary =========")'
    Enumeration elements = cart.getContents();
    while(elements.hasMoreElements()) {
        Item current = (Item) elements.nextElement():
        System.out.println("Price: $" + current.getPrice() + "\t" +
          current.getClass().getName() + " title: " + current.getTitle());
    }
    System.out.println("Total: $" + cart.getTotalPrice());
    System.out.println("===============================");
}
The program then calls cart's removeItem() method to remove an item from the cart. It adds a different item and summarizes the cart contents again.

Finally, the program attempts to purchase the items. The purchase operation fails because it is not implemented on the server, and a PurchaseProblemException is thrown.

Because the user is finished with the shopping session, the program removes the cart. It's not necessary to remove the cart, although it is good programming practice to do so. A session bean exists for the client that created it. When the client ends the session, the container automatically removes the session bean object. The container also removes the session bean object when it times out, although this doesn't happen immediately.

CartClient also includes code that extends the generic Item class with two types of items: a book and a compact disc. Book and CompactDisc are the classes used in the example.