- Introducing Stateful Session Beans
- MusicCart Stateful Session Bean
- Value List Iterator Pattern
- Web Component Client
- Local Interfaces
- Design Guidelines and Patterns
- Key Point Summary
5.2 MusicCart Stateful Session Bean
The MusicCart EJB is a stateful session bean that allows clients to create a shopping cart and add and remove recordings (read from the Music Collection database). Before we show you the code, we'll describe a general, architectural view of the major components of our example enterprise application.
The Big Picture
Figure 5-1 presents an architectural overview of our design using J2EE components. A client becomes the "customer" which we encapsulate in the value object CustomerVO(not shown). The stateful session bean MusicCart (denoted SFSB) manipulates RecordingVO objects (see Listing 4.4 on page 96) which originate from the database store we presented in the previous chapter. To keep database access vendor independent, we use the MusicDAO implementation from Chapter 4 (see "DAO Pattern Implementation" on page 121). A new, stateful MusicIterator session bean (denoted SFSB) implements the Value List Iterator Pattern. The Value List Iterator Pattern allows a client to request data in small, manageable chunks instead of all at once. In addition, the stateful MusicIterator EJB accesses the stateless session bean MusicPage (denoted SLSB) to read the data. Finally, the user interacts with all these components through a JSP web component that manages the user input and presentation aspects of the application.
Figure 5-1 Architectural Overview of the Music Shopping Cart Enterprise Application
Figure 5-2 is a sequence diagram that shows the interactions between the client and the MusicCart EJB. The diagram shows the creation of the MusicCart EJB, which in turn creates a CustomerVO and shopping ArrayList object. It also depicts the MusicCart EJB's business methods.
Figure 5-2 Sequence Diagram Showing Interactions Between a Client and a MusicCart EJB
As we present each component, we'll discuss the design issues that motivate our choice of stateful versus stateless session bean implementation.
CustomerVO
Listing 5.1 shows the class definition for value object CustomerVO, which holds a String name (name), a String password (password), and a String e-mail address (email). Its constructor takes three String arguments, and the class provides the typical getter and setter methods of a value object.
Note that CustomerVO implements the java.io.Serializable interface. There are two reasons for this. One, CustomerVOmust be serializable because we use it in remote EJB calls as an argument. Two, the MusicCart EJB will use a CustomerVOinstance variable to hold state information for a specific customer. This field must be serializable for possible activation and passivation in the MusicCart EJB stateful session bean. Remember that fields declared as instance variables in stateful session beans must either be serializable or specifically passivated and activated.
Listing 5.1 CustomerVO.java
// CustomerVO.java public class CustomerVO implements java.io.Serializable { private String name; private String password; private String email; public CustomerVO(String name, String password, String email) { setName(name); setPassword(password); setEmail(email); } // getters public String getPassword() { return password; } public String getName() { return name; } public String getEmail() { return email; } // setters public void setPassword(String password) { this.password = password; } public void setName(String name) { this.name = name; } public void setEmail(String email) { this.email = email; } }
Home Interface
A stateful session bean has the following: a home interface containing one or more create()methods, a remote interface with business methods, and a bean implementation class with the code for the methods in both interfaces. Let's look at the home interface of our MusicCart EJB first.
Because the MusicCart EJB is stateful, there may be multiple ways to create the bean and initialize its state. In Listing 5.2, you will find two create() methods in the home interface of the MusicCart EJB. The first one accepts only a customer name, whereas the second one creates the bean from a CustomerVO object. Note that both create()methods return an object of the bean's remote interface type (MusicCart). And, as we've seen in previous examples, each create() method must specify RemoteExceptionand CreateExceptionin its throws clause. The home interface always extends EJBHome.
Listing 5.2 MusicCartHome.java
// MusicCartHome.java import java.io.Serializable; import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; public interface MusicCartHome extends EJBHome { MusicCart create(String person) throws RemoteException, CreateException; MusicCart create(CustomerVO customer) throws RemoteException, CreateException; }
Remote Interface
The MusicCart EJB remote interface in Listing 5.3 contains the bean's business methods. We have methods for adding and removing recordings to the shopping cart, obtaining the current shopping list, and querying customer information.
Listing 5.3 MusicCart.java
// MusicCart.java import javax.ejb.EJBObject; import java.rmi.RemoteException; import java.util.*; public interface MusicCart extends EJBObject { public void addRecording(RecordingVO album) throws RemoteException; public void removeRecording(RecordingVO album) throws ShoppingException, RemoteException; public void clearShoppingList() throws RemoteException; public ArrayList getShoppingList() throws RemoteException; public CustomerVO getCustomer() throws RemoteException; }
The addRecording() and removeRecording() methods both accept RecordingVOvalue objects as arguments. Method getShoppingList()returns an ArrayList collection of recordings and method clearShoppingList() removes all objects from the shopping list. The removeRecording() method also specifies ShoppingExceptionin its throwsclause. This application exception indicates that the client requested removal of a recording that was not in the shopping cart.
Note that addRecording(), clearShoppingList(), and removeRecording() update the MusicCart's state, whereas getShoppingList() and get-Customer() are getter methods that return portions of the bean's state.
Bean Implementation
The bean implementation class for the MusicCart EJB is shown in Listing 5.4. Because this session bean is stateful, it must have instance variables that maintain state information. For our MusicCart EJB, this state consists of a Custom-erVOvalue object (customer) and an ArrayListof recordings in the shopping cart (shoppingList). Because both of these variables are serializable, we do not need to write implementation code for methods ejbPassivate()and ejbAc-tivate(). The bean class also contains the implementations of the home and remote interface methods.
Each create()method in the home interface has a corresponding ejbCre-ate() method in the bean implementation class with matching arguments. Note that create() always returns a remote interface type but ejbCreate() has void for the return type.
The job of ejbCreate() is to make sure that the EJB container instantiates the MusicCart EJB object correctly. Both ejbCreate()methods check for valid initialization parameters before they initialize the shoppingListand customer instance variables. If either ejbCreate() method finds inconsistencies in the input parameters, it throws a CreateException.
The ArrayList collection is serializable and stores recordings in the shopping cart. Its methods add(), contains(), clear(), and remove()manipulate the collection of recordings.
Implementation Factoid
ArrayList method contains() returns true if the collection contains the specified object. ArrayList uses the Java Object's equals() method to determine equality. This is why we implement method equals() in class RecordingVO.java (see Listing 4.4 on page 96).
Note that removeRecording()checks to see if the recording to be removed is actually in the collection. If it isn't, the method throws a ShoppingException. A client of the MusicCart EJB should check for this exception separately from a more drastic system exception.
Listing 5.4 MusicCartBean.java
// MusicCartBean.java import javax.ejb.*; import javax.naming.*; import java.rmi.RemoteException; import java.util.*; public class MusicCartBean implements SessionBean { // EJB instance variables CustomerVO customer; ArrayList shoppingList; // create() methods implementation // from the EJBHome interface // Methods ejbCreate() should initialize the // instance variables public void ejbCreate(String person) throws CreateException { if (person == null || person.equals("")) { throw new CreateException( "Name cannot be null or empty."); } else { customer = new CustomerVO(person, "NoPassword", "NoEmail"); } shoppingList = new ArrayList(); } // Implementation for create(CustomerVO cust) public void ejbCreate(CustomerVO cust) throws CreateException { if (cust.getName() == null) { throw new CreateException("Name cannot be null."); } if (cust.getPassword() == null || cust.getPassword().equals("")) { throw new CreateException( "Password cannot be null or empty."); } if (cust.getEmail() == null || cust.getEmail().equals("")) { throw new CreateException( "Email cannot be null or empty."); } customer = cust; shoppingList = new ArrayList(); } // Business methods implementation public CustomerVO getCustomer() { return customer; } public void addRecording(RecordingVO album) { shoppingList.add(album); } // ShoppingException is an application exception public void removeRecording(RecordingVO album) throws ShoppingException { if (shoppingList.contains(album)) { shoppingList.remove(shoppingList.indexOf(album)); } else { throw new ShoppingException( album.getTitle() + " not in cart."); } } // clear the shopping list public void clearShoppingList() { shoppingList.clear(); } public ArrayList getShoppingList() { return shoppingList; } // EJB Methods public MusicCartBean() {} public void ejbRemove() {} public void ejbActivate() {} public void ejbPassivate() {} public void setSessionContext(SessionContext sc) {} } // MusicCartBean
Deployment Descriptor
Listing 5.5 shows the deployment descriptor for the MusicCart EJB. Here, we indicate that it is a stateful session bean, its home interface is MusicCartHome, its remote interface is MusicCart, and its bean implementation class is Music-CartBean. We show tag values in bold.
Listing 5.5 MusicCart EJB Deployment Descriptor
<ejb-jar> <display-name>MusicCartJAR</display-name> <enterprise-beans> <session> <display-name>MusicCartBean</display-name> <ejb-name>MusicCartBean</ejb-name> <home>MusicCartHome</home> <remote>MusicCart</remote> <ejb-class>MusicCartBean</ejb-class> <session-type>Stateful</session-type> <transaction-type>Bean</transaction-type> <security-identity> <description></description> <use-caller-identity></use-caller-identity> </security-identity> </session> </enterprise-beans> </ejb-jar>
Before we examine the other EJBs in our MusicCart design, let's learn about the Value List Iterator Pattern. This important pattern helps you understand the advantages of both stateless and stateful session bean properties.