6.5. Session Facade Pattern

Consider what we need to do to integrate our Customer EJB into the Music Collection virtual shopping application (return to Figure 6-2 on page 205). When a user accesses the “login” web page, the JSP client asks for a login name and password. The client must determine if the user name is valid and if the password matches the given user name. We accomplish this multiple-step process by invoking method findByCustomerName(), which returns a collection. The client must then verify that only one Customer instance is in the collection. It then must call getPassword() and check to see if the entity bean's password matches the one provided by the user.

For a JSP client or stand-alone Java client to accomplish this task, we must issue more than one remote call. Multiple remote calls from many clients can quickly cause a network bottleneck. A better approach is to encapsulate the steps of a business process into a single method. Using the Session Facade Pattern, we can create a session bean that delegates multiple-step business methods to our Customer EJB entity bean. Figure 6-7 shows the approach. The diagram illustrates the difference between having the remote client access the Customer EJB directly (top half) compared to accessing the entity bean through the Session Facade (bottom half). Within the Session Facade implementation, the identifyCustomer() method is a remote call, whereas findByCustomerName() and getPassword() are local calls. Not only does a Session Facade improve performance here, but it also provides a simpler interface to the client.

Figure 6-7. Sequence Diagram Showing the Advantages of Using a Session Facade


The Session Facade can (and typically will) implement more than one business process.

A session facade can use a stateless or stateful session bean and offers the following advantages:

  • The session bean encapsulates the business process in one place and is callable from any client. Only one remote call is necessary, which replaces the multiple-remote call sequence. Using a session bean facade thus simplifies the client.

  • The session bean can access the entity bean with local interfaces, minimizing the number of remote calls. Thus, the multiple, fine-grained calls become local calls, improving performance.

  • Using a session facade isolates the entity bean from general clients. If we need to change our underlying database structure, changes to the client are unlikely. And (depending on how radically we alter the interface to our entity bean), changes will be limited to the session facade.

  • Since session bean methods execute within transactions, we can perform multiple-step entity bean accesses (which may include database updates) easily within a single transaction. Thus, a session facade simplifies multiple-step operations with entity beans by creating a single, transactional session bean method.

Because we store customer data information in the database only, our session bean can be stateless. This session bean provides a simplified interface to the Customer EJB and isolates the entity bean from general clients such as web components or stand-alone Java clients. The CustomerSession EJB will only access methods in the Customer EJB. In the next chapter we'll implement the Order EJB and LineItem EJB, and add methods for a coordinated interface to all three entity beans.

Home Interface

CustomerSession EJB's home interface contains a single create() method since CustomerSession EJB is a stateless session bean. Listing 6.16 contains the source for CustomerSessionHome.java.

Listing 6.16. CustomerSessionHome.java
// CustomerSessionHome.java
import java.io.Serializable;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface CustomerSessionHome extends EJBHome {

    CustomerSession create()
         throws RemoteException, CreateException;
}

Remote Interface

CustomerSession EJB's remote interface contains the business methods. These methods manipulate the Customer EJB and perform such tasks as creating a new customer (method createCustomer()) or verifying a particular customer's password (identifyCustomer()). Listing 6.17 contains the source for CustomerSession.java.

Listing 6.17. CustomerSession.java
// CustomerSession.java
import javax.ejb.*;
import java.rmi.RemoteException;
import java.util.*;

public interface CustomerSession extends EJBObject {

  public int getTotalCustomers()
    throws RemoteException;

  public void createCustomer(CustomerVO customer)
      throws CreateException, RemoteException;

  public void changePassword(String name, String password)
      throws CustomerIdentityException, CustomerException,
      RemoteException;

  public void changeEmail(String name, String email)
      throws CustomerIdentityException, CustomerException,
      RemoteException;

  public void identifyCustomer(String name,
      String password)
      throws CustomerIdentityException, RemoteException;

  public boolean getOrdersPending(String name)
      throws CustomerIdentityException, RemoteException;
  public Collection getCustomers() throws RemoteException;
}

Method identifyCustomer() throws a CustomerIdentityException if the specified password does not match the Customer's record as identified by argument name. Method getOrdersPending() returns the OrdersPending field of the customer specified in name; it throws a CustomerIdentityException if customer name cannot be found. Method getCustomers() returns a collection of CustomerVO objects built from the Customer database.

Application Exceptions

The CustomerSession EJB uses two application exceptions to detect and send errors back to the client. The CustomerIdentityException indicates that the client is attempting to perform some task involving a customer identified by name, but the name cannot be located in the database. We also use the CustomerIdentityException to indicate that the given name and password combination do not match.

The CustomerException indicates any other problems associated with the Customer EJB or the underlying database.

These two exceptions are used exclusively by the CustomerSession EJB to manage error detection between client requests and the Customer EJB. Listing 6.18 shows the source for CustomerIdentityException.java.

Listing 6.18. CustomerIdentityException.java
// CustomerIdentityException.java
// Application Exception
public class CustomerIdentityException extends Exception {

   public CustomerIdentityException() { }

   public CustomerIdentityException(String msg) {
      super(msg);
   }
}

Listing 6.19 contains the source for CustomerException.java.

Listing 6.19. CustomerException.java
// CustomerException.java
// Application Exception
public class CustomerException extends Exception {

   public CustomerException() { }

   public CustomerException(String msg) {
      super(msg);
   }
}

Bean Implementation

Listing 6.20 is the implementation code for the CustomerSession EJB. Its primary job is to translate client requests into the proper sequence of business method invocations on the Customer EJB. The business method identifyCustomer(), for example, must use the Customer EJB finder method to find the correct Customer entity bean and compare the password in its argument with the password stored in the bean. The client can assume that the Customer is correctly identified as long as the CustomerSession EJB does not throw a CustomerIdentityException.

Note that the createCustomer() method performs parameter checking on the values in the CustomerVO object before creating a Customer entity bean. It also makes sure that the name does not already exist in the database.

The business methods in the CustomerSession EJB all execute within a transaction. During deployment, we specify container-managed transactions and assign transaction attribute Required to each business method. For business methods that perform multiple-step operations, this provides all the ACID properties we need for transactional processing. In createCustomer(), for instance, we call findByCustomerName() to verify that a customer name is not already in use. If the name does not exist, we proceed to create the new customer record using that name. If this method doesn't execute within a single transaction, another client could conceivably create a customer record using the same name in between the return of findByCustomerName() and the completion of the create() call. By using a transaction in createCustomer(), we ensure that this multiple-step procedure is treated as a single unit of work (satisfying the requirements for atomicity and consistency).

Listing 6.20. CustomerSessionBean.java
// CustomerSessionBean.java
import java.rmi.RemoteException;
import javax.ejb.*;
import javax.naming.*;
import java.util.*;

public class CustomerSessionBean implements SessionBean {

  // initialize in ejbCreate()
  private CustomerLocalHome customerHome;

  // Business methods
  // Return the number of Customers in the database
   public int getTotalCustomers() {
    return customerHome.getTotalCustomers();
  }

  // Create a new Customer in the CustomerDB
  // verify that the name is unique and the password
  // is non-empty.

  public void createCustomer(CustomerVO customer)
      throws CreateException, FinderException {
    if (customer.getName() == null
        || customer.getPassword() == null
        || customer.getEmail() == null) {
      throw new CreateException("Customer data is null");
    }

    if (customer.getName().equals("")
        || customer.getPassword().equals("")
        || customer.getEmail().equals("")) {
      throw new CreateException(
                 "Customer fields cannot be empty.");
    }
    Collection c =
      customerHome.findByCustomerName(customer.getName());
    if (c.size() == 0) {
      customerHome.create(
        customer.getName(),
        customer.getPassword(),
        customer.getEmail());
    }
    else {
      throw new CreateException(
            "Customer name already in use.");
    }
  }

  // Change the password identified by customer 'name'.
  // Make sure customer is in the database
  // Throws CustomerIdentityException if no match.
  // Throws CustomerException for other problems.

  public void changePassword(String name, String password)
      throws CustomerIdentityException, CustomerException,
      FinderException {
    if (password.equals("")) {
      throw new CustomerException(
               "Password cannot be empty");
    }

    Collection c = customerHome.findByCustomerName(name);
    if (c.size() == 1) {
      Iterator i = c.iterator();
      CustomerLocal cust = (CustomerLocal)i.next();
      cust.setPassword(password);
    }
    else {
      throw new CustomerIdentityException(
        "Cannot find customer " + name);
    }
  }
  // Change the email for the customer 'name'.
  // Make sure customer is in the database
  // Throws CustomerIdentityException if no match.
  // Throws CustomerException for other problems.

  public void changeEmail(String name, String email)
      throws CustomerIdentityException, CustomerException,
      FinderException {
    if (email.equals("")) {
      throw new CustomerException(
               "Email cannot be empty");
    }

    Collection c = customerHome.findByCustomerName(name);
    if (c.size() == 1) {
      Iterator i = c.iterator();
      CustomerLocal cust = (CustomerLocal)i.next();
      cust.setEmail(email);
    }
    else {
      throw new CustomerIdentityException(
             "Cannot find customer " + name);
    }
  }

  // Given a customer name, make sure the password
  // matches the customer's name in the database
  // Throws CustomerIdentityException if no match.

  public void identifyCustomer(String name,
      String password) throws CustomerIdentityException,
       FinderException {

    Collection c = customerHome.findByCustomerName(name);
    if (c.size() == 1) {
      Iterator i = c.iterator();
      CustomerLocal cust = (CustomerLocal)i.next();
      if (!cust.getPassword().equals(password)) {
        throw new CustomerIdentityException(
            "Incorrect Password for customer " + name);
      }
    }
    else {
      throw new CustomerIdentityException(
          "Cannot find customer " + name);
    }
  }

  // For customer 'name', return the ordersPending field.
  // Throw CustomerIdentityException if customer
  // cannot be found.

  public boolean getOrdersPending(String name)
      throws CustomerIdentityException,
      FinderException {

    Collection c = customerHome.findByCustomerName(name);

    if (c.size() == 1) {
      Iterator i = c.iterator();
      CustomerLocal cust = (CustomerLocal)i.next();
      return cust.getOrdersPending();
    }
    else {
      throw new CustomerIdentityException(
             "Cannot find customer " + name);
    }
  }
  // Get all the customers in the customer database:
  // Return an ArrayList of CustomerVOs

  public Collection getCustomers()
               throws FinderException {

    // return an ArrayList of CustomerVOs
    ArrayList customerList = new ArrayList();

    Collection a = customerHome.findAll();
    Iterator i = a.iterator();

    while (i.hasNext()) {
      CustomerLocal customerEJB = (CustomerLocal)i.next();

      CustomerVO customer =
        new CustomerVO(customerEJB.getName(),
          customerEJB.getPassword(),
          customerEJB.getEmail());
      customerList.add(customer);
    }
    return customerList;
  }

   // EJB Methods
  public CustomerSessionBean() {}

  public void ejbCreate() {
    try {
      Context initial = new InitialContext();

      // Find Local Home Interface to CustomerEJB
      Object objref =
            initial.lookup("java:comp/env/ejb/Customer");
      customerHome = (CustomerLocalHome)objref;

    } catch (Exception ex) {
      ex.printStackTrace();
      throw new EJBException(ex.getMessage());
    }
  }
   public void ejbRemove() {}
   public void ejbActivate() {}
   public void ejbPassivate() {}
   public void setSessionContext(SessionContext sc) {}

} // CustomerSessionBean

Deployment Descriptor

Listing 6.21 shows portions of the deployment descriptor for the EJB JAR file that holds the CustomerSession EJB. The CustomerSession EJB is a stateless session bean with container-managed transactions. It references the Customer entity bean through its local and local home interfaces. Under the <assembly-descriptor> tag, the deployment descriptor lists all its transactional methods, along with the transaction attribute we assign (in all cases, attribute Required). The <method> tag identifies methods with transactions. We show the <method> tags for getTotalCustomers() and createCustomer() only.

Listing 6.21. Deployment Descriptor for Customer JAR File
<ejb-jar>
 <display-name>CustomerJAR</display-name>
 <enterprise-beans>
    <session>

     <display-name>CustomerSessionBean</display-name>
      <ejb-name>CustomerSessionBean</ejb-name>
      <home>CustomerSessionHome</home>
      <remote>CustomerSession</remote>
      <ejb-class>CustomerSessionBean</ejb-class>
      <session-type>Stateless</session-type>
      <transaction-type>Container</transaction-type>

       <ejb-local-ref>
        <ejb-ref-name>ejb/Customer</ejb-ref-name>
        <ejb-ref-type>Entity</ejb-ref-type>
        <local-home>CustomerLocalHome</local-home>
        <local>CustomerLocal</local>
        <ejb-link>CustomerBean</ejb-link>
      </ejb-local-ref>
 <security-identity>
        <description></description>
        <use-caller-identity></use-caller-identity>
      </security-identity>
    </session>
 </enterprise-beans>

 <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>CustomerSessionBean</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>getTotalCustomers</method-name>
        <method-params />
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>

    <container-transaction>
      <method>
        <ejb-name>CustomerSessionBean</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>createCustomer</method-name>
        <method-params>
          <method-param>CustomerVO</method-param>
        </method-params>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
   . . .
 </assembly-descriptor>
</ejb-jar>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.190.175