Using Enterprise Beans

Let’s look at how a client would use an enterprise bean to do something useful. We’ll start with the Cabin EJB defined earlier. A cabin is a thing or place with a description that is stored in a database. To make the example a little more real, assume that there are other entity beans: Ship, Cruise, Ticket, Customer, Employee, and so on.

Getting Information from an Entity Bean

Imagine that a GUI client needs to display information about a particular cruise: the cruise name, the ship name, and a list of cabins. Using the cruise ID obtained from a text field, we can use our beans to look up data about the cruise. Here’s the code:

CruiseHomeRemote cruiseHome = ... ; // use JNDI to get the home
// Get the cruise ID text field 1.
String cruiseID = textField1.getText( );
// Create an EJB primary key from the cruise ID.
Integer pk = new Integer(cruiseID);
// Use the primary key to find the cruise.
CruiseRemote cruise = cruiseHome.findByPrimaryKey(pk);
// Set text field 2 to show the cruise name.
textField2.setText(cruise.getName( ));
// Get a remote reference to the ship that will be used
// for the cruise from the cruise bean.
ShipRemote ship = cruise.getShip( );
// Set text field 3 to show the ship's name.
textField3.setText(ship.getName( ));

// Get all the cabins on the ship.
Collection cabins = ship.getCabins( );
Iterator cabinItr = cabins.iterator( );

// Iterate through the enumeration, adding the name of each cabin
// to a list box.
while(cabinItr.hasNext( ))
    CabinRemote cabin = (CabinRemote)cabinItr.next( );
    listBox1.addItem(cabin.getName( ));
}

We start by getting a remote reference to the EJB home for an entity bean that represents a cruise. We need a remote reference rather than a local one because the client is an application located outside the EJB container. It’s not shown in the example, but references to the EJB home are obtained using JNDI. JNDI is a powerful API for locating resources, such as remote objects, on networks. JNDI lookups are covered in subsequent chapters.

We read a cruise ID from a text field, use it to create a primary key, and use that primary key together with the EJB home to get a CruiseRemote reference. This reference implements the bean’s business methods. Once we have the appropriate Cruise EJB, we can ask the bean to give us a remote reference to a Ship EJB that represents the ship used for the cruise. We can then call the ship.getCabins( ) method to get a Collection of remote Cabin EJB references from the Ship EJB, and, with the Cabin EJBs in hand, we can retrieve and display the names of the Cabin EJBs.

Modeling Taskflow with Session Beans

Entity beans are useful for representing data and describing business concepts that can be expressed as nouns, but they’re not very good at representing a process or a task. A Ship bean provides methods and behavior for doing things directly to a ship, but it does not define the context under which these actions are taken. The previous example retrieved data about cruises and ships; we could also have modified this data. With enough effort, we could have figured out how to book a passenger—perhaps by adding a Customer EJB to a Cruise EJB, or adding a customer to a list of passengers maintained by the ship. We could try to shove methods for accepting payment and other tasks related to booking into our GUI client application, or even into the Ship or Cabin EJBs, but that’s a contrived and inappropriate solution. We don’t want business logic in the client application—that’s why we went to a multitier architecture in the first place. Similarly, we don’t want this kind of logic in our entity beans that represent ships and cabins. Booking passengers on a ship or scheduling a ship for a cruise are the types of activities or functions of the business, not the Ship or the Cabin bean, and are therefore expressed in terms of a process or task.

Session beans act as agents that manage business processes or tasks for the client; they’re the appropriate place for business logic. A session bean is not persistent; nothing in a session bean maps directly into a database or is stored between sessions. Session beans work with entity beans, data, and other resources to control taskflow . Taskflow is the essence of any business system, because it expresses how entities interact to model the actual business. Session beans control tasks and resources but do not themselves represent data.

Tip

The term “taskflow” was coined specifically for this book. It’s derived from the term “workflow,” which is frequently used to describe the management of business processes that may span several days with lots of human intervention. In contrast to workflow, the term taskflow is used in this book to describe the interactions of beans within a single transaction that takes only a few seconds to execute.

The following code demonstrates how a session bean designed to make cruise-line reservations might control the taskflow of other entity and session beans. Imagine that a piece of client software, in this case a user interface, obtains a remote reference to a TravelAgent session bean. Using the information entered into text fields by the user, the client application books a passenger on a cruise:

// Get the credit card number from the text field.
String creditCard = textField1.getText( );
int cabinID = Integer.parseInt(textField2.getText( ));
int cruiseID = Integer.parseInt(textField3.getText( ));

// Create a new Reservation session passing in a reference to a 
// customer entity bean.
TravelAgent travelAgent = travelAgentHome.create(customer);

// Set cabin and cruise IDs.
travelAgent.setCabinID(cabinID);
travelAgent.setCruiseID(cruiseID);

// Using the card number and price, book passage.
// This method returns a Ticket object.
TicketDO ticket = travelAgent.bookPassage(creditCard, price);

This is a fairly coarse-grained abstraction of the process of booking a passenger: most of the details are hidden from the client. Hiding the fine-grained details of taskflow is important because it provides the system with flexibility as it evolves: we know that we will always want to book passengers, but the process for booking a passenger may change.

Tip

Course-grained and fine-grained are terms that are sometimes used to describe the level of detail exposed by the public interface of a component. A component whose public interfaces exposes a lot of detail about how the component funtions is called fine-grained. Components that provide a public interface but do not expose the details of its operation are called coarse-grained. When dealing with remote clients, coarse-grained interfaces are usally prefered because they are more flexible—the client doesn’t have to be aware of all the nitty-gritty details of how the component works.

The following listing shows some of the code for the TravelAgentBean. The bookPassage( ) method works with three entity beans, the Customer, Cabin, and Cruise EJBs, and another session bean, the ProcessPayment EJB. The ProcessPayment EJB provides several methods for making a payment, including check, cash, and credit card. In this case, we use the ProcessPayment bean to make a credit card payment. Once payment has been made, a serializable TicketDO object is returned to the client.

public class TravelAgentBean implements javax.ejb.SessionBean {
    public CustomerRemote customer;
    public CruiseRemote cruise;
    public CabinRemote cabin;

    public void ejbCreate(CustomerRemote cust){
        customer =cust;
    }
    public TicketDO bookPassage(CreditCardDO card,double price)
        throws IncompleteConversationalState {
        if (customer == null ||cruise == null ||cabin == null){
            throw new IncompleteConversationalState( );
        }
        try {
            ReservationHomeRemote resHome = (ReservationHomeRemote)
                getHome("ReservationHome",ReservationHomeRemote.class);
            ReservationRemote reservation =
                resHome.create(customer,cruise,cabin,price,new Date( ));
            ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
                getHome("ProcessPaymentHome",ProcessPaymentHomeRemote.class);

            ProcessPaymentRemote process = ppHome.create( );
            process.byCredit(customer,card,price);

            TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
            return ticket;
        }catch(Exception e){
            throw new EJBException(e);
        }
    }

// More business methods and callback methods follow
}

This example leaves out some details, but it demonstrates the difference in purpose between a session bean and an entity bean. Entity beans represent the behavior and data of a business object, while session beans model the taskflow. The client application uses the TravelAgent EJB to perform a task using other beans. For example, the TravelAgent EJB uses a ProcessPayment EJB and a Reservation EJB in the process of booking passage. The ProcessPayment EJB processes the credit card, and the Reservation EJB records the actual reservation in the system. Session beans can also be used to read, update, and delete data that can’t be adequately captured in an entity bean. Session beans don’t represent records or data in the database, but they can access data.

All of the work performed by the TravelAgent session bean could have been coded in the client application. Having the client interact directly with entity beans is a common but troublesome design approach because it ties the client directly to the details of the business tasks. As a result, any changes in the way entity beans interact requires changes to the client, and it’s very difficult to reuse the code that models the taskflow.

Session beans allow clients to perform tasks without being concerned with the details that make up the task. A developer can update the session bean, possibly changing the taskflow, without affecting the client code. In addition, if the session bean is properly defined, other clients that perform the same tasks can reuse it. The ProcessPayment session bean, for example, can be used in many areas besides reservations, including retail and wholesale sales. For example, the ship’s gift shop could use the ProcessPayment EJB to process purchases. As a client of the ProcessPayment EJB, the TravelAgent EJB doesn’t care how ProcessPayment works; it’s only interested in the ProcessPayment EJB’s coarse-grained interface, which validates and records charges.

Moving taskflow logic into a session bean also simplifies the client application and reduces network traffic. Excessive network traffic is a common problem for distributed object systems: it can overwhelm the server and clog the network, hurting response time and performance. Session beans, if used properly, can reduce network traffic by limiting the number of requests needed to perform a task. The user of session beans keeps the interaction between the beans involved in a taskflow on the server. One method invocation on the client application results in many method invocations on the server, but the network sees only the traffic produced by the client’s call to the session bean. In the TravelAgent EJB, the client invokes bookPassage( ); in turn, bookPassage( ) makes several method invocations on other enterprise beans. Furthermore, the TravelAgent bean may be in the same container as the other beans, and therefore can use the local interfaces, further reducing network traffic. For the network cost of one method invocation, the client gets several method invocations.

In addition, session beans reduce the number of network connections that the client needs. The cost of maintaining many network connections can be high, so reducing the number of connections each client needs improves the performance of the system as a whole. Figure 2-3 compares the network traffic and connections generated by a client that uses only entity beans to those generated by a client that uses session beans.

Session beans reduce network traffic and thin down clients

Figure 2-3. Session beans reduce network traffic and thin down clients

Session beans also limit the number of stubs used on the client, which saves the client memory and processing cycles. This may not seem like a big deal, but without the use of session beans, a client might be expected to manage hundreds or even thousands of remote references at one time. In the TravelAgent EJB, for example, the bookPassage( ) method works with several remote references, but the client is exposed only to the TravelAgent’s remote reference.

Stateless and stateful session beans

Session beans can be either stateful or stateless . Stateful session beans maintain conversational state when used by a client. Conversational state is not written to a database; it’s information that is kept in memory while a client carries on a conversation with an enterprise bean, and is lost when the conversation ends or if the EJB container crashes. For example, a client making a reservation through the TravelAgent bean may call the methods that set cabin and cruise IDs. These IDs are part of the session’s conversational state, and affect the behavior of subsequent method calls, such as the call to bookPassage( ) that makes the actual reservation. Conversational state is kept for only as long as the client application is actively using the bean. Once the client shuts down or releases the TravelAgent EJB, the conversational state is lost forever. Stateful session beans are not shared among clients; they are dedicated to the same client for the life of the enterprise bean.

Stateless session beans do not maintain any conversational state. Each method is completely independent and uses only data passed in its parameters. The ProcessPayment EJB is a perfect example of a stateless session bean: it doesn’t need to maintain any conversational state from one method invocation to the next. All the information needed to make a payment is passed into the byCreditCard( ) method. Stateless session beans provide better performance and consume fewer resources than entity and stateful session beans because a few stateless session bean instances can serve hundreds and possibly thousands of clients. Chapter 11 talks more about stateless session beans.

Message-Driven Beans

Message-driven beans are integration points for other applications interested in working with EJB applications. Java applications or legacy systems that need to access EJB applications can send messages to message-driven beans via JMS. This bean processes those messages and performs the required tasks using other entity and session beans. EJB 2.1 is not limited to JMS-based message-driven beans: message-driven beans can support any messaging system that implements the correct J2eeCA 1.5 (J2EE Connector Architecture Version 1.5) contracts. However, support for JMS-based message-driven beans (JMS-MDBs) in EJB 2.1 is mandatory, so JMS-MDBs are the type of message-driven bean addressed in this section.

In many ways, JMS-MDBs fulfill the same role as stateless session beans: they manage the taskflow of entity and session beans. The task is initiated by an asynchronous message sent by an application using JMS. Unlike session beans, which respond to business methods invoked on their component interfaces, a JMS-MDB responds to messages delivered through its onMessage( ) method. Since the messages are asynchronous, the client that sends them doesn’t expect a reply. The messaging client simply sends the message and forgets about it.

As an example, we can recast the TravelAgent EJB developed earlier as the ReservationProcessor JMS message-driven bean:

public class ReservationProcessorBean implements javax.ejb.MessageDrivenBean,
    javax.jms.MessageListener {

    public void onMessage(Message message) {
        try {
            MapMessage reservationMsg = (MapMessage)message;

            Integer customerPk = (Integer)reservationMsg.getObject("CustomerID");
            Integer cruisePk = (Integer)reservationMsg.getObject("CruiseID");
            Integer cabinPk = (Integer)reservationMsg.getObject("CabinID");
            double price = reservationMsg.getDouble("Price");

            CreditCardDO card = getCreditCard(reservationMsg);
            CustomerRemote customer = getCustomer(customerPk);
            CruiseLocal cruise = getCruise(cruisePk);
            CabinLocal cabin = getCabin(cabinPk);

            ReservationHomeLocal resHome = (ReservationHomeLocal)
                jndiContext.lookup("java:comp/env/ejb/ReservationHome");
            ReservationLocal reservation =
                resHome.create(customer,cruise,cabin,price,new Date( ));

            Object ref = jndiContext.lookup("java:comp/env/ejb/ProcessPaymentHome");
            ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
                PortableRemoteObject.narrow(ref,ProcessPaymentHomeRemote.class);

            ProcessPaymentRemote process = ppHome.create( );
            process.byCredit(customer,card,price);

        } catch(Exception e) {
            throw new EJBException(e);
        }
    }
   // More helper methods and callback methods follow
}

All the information about the reservation is obtained from the message delivered to the MDB. JMS messages can take many forms; the javax.jms.MapMessage used in this example carries name-value pairs. Once the information is gathered from the message and the enterprise bean references are obtained, the reservation is processed in the same way as it was in the session bean. The only difference is that a TicketDO object isn’t created and returned to the caller; message-driven beans don’t have to respond to the caller.

Regardless of the messaging system, message-driven beans do not maintain any conversational state. Each new message is independent of the previous messages. The message-driven bean is explained in detail in Chapter 12.

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

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