ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button O'Reilly Book Excerpts: Enterprise JavaBeans, 4th Edition

Developing Your First EJBs, Part 2

by Bill Burke, Richard Monson-Haefel, and Sacha Labourey

Editor's note: In part one of this two-part excerpt from Chapter 4 of Enterprise JavaBeans, 4th Edition, the authors walked through what you need to do to develop your first entity bean. This week concludes this series with a look at how to develop a session bean, building on the examples presented in part one.

Developing a Session Bean

Related Reading

Enterprise JavaBeans
By Richard Monson-Haefel, Bill Burke, Sacha Labourey

Session beans act as agents to the client, controlling taskflow (the business process) and filling the gaps between the representation of data by entity beans and the business logic. Session beans are often used to manage interactions between entity beans and can perform complex manipulations of beans. Since we have defined only one entity bean so far, we will start by manipulating this bean. The interactions of entity beans within session beans is explored in greater detail in Chapter 11.

Client applications and other beans use the Cabin EJB in a variety of ways. Some of these uses were predictable when the Cabin EJB was defined, but many were not. After all, an entity bean represents data - in this case, data describing a cabin. The uses to which we put that data change over time - hence the importance of separating the data itself from the taskflow. In Titan's business system, for example, we may need to list and report on cabins in ways that were not predictable when the Cabin EJB was defined. Rather than change the Cabin EJB every time we need to look at it differently, we will obtain the information we need using a session bean. The definition of an entity bean should only be changed within the context of a larger process - for example, a major redesign of the business system.

We'll start developing a TravelAgent EJB that is responsible for the taskflow of booking a passage on a cruise. This session bean will be used in client applications accessed by travel agents throughout the world. In addition to booking tickets, the TravelAgent EJB provides information about which cabins are available on the cruise. In this chapter, we develop the first implementation of this listing behavior. The "list cabins" behavior will be used to provide customers with a list of cabins that can accommodate their needs. The Cabin EJB does not directly support this kind of list, nor should it. The list we need is specific to the TravelAgent EJB, so it's the TravelAgent EJB's responsibility to query the Cabin EJB and produce the list.

Start by creating a development directory for the TravelAgent EJB, as we did for the Cabin EJB. Name this directory travelagent and nest it below the /dev/com/titan directory, which also contains the cabin directory (see Figure 4-5). Place all the Java files and the XML deployment descriptor for the TravelAgent EJB into the travelagent directory.

Figure 4-5
Figure 4-5. Directory structure for the TravelAgent EJB

TravelAgentRemote: The Remote Interface

As before, we start by defining the remote interface so that our focus is on the business purpose of the bean, rather than its implementation. Starting small, we know that the TravelAgent EJB will need to provide a method for listing all the cabins available with a specified bed count for a specific ship. We'll call that method listCabins( ). Since we need only a list of cabin names and deck levels, we'll define listCabins( ) to return an array of Strings. Here's the remote interface for TravelAgentRemote:

package com.titan.travelagent;

import java.rmi.RemoteException;
import javax.ejb.FinderException;

public interface TravelAgentRemote extends javax.ejb.EJBObject {

    // String elements follow the format "id, name, deck level"
    public String [] listCabins(int shipID, int bedCount)
        throws RemoteException;
}

TravelAgentHomeRemote: The Remote Home Interface

The second step in the development of the TravelAgent EJB bean is to create the remote home interface. The remote home interface for a session bean defines the create methods that initialize a new session bean for use by a client.

Find methods are not used in session beans; session beans do not represent data in the database, so a find method would not be meaningful. A session bean is dedicated to a client for the life of that client (or less). For the same reason, we don't need to worry about primary keys - since session beans don't represent persistent data, we don't need a key to access that data.

package com.titan.travelagent;

import java.rmi.RemoteException;
import javax.ejb.CreateException;

public interface TravelAgentHomeRemote extends javax.ejb.EJBHome {
    public TravelAgentRemote create( )
        throws RemoteException, CreateException;
}

In the case of the TravelAgent EJB, we need only a simple create( ) method to get a reference to the bean. Invoking this create( ) method returns the TravelAgent EJB's remote reference, which the client can use for the reservation process.

TravelAgentBean: The Bean Class

Using the remote interface as a guide, we can define the TravelAgentBean class that implements the listCabins( ) method. Here's the definition of TravelAgentBean for this example:

package com.titan.travelagent;

import com.titan.cabin.CabinRemote;
import com.titan.cabin.CabinHomeRemote;
import java.rmi.RemoteException;
import javax.naming.InitialContext;
import javax.naming.Context;
import java.util.Properties;
import java.util.Vector;
import javax.rmi.PortableRemoteObject;
import javax.ejb.EJBException;


public class TravelAgentBean implements javax.ejb.SessionBean {

    public void ejbCreate( ) {
    // Do nothing.
    }
    public String [] listCabins(int shipID, int bedCount) {

        try {
            javax.naming.Context jndiContext = new InitialContext( );
            Object obj = jndiContext.lookup("java:comp/env/ejb/CabinHomeRemote");

            CabinHomeRemote home = (CabinHomeRemote)
                PortableRemoteObject.narrow(obj,CabinHomeRemote.class);
    
            Vector vect = new Vector( );
            for (int i = 1; ; i++) {
                Integer pk = new Integer(i);
                CabinRemote cabin;
                try {
                    cabin = home.findByPrimaryKey(pk);
                } catch(javax.ejb.FinderException fe) {
                    break;
                }
                // Check to see if the bed count and ship ID match.
                if (cabin.getShipId( ) == shipID && 
                    cabin.getBedCount( ) == bedCount) {
                    String details = i+","+cabin.getName( )+
                                     ","+cabin.getDeckLevel( );
                    vect.addElement(details);
                }
            }
        
            String [] list = new String[vect.size( )];
            vect.copyInto(list);
            return list;
       
        } catch(Exception e) {throw new EJBException(e);}    
    }

    public void ejbRemove( ){}
    public void ejbActivate( ){}
    public void ejbPassivate( ){}
    public void setSessionContext(javax.ejb.SessionContext cntx){}
}

In order to examine the listCabins( ) method in detail, let's address the implementation in pieces, starting with the use of JNDI to locate the CabinHomeRemote:

javax.naming.Context jndiContext = new InitialContext( );

Object obj = jndiContext.lookup("java:comp/env/ejb/CabinHomeRemote");

CabinHomeRemote home = (CabinHomeRemote)
    javax.rmi.PortableRemoteObject.narrow(obj, CabinHomeRemote.class);

Beans are clients to other beans, just like client applications. This means that they must interact with other beans in the same way that J2EE application clients interact with beans. For one bean to locate and use another bean, it must first locate and obtain areference to the bean's EJB home. This is accomplished using the JNDI default context, which is the JNDI context that the container provides automatically when you create a new instance of the InitialContext. You don't need to set any properties on the InitialContext when using a standard J2EE component (EJB, Servlet/JSP, or J2EE Application Client).

All beans have their own default JNDI context called the environment naming context, which was discussed briefly in Chapter 3. The default context exists in the name space (directory) called "java:comp/env" and its subdirectories. When the bean is deployed, any beans it uses are mapped into the subdirectory "java:comp/env/ejb", so that bean references can be obtained at runtime through a simple and consistent use of the JNDI default context. We'll come back to this when we look at the deployment descriptor for the TravelAgent EJB.

Once the remote EJB home of the Cabin EJB has been obtained, we can use it to produce a list of cabins that match the parameters passed into the method. The following code loops through all the Cabin EJBs and produces a list that includes only those cabins in which the ship and bed count are specified:

Vector vect = new Vector( );
for (int i = 1; ; i++) {
    Integer pk = new Integer(i);
    CabinRemote cabin;
    try {
        cabin = home.findByPrimaryKey(pk);
    } catch(javax.ejb.FinderException fe){
        break;
    }
    // Check to see if the bed count and ship ID match.
    if (cabin.getShipId( ) == shipID && cabin.getBedCount( ) == bedCount) {
        String details = i+","+cabin.getName( )+","+cabin.getDeckLevel( );
        vect.addElement(details);
    }
}

This method iterates through all the primary keys, obtaining a remote reference to each Cabin EJB in the system and checking whether its shipId and bedCount match the parameters passed. The for loop continues until a FinderException is thrown, which will probably occur when a primary key that isn't associated with a bean is used. (This isn't the most robust code possible, but it will do for now.) Following this block of code, we simply copy the Vector's contents into an array and return it to the client.

While this is a very crude approach to locating the right Cabin EJBs - we will define a better method in Chapter 11 - it is adequate for our current purposes. The purpose of this example is to illustrate that the taskflow associated with this listing behavior is not included in the Cabin EJB, nor is it embedded in a client application. Taskflow logic, whether it's a process like booking a reservation or like obtaining a list, is placed in a session bean.

Pages: 1, 2

Next Pagearrow