Using Enterprise Beans

Let’s look at how a client would work with an enterprise bean to do something useful. We’ll start with the Cabin EJB defined earlier. A cabin is a thing or place whose description is stored in a database. To make the example a little more real, imagine 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, including the cruise name, the ship name, and a list of cabins. Using the cruise ID obtained from a text field, we can use some of our beans to populate the GUI with data about the requested cruise. Here’s what the code would look like:

CruiseHomeRemote cruiseHome = ... use JNDI to get the home
// Get the cruise ID from a text field.
String cruiseID = textFields1.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());
}

Let’s start by getting a remote reference to the EJB home for an entity bean that represents a cruise. We are using a remote reference instead of a local one, because the client is a GUI Java application located outside the EJB container. In EJB 1.1, we don’t have a choice because only remote component interfaces are supported. 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. It’s a little too complicated to talk about here, but it will be 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, which implements the business methods of our bean. Once we have the appropriate Cruise EJB, we can ask the Cruise EJB to give us a remote reference to a Ship EJB that will be used for the cruise. We can then get a Collection of remote Cabin EJB references from the Ship EJB and display the names of the Cabin EJBs in the client.

Entity beans model data and behavior. They provide a reusable and consistent interface to data in the database. The behavior used in entity beans is usually focused on applying business rules that pertain directly to changing data. In addition, entity beans can model relationships with other entities. A ship, for example, has many cabins. We can get a list of cabins owned by the ship by invoking the ship.getCabins() method.

Modeling Workflow with Session Beans

Entity beans are useful for objectifying 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 for the client managing business processes or tasks; they’re the appropriate place for business logic. A session bean is not persistent like an entity bean; 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 workflow. Workflow 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

Although the term “workflow” is frequently used to describe the management of business processes that may span several days with lots of human intervention, that is not the definition used in this book. The term workflow in this book describes the interactions of beans within a single transaction, which takes only a few seconds to execute.

The following code demonstrates how a session bean, designed to make cruise-line reservations, might control the workflow of other entity and session beans to accomplish this task. 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 workflow is important because it provides us with more flexibility in how the system evolves and how clients are allowed to interact with the EJB system.

The following listing shows some of the code included in the TravelAgentBean. The bookPassage() method actually 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 are using the ProcessPayment session to make a credit card purchase of a cruise ticket. Once payment has been made, a serializable TicketDO object is created and returned to the client application.

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 workflow of beans. 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 like entity beans, but they can access data in the database.

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. This is troublesome for two reasons: any changes in the entity beans and their interaction require changes to the client, and it’s very difficult to reuse the code that models the workflow.

Session beans are coarse-grained components that allow clients to perform tasks without being concerned with the details that make up the task. This allows developers to update the session bean, possibly changing the workflow, without impacting 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 reused 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 workflow logic into a session bean also helps to thin down the client applications and reduce network traffic and connections. Excessive network traffic is actually one of the biggest problems in distributed object systems. Excessive traffic can overwhelm the server and clog the network, hurting response times and performance. Session beans, if used properly, can substantially reduce network traffic by limiting the number of requests needed to perform a task. In distributed objects, every method invocation produces network traffic. Distributed objects communicate requests using an RMI loop. This requires that data be streamed between the stub and skeleton with every method invocation. With session beans, the interaction of beans in a workflow is kept 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 one method call on the session bean. In the TravelAgent EJB, the client invokes bookPassage(), but on the server, the bookPassage() method produces several method invocations on the component interfaces of other enterprise beans. For the network cost of one method invocation, the client gets several method invocations.[8]

In addition, session beans reduce the number of network connections needed by the client. The cost of maintaining many network connections can be very high, so reducing the number of connections each client needs improves the performance of the system as a whole. When session beans are used to manage workflow, the number of connections that each client has to the server may be substantially reduced, which improves the EJB server’s performance. Figure 2-3 compares the network traffic and connections used by a client that uses only entity beans to those used 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 remote reference of the TravelAgent EJB.

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 state that is kept in memory while a client uses a session. Maintaining conversational state allows a client to carry on a conversation with an enterprise bean. As each method on the enterprise bean is invoked, the state of the session bean may change, and that change can affect subsequent method calls. The TravelAgent session bean, for example, may have many more methods than the bookPassage() method. The methods that set the cabin and cruise IDs are examples. These set methods are responsible for modifying conversational state. They convert the IDs into remote references to Cabin and Cruise EJBs that are later used in the bookPassage() method. 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. The ProcessPayment EJB 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 higher performance in terms of throughput and resource consumption than entity and stateful session beans because only a few stateless session bean instances are needed to serve hundreds and possibly thousands of clients. Chapter 12 talks more about the use of stateless session beans.

EJB 2.0: Accessing EJB with 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 via JMS to message-driven beans. The message-driven beans can then process those messages and perform the required tasks using other entity and session beans.

In many ways, message-driven beans fulfill the same role as session beans by managing the workflow of entity and session beans to complete a given task. The task to be completed 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 message-driven bean responds to asynchronous messages delivered to the bean through its onMessage() method. Since the messages are asynchronous, the client that sends them doesn’t expect or wait for 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 EJB, a 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);

            TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
        } catch(Exception e) {
            throw new EJBException(e);
        }
    }
// More helper methods and callback methods follow
}

Notice that all the information about the reservation is obtained from the message delivered to the message-driven bean. JMS messages can take many forms, one of which is javax.jms.MapMessage form used in this example, which carries name-value pairs. Once the information is obtained from the message and the enterprise bean references are obtained, the reservation is processed as it was in the session bean. The only difference is that a TicketDO is not returned to the caller; message-driven beans don’t have to respond to the caller because the process is asynchronous.

Message-driven beans, like stateless session beans, do not maintain any conversational state. The processing of each new message is independent from the previous messages. The message-driven bean is explained in detail in Chapter 13.



[8] In EJB 2.0, the bookPassage() method might have used the local component interfaces of the other enterprise beans, because they are much more efficient.

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

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