Exercise 11.2: A Stateful Session Bean

In this exercise, you will build and examine a stateful session bean, TravelAgent, which coordinates the work of booking a trip on a ship. You will also build a client application to test this EJB.

Our version of this exercise does not follow the one in the EJB section strictly. Instead of simplifying the beans and their relationships as the EJB section does, we use the beans implemented in Chapter 6 and Chapter 7 and thus take advantage of the CMP 2.0 features of JBoss.

Examine the EJB

This exercise is based on the EJBs from Exercise 7.3 and doesn’t contain much material that previous sections haven’t covered. Nevertheless, a few modifications have been made:

  • The Customer EJB again has a remote home and bean interfaces (as in Chapter 6) and exposes its relationship with the Address EJB in the remote interface through a new data object, AddressDO.

  • The Cabin EJB has a new create method that takes several parameters.

  • The Reservation EJB has a new create method that takes several parameters, and has a local reference to the Customer EJB.

The TravelAgent bean’s role is to perform all activities needed to book a successful trip. Thus, as in the preceding example, this session bean acts as a coordinator between different EJBs and groups several actions on different beans in the same transaction. Here, though, the bean maintains a conversational state with the client; i.e., each client has a dedicated bean on the server.

In the previous example featuring stateless session beans, the home create method was not allowed to have parameters: providing initialization parameters would be useless, as the bean wouldn’t be able to remember them for forthcoming invocations. A stateful session bean, by contrast, maintains a conversational state, so its create methods can have parameters to initialize the bean state. Indeed, the home interface can have several create methods. In this example, however, the TravelAgent home interface declares only one:

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

Furthermore, if you take a look at the remote interface, you can see that methods are correlated around an identical state:

public interface TravelAgentRemote extends javax.ejb.EJBObject
{
   public void setCruiseID (Integer cruise)
      throws RemoteException, FinderException;
   
   public void setCabinID (Integer cabin)
      throws RemoteException, FinderException;
   
   public TicketDO bookPassage (CreditCardDO card, double price)
      throws RemoteException, IncompleteConversationalState;
   
   public String [] listAvailableCabins (int bedCount)
      throws RemoteException, IncompleteConversationalState;
}

If no conversational state between the client and the server existed, calling setCruiseId( ) would make no sense. The role of this method is simply to populate this conversational state so that future calls can use this data in their processing.

Because this exercise is based on the beans implemented in Chapter 6 and Chapter 7, it needs a database schema that includes all the relationships among them, and thus differs from the one in the EJB book. Because the listAvailableCabins( ) method performs direct SQL calls, it must be rewritten to take this new database schema into account:

...
Integer cruiseID = (Integer)cruise.getPrimaryKey ( );
Integer shipID = (Integer)cruise.getShip ( ).getPrimaryKey ( );
con = getConnection ( );

ps = con.prepareStatement (
        "select ID, NAME, DECK_LEVEL from CABIN "+
        "where SHIP_ID = ? and BED_COUNT = ? and ID NOT IN "+
        "(SELECT RCL.CABIN_ID FROM RESERVATION_CABIN_LINK AS RCL,"+
        "RESERVATION AS R "+
        "WHERE RCL.RESERVATION_ID = R.ID " +
        "AND R.CRUISE_ID = ?)");

ps.setInt (1,shipID.intValue ( ));
ps.setInt (2,bedCount);
ps.setInt (3,cruiseID.intValue ( ));

result = ps.executeQuery ( );
...

You may remember that in previous examples we added a method (either home or remote) to the EJB to be able to initialize the test environment. As you can guess, this example uses the same trick. The TravelAgent EJB remote interface has been extended with one method:

public interface TravelAgentRemote extends javax.ejb.EJBObject
{
   ...
   // Mechanism for building local beans for example programs.
   //
   public void buildSampleData ( ) throws RemoteException;
}

This method removes any Customer, Cabin, Ship, Cruise, and Reservation EJBs from the database and recreates a basic environment. You can follow this initialization step by step. First, the method acquires references to the remote home of the Customer EJB, and to the local homes of the Cabin, Ship, Cruise, and Reservation EJBs:

public Collection buildSampleData ( )
{
   Collection results = new ArrayList ( );

   try
   {
      System.out.println ("TravelAgentBean::buildSampleData( )");

      Object obj = jndiContext.lookup 
                 ("java:comp/env/ejb/CustomerHomeRemote");
      CustomerHomeRemote custhome = (CustomerHomeRemote)
      javax.rmi.PortableRemoteObject.narrow (obj, 
                                     CustomerHomeRemote.class);

      CabinHomeLocal cabinhome =
      (CabinHomeLocal)jndiContext.lookup 
                      ("java:comp/env/ejb/CabinHomeLocal");
      ShipHomeLocal shiphome =
      (ShipHomeLocal)jndiContext.lookup 
                     ("java:comp/env/ejb/ShipHomeLocal");
      CruiseHomeLocal cruisehome =
      (CruiseHomeLocal)jndiContext.lookup 
                       ("java:comp/env/ejb/CruiseHomeLocal");
      ReservationHomeLocal reshome =
      (ReservationHomeLocal)jndiContext.lookup 
                       ("java:comp/env/ejb/ReservationHomeLocal");

Then any existing bean is deleted from the database:

// we first clean the db by removing any customer, cabin,
// ship, cruise and reservation beans.
//
removeBeansInCollection (custhome.findAll( ));
results.add ("All customers have been removed");
removeBeansInCollection (cabinhome.findAll( ));
results.add ("All cabins have been removed");
removeBeansInCollection (shiphome.findAll( ));
results.add ("All ships have been removed");
removeBeansInCollection (cruisehome.findAll( ));
results.add ("All cruises have been removed");
removeBeansInCollection (reshome.findAll( ));
results.add ("All reservations have been removed");

The removeBeansInCollection( ) method is a simple one. It iterates through the specified collection and removes each EJBObject or EJBLocalObject.

Two customers and two ships are created:

// We now set our new basic environment
//
System.out.println ("Creating Customers 1 and 2...");
CustomerRemote customer1 = custhome.create (new Integer (1));
customer1.setName ( new Name ("Burke","Bill") );
results.add ("Customer with ID 1 created (Burke Bill)");
         
                  CustomerRemote customer2 = custhome.create (new Integer (2));
customer2.setName ( new Name ("Labourey","Sacha") );
results.add("Customer with ID 2 created (Labourey Sacha)");

System.out.println ("Creating Ships A and B...");
ShipLocal shipA = shiphome.create (new Integer (101), 
                  "Nordic Prince", 50000.0);
results.add("Created ship with ID 101...");
                  ShipLocal shipB = shiphome.create (new Integer (102), 
                  "Bohemian Rhapsody", 70000.0);
results.add("Created ship with ID 102...");

The buildSampleData( ) method adds a message to the results collection after each significant step, and ultimately returns results so the caller knows what’s happened on the server. It then creates 10 cabins on each ship:

System.out.println ("Creating Cabins on the Ships...");         
ArrayList cabinsA = new ArrayList ( );
ArrayList cabinsB = new ArrayList ( );
for (int jj=0; jj<10; jj++)
{
   CabinLocal cabinA = cabinhome.create (new Integer 
                                (100+jj),shipA,"Suite 10"+jj,1,1);
   cabinsA.add(cabinA);
   CabinLocal cabinB = cabinhome.create (new Integer 
                                (200+jj),shipB,"Suite 20"+jj,2,1);
   cabinsB.add(cabinB);
}
results.add("Created cabins on Ship A with IDs 100-109");
results.add("Created cabins on Ship B with IDs 200-209");

The method quickly organizes some cruises for each ship:

                  CruiseLocal cruiseA1 = cruisehome.create ("Alaska Cruise", shipA);
CruiseLocal cruiseA2 = cruisehome.create ("Norwegian Fjords", 
                                           shipA);
CruiseLocal cruiseA3 = cruisehome.create ("Bermuda or Bust", shipA);
results.add("Created cruises on ShipA with IDs 
            "+cruiseA1.getId( )+", "+cruiseA2.getId( )+", 
            "+cruiseA3.getId( ));

CruiseLocal cruiseB1 = cruisehome.create ("Indian Sea 
                                           Cruise", shipB);
CruiseLocal cruiseB2 = cruisehome.create ("Australian Highlights", 
                                           shipB);
CruiseLocal cruiseB3 = cruisehome.create ("Three-Hour Cruise", 
                                           shipB);
results.add ("Created cruises on ShipB with IDs "+
              cruiseB1.getId ( )+", "+cruiseB2.getId ( )+", 
             "+cruiseB3.getId ( ));

Finally, some reservations are made for these cruises:

                  ReservationLocal res =
                           reshome.create (customer1, cruiseA1, 
                         (CabinLocal)(cabinsA.get (3)), 
                         1000.0, new Date ( ));
         res = reshome.create (customer1, cruiseB3, 
                         (CabinLocal)(cabinsB.get (8)),
                         2000.0, new Date ( ));
         res = reshome.create (customer2, cruiseA2, 
                         (CabinLocal)(cabinsA.get (5)), 
                         2000.0, new Date ( ));
         res = reshome.create (customer2, cruiseB3, 
                          (CabinLocal)(cabinsB.get (2)), 
                         2000.0, new Date ( ));

         results.add ("Made reservation for Customer 1 on Cruise "+
                      cruiseA1.getId ( )+" for Cabin 103");
         results.add ("Made reservation for Customer 1 on Cruise "+
                      cruiseB3.getId ( )+" for Cabin 208");
         results.add ("Made reservation for Customer 2 on Cruise "+
                      cruiseA2.getId ( )+" for Cabin 105");
         results.add ("Made reservation for Customer 2 on Cruise "+
                      cruiseB3.getId ( )+" for Cabin 202");
   }
   ...
   return results;
}

Later, you’ll see how to call this method to set up the environment.

Examine the EJB Standard Deployment Descriptor

Most of the ejb-jar.xml file comprises definitions you’ve seen in previous examples (entity beans, relationships, the ProcessPayment stateless session bean, etc.). Only two things have been added.

ejb-jar.xml

First, the Customer EJB now has both local and remote interfaces:

<entity>
  <ejb-name>CustomerEJB</ejb-name>

                          <home>com.titan.customer.CustomerHomeRemote</home>
                     <remote>com.titan.customer.CustomerRemote</remote>
                     <local-home>com.titan.customer.CustomerHomeLocal</local-home>
                     <local>com.titan.customer.CustomerLocal</local>
  <ejb-class>com.titan.customer.CustomerBean</ejb-class>
  <persistence-type>Container</persistence-type>
  <prim-key-class>java.lang.Integer</prim-key-class>
  <reentrant>False</reentrant>
  <cmp-version>2.x</cmp-version>
  <abstract-schema-name>Customer</abstract-schema-name>
  <cmp-field><field-name>id</field-name></cmp-field>
  <cmp-field><field-name>lastName</field-name></cmp-field>
  <cmp-field><field-name>firstName</field-name></cmp-field>
  <cmp-field><field-name>hasGoodCredit</field-name></cmp-field>
  <primkey-field>id</primkey-field>
  <security-identity><use-caller-identity/></security-identity>
</entity>

Providing the second interface enables the Customer EJB to serve local clients as well as remote ones. Note that the remote and local interfaces do not declare the same methods. For example, it’s illegal for a remote interface to expose entity relationships, so they’re accessible only via the local interface.

The second addition is the new TravelAgent stateful session bean that is the heart of this exercise:

<session>
   <ejb-name>TravelAgentEJB</ejb-name>
   <home>com.titan.travelagent.TravelAgentHomeRemote</home>
   <remote>com.titan.travelagent.TravelAgentRemote</remote>
   <ejb-class>com.titan.travelagent.TravelAgentBean</ejb-class>
   <session-type>Stateful</session-type>
   <transaction-type>Container</transaction-type>
   ...

As you can see, only the value of the <session-type> tag distinguishes the declaration of a stateful session bean from that of a stateless bean.

The deployment descriptor then declares all the beans referenced by the TravelAgent EJB:

   ...
   <ejb-ref>
      <ejb-ref-name>ejb/ProcessPaymentHomeRemote</ejb-ref-name>
      <ejb-ref-type>Session</ejb-ref-type>
      <home>
         com.titan.processpayment.ProcessPaymentHomeRemote
      </home>
      <remote>
         com.titan.processpayment.ProcessPaymentRemote
      </remote>
      <ejb-link>ProcessPaymentEJB</ejb-link>
   </ejb-ref>
   <ejb-ref>
      <ejb-ref-name>ejb/CustomerHomeRemote</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <home>
         com.titan.customer.CustomerHomeRemote
      </home>
      <remote>com.titan.customer.CustomerRemote</remote>
      <ejb-link>CustomerEJB</ejb-link>
   </ejb-ref>
   <ejb-local-ref>
      <ejb-ref-name>ejb/CabinHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>
         com.titan.cabin.CabinHomeLocal
      </local-home>
      <local>com.titan.cabin.CabinLocal</local>
      <ejb-link>CabinEJB</ejb-link>
   </ejb-local-ref>
   <ejb-local-ref>
      <ejb-ref-name>ejb/ShipHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>
         com.titan.cabin.ShipHomeLocal
      </local-home>
      <local>com.titan.cabin.ShipLocal</local>
      <ejb-link>ShipEJB</ejb-link>
   </ejb-local-ref>
   <ejb-local-ref>
      <ejb-ref-name>ejb/CruiseHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>
         com.titan.cruise.CruiseHomeLocal
      </local-home>
      <local>com.titan.cruise.CruiseLocal</local>
      <ejb-link>CruiseEJB</ejb-link>
   </ejb-local-ref>
   <ejb-local-ref>
      <ejb-ref-name>ejb/ReservationHomeLocal</ejb-ref-name>
      <ejb-ref-type>Entity</ejb-ref-type>
      <local-home>
         com.titan.reservation.ReservationHomeLocal
      </local-home>
      <local>com.titan.reservation.ReservationLocal</local>
      <ejb-link>ReservationEJB</ejb-link>
   </ejb-local-ref>
   <resource-ref>
      <res-ref-name>jdbc/titanDB</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
   </resource-ref>
</session>

Examine the JBoss Deployment Descriptor

The jboss.xml deployment descriptor contains the JNDI name mapping found in the previous examples. The only new entry is the TravelAgent EJB definition.

jboss.xml

<session>
   <ejb-name>TravelAgentEJB</ejb-name>
   <jndi-name>TravelAgentHomeRemote</jndi-name>
   <resource-ref>
      <res-ref-name>jdbc/titanDB</res-ref-name>
      <jndi-name>java:/DefaultDS</jndi-name>
   </resource-ref>
</session>

This file defines the JNDI name for the TravelAgent, then maps the data source’s JNDI ENC name to the embedded database.

The listAvailableCabins( ) method uses this mapping to execute SQL statements directly against the database, so it must know precisely the names of the tables and fields to use in each query. While jbosscmp-jdbc.xml already defines the field-to-column mapping of all CMP beans, it doesn’t define the fields and tables used by relationships between these beans. If it doesn’t have those definitions, JBoss will use arbitrary names for these tables—not good in this case. To avoid this problem, extend jbosscmp-jdbc.xml, adding definitions that map the relationships into the desired tables and columns exactly. For this exercise, we mapped only the relationships used in the SQL query: Cabin-Ship, Cabin-Reservation, and Cruise-Reservation.

jbosscmp-jdbc.xml

Cabin-Reservation is a many-to-many relationship:

<ejb-relation>
   <ejb-relation-name>Cabin-Reservation</ejb-relation-name>
   <relation-table-mapping>
      <table-name>RESERVATION_CABIN_LINK</table-name>
      <create-table>true</create-table>
      <remove-table>true</remove-table>
   </relation-table-mapping>
   <ejb-relationship-role>

      
                        <ejb-relationship-role-name

                     
                               >Cabin-has-many-Reservations<

                     
                              /ejb-relationship-role-name>

      <key-fields>
         <key-field>

                                    <field-name>id</field-name>

                     
                                    <column-name>CABIN_ID</column-name>

         </key-field>
      </key-fields>
   </ejb-relationship-role>
   <ejb-relationship-role>
      
                        <ejb-relationship-role-name

                     
                               >Reservation-has-many-Cabins<

                     
                               /ejb-relationship-role-name>

      <key-fields>
         <key-field>

                                    <field-name>id</field-name>

                     
                                    <column-name>RESERVATION_ID</column-name>

         </key-field>
      </key-fields>
   </ejb-relationship-role>
</ejb-relation>
...

Many-to-many relationships always need an intermediate table. The name of this table is defined in the <table-name> tag. Then, for each role of the relationship, the <field-name> and <column-name> tags do the mapping between the CMR field of the bean and the column in the table.

The last two mappings needed are for one-to-many relationships, Cabin-Ship and Cruise-Reservation:

...
<ejb-relation>
   <ejb-relation-name>Cabin-Ship</ejb-relation-name>
   <foreign-key-mapping/>
   <ejb-relationship-role>
      <ejb-relationship-role-name
                     >Ship-has-many-Cabins<
                     /ejb-relationship-role-name>
      <key-fields>
         <key-field>
            <field-name>id</field-name>
                     <column-name>SHIP_ID</column-name>
         </key-field>
      </key-fields>
   </ejb-relationship-role>
   <ejb-relationship-role>
      <ejb-relationship-role-name
                     >Cabin-has-a-Ship<
                     /ejb-relationship-role-name>
      <key-fields/>
   </ejb-relationship-role>
</ejb-relation>

<ejb-relation>
   <ejb-relation-name>Cruise-Reservation</ejb-relation-name>
   <foreign-key-mapping/>      
   <ejb-relationship-role>
      <ejb-relationship-role-name
                     >Cruise-has-many-Reservations<
                     /ejb-relationship-role-name>
      <key-fields>
         <key-field>
            <field-name>id</field-name>
                     <column-name>CRUISE_ID</column-name>
         </key-field>
      </key-fields>
   </ejb-relationship-role>
   <ejb-relationship-role>
      <ejb-relationship-role-name
                     >Reservation-has-a-Cruise<
                     /ejb-relationship-role-name>
      <key-fields/>
   </ejb-relationship-role>
</ejb-relation>

For each relationship identified by an <ejb-relation-name> tag (the name must be the same as the one declared in ejb-jar.xml), the mapping of the CMR field to a table column is defined by the <field-name> and <column-name> tags.

Start Up JBoss

If JBoss is already running, there is no reason to restart it.

Build and Deploy the Example Programs

Perform the following steps:

  1. Open a command prompt or shell terminal and change to the ex11_2 directory created by the extraction process.

  2. Set the JAVA_HOME and JBOSS_HOME environment variables to point to where your JDK and JBoss 4.0 are installed. Examples:

    Windows:C:workbookex11_2> set JAVA_HOME=C:jdk1.4.2 C:workbookex11_2> set JBOSS_HOME=C:jboss-4.0
    Unix:$ export JAVA_HOME=/usr/local/jdk1.4.2 $ export JBOSS_HOME=/usr/local/jboss-4.0
  3. Add ant to your execution path.

    Windows:C:workbookex11_2> set PATH=..antin;%PATH%

    Unix:

    $ export PATH=../ant/bin:$PATH
  4. Perform the build by typing ant.

As in the last exercise, you will see titan.jar rebuilt, copied to the JBoss deploy directory, and redeployed by the application server.

Initialize the Database

Because the exercise uses the ProcessPayment EJB from the previous example, the database must contain the PAYMENT table. The createdb and dropdb Ant targets, Java code, and clients here have been borrowed from Exercise 11.1.

If you have dropped the PAYMENT table after running the examples in Exercise 11.1, re-create it now by running the createdb Ant target.

C:workbookex11_2>ant createdb
Buildfile: build.xml

prepare:

compile:

ejbjar:

createdb:
     [java] Looking up home interfaces..
     [java] Creating database table...

On the JBoss console, you’ll see:

INFO  [STDOUT] Creating table PAYMENT...
INFO  [STDOUT] ...done!

Tip

If you’re having trouble creating the database, shut down JBoss. Then run the Ant build target clean.db. This removes all database files and allows you to start fresh.

The container manages the persistence of all other entity beans used in this exercise, so it will create the needed tables for them automatically.

Examine the Client Applications

This exercise includes three example client applications.

Client_112a

The first client simply calls the TravelAgent bean’s buildSampleData( ) method. To run this application, invoke the Ant target run.client_112a:

C:workbookex11_2>ant run.client_112a
Buildfile: build.xml

prepare:

compile:

ejbjar:

run.client_112a:
  [java] Calling TravelAgentBean to create sample data..
  [java] All customers have been removed
  [java] All cabins have been removed
  [java] All ships have been removed
  [java] All cruises have been removed
  [java] All reservations have been removed
  [java] Customer with ID 1 created (Burke Bill)
  [java] Customer with ID 2 created (Labourey Sacha)
  [java] Created ship with ID 101...
  [java] Created ship with ID 102...
  [java] Created cabins on Ship A with IDs 100-109
  [java] Created cabins on Ship B with IDs 200-209
  [java] Created Alaska Cruise with ID 0 on ShipA
  [java] Created Norwegian Fjords Cruise with ID 1 on ShipA
  [java] Created Bermuda or Bust Cruise with ID 2 on ShipA
  [java] Created Indian Sea Cruise with ID 3 on ShipB
  [java] Created Australian Highlights Cruise with ID 4 on ShipB
  [java] Created Three-Hour Cruise with ID 5 on ShipB
  [java] Made reservation for Customer 1 on Cruise 0 for Cabin 103
  [java] Made reservation for Customer 1 on Cruise 5 for Cabin 208
  [java] Made reservation for Customer 2 on Cruise 1 for Cabin 105
  [java] Made reservation for Customer 2 on Cruise 5 for Cabin 202

Now that you’ve prepared the environment, you can use the other two client applications. Client_112b allows you to book a passage, while Client_112c gives you a list of the Cabins for a specific Cruise that have a specified number of beds.

Client_112b

The second client starts by getting remote home interfaces to the TravelAgent and Customer EJBs:

public static void main(String [] args) throws Exception 
{

   if (args.length != 4) 
   {
      System.out.println
         ("Usage: java " +
          "com.titan.clients.Client_122b" + 
          "<customerID> <cruiseID> <cabinID> <price>");
      System.exit(-1);
   }

   Integer customerID = new Integer(args[0]);
   Integer cruiseID = new Integer(args[1]);
   Integer cabinID = new Integer(args[2]);
   double price = new Double(args[3]).doubleValue( );

   Context jndiContext = getInitialContext( );
   Object obj = jndiContext.lookup("TravelAgentHomeRemote");
   TravelAgentHomeRemote tahome = (TravelAgentHomeRemote) 
      javax.rmi.PortableRemoteObject.narrow(obj, 
                TravelAgentHomeRemote.class);

   obj = jndiContext.lookup("CustomerHomeRemote");
   CustomerHomeRemote custhome = (CustomerHomeRemote) 
      javax.rmi.PortableRemoteObject.narrow(obj, 
                CustomerHomeRemote.class);

With the home references in hand, it can now get a reference to the customer whose ID was given on the command line. If no customer with this ID exists, an exception is thrown.

   // Find a reference to the Customer for which to book a cruise
   System.out.println("Finding reference to Customer "+customerID);
   CustomerRemote cust = custhome.findByPrimaryKey(customerID);

The application then creates a TravelAgent stateful session bean and gives it, as part of the transactional state, the reference to the customer, the cruise ID, and the Cabin ID.

   // Start the Stateful session bean
   System.out.println("Starting TravelAgent Session...");
   TravelAgentRemote tagent = tahome.create(cust);

   // Set the other bean parameters in agent bean
   System.out.println("Setting Cruise and Cabin information in TravelAgent..");
   tagent.setCruiseID(cruiseID);
   tagent.setCabinID(cabinID);

It can then book the passage, thanks to a dummy credit card:

   // Create a dummy CreditCard for this
   //
   Calendar expdate = Calendar.getInstance( );
   expdate.set(2005,1,5);
   CreditCardDO card = new CreditCardDO("370000000000002",
                                        expdate.getTime( ),
                                        "AMERICAN EXPRESS");

   // Book the passage
   //
   System.out.println("Booking the passage on the Cruise!");
   TicketDO ticket = tagent.bookPassage(card,price);

   System.out.println("Ending TravelAgent Session...");
   tagent.remove( );

   System.out.println("Result of bookPassage:");
   System.out.println(ticket.description);

 }

Test this client application by booking Suite 201 for Mr. Bill Burke on the Three-Hour Cruise aboard the “Bohemian Rhapsody.”

Ant doesn’t make it particularly easy to pass command-line parameters through to the client. To make this task easier, use one of the scripts that accept command-line parameters in a more customary fashion, available in the ex11_2 directory.

To book a passage, use the BookPassage.bat (Windows) or the BookPassage script (Unix):

BookPassage.bat <customerID> <cruiseID> <cabinID> <price>
Or
./BookPassage <customerID> <cruiseID> <cabinID> <price>

C:workbookex11_2>BookPassage 1 5 201 2000.0
Buildfile: build.xml

prepare:

compile:

ejbjar:

run.client_112b:
     [java] Finding reference to Customer 1
     [java] Starting TravelAgent Session...
     [java] Setting Cruise and Cabin information in TravelAgent..
     [java] Booking the passage on the Cruise!
     [java] Ending TravelAgent Session...
     [java] Result of bookPassage:
     [java] Bill Burke has been booked for the Three-Hour Cruise cruise on ship Bohemian Rhapsody.
     [java]  Your accommodations include Suite 201 a 2 bed cabin on deck level 1.
     [java]  Total charge = 2000.0

BUILD SUCCESSFUL

Client_112c

The last application gives you a list of available cabins for a specific cruise that have a desired number of beds. First, the application verifies that it’s been given the correct number of command-line arguments and gets a remote home reference to the TravelAgent EJB:

public static void main(String [] args) throws Exception 
{
   if (args.length != 2) 
   {
      System.out.println("Usage: java " +
                         "com.titan.clients.Client_122c" +
                         " <cruiseID> <bedCount>");
      System.exit(-1);
   }

   Integer cruiseID = new Integer(args[0]);
   int bedCount = new Integer(args[1]).intValue( );
   Context jndiContext = getInitialContext( );
   Object obj = jndiContext.lookup("TravelAgentHomeRemote");
   TravelAgentHomeRemote tahome = (TravelAgentHomeRemote) 
      javax.rmi.PortableRemoteObject.narrow(obj,
                TravelAgentHomeRemote.class);

Because the session bean is not really dedicated to a specific instance of Customer, but is instead making an SQL query in the database, the client creates a TravelAgent bean with a dummy Customer reference, which will never be used. Then it supplies the Cruise ID:

   // Start the Stateful session bean
   System.out.println("Starting TravelAgent Session...");
   TravelAgentRemote tagent = tahome.create(null);

   // Set the other bean parameters in agent bean
   System.out.println
      ("Setting Cruise information in TravelAgent..");
   tagent.setCruiseID(cruiseID);

Finally, the application asks for a list of all available cabins with a desired number of beds on a particular cruise and displays the result, if any:

   String[] results = tagent.listAvailableCabins(bedCount);

   System.out.println("Ending TravelAgent Session...");
   tagent.remove( );

   System.out.println("Result of listAvailableCabins:");
   for (int kk=0; kk<results.length; kk++) 
   {
      System.out.println(results[kk]);
   }

 }

To launch this application, you can use the ListCabins.bat (Windows) or ListCabins (Unix) script:

ListCabins.bat <cruiseID> <bedCount>
Or
./ListCabins <cruiseID> <bedCount>

Ask the system for a list of the two-bed cabins that are available on the Three-Hour Cruise, the one Mr. Bill Burke chose:

C:workbookex11_2>ListCabins 5 2
Buildfile: build.xml

prepare:

compile:

ejbjar:

run.client_112c:
     [java] Starting TravelAgent Session...
     [java] Setting Cruise information in TravelAgent..
     [java] Ending TravelAgent Session...
     [java] Result of listAvailableCabins:
     [java] 200,Suite 200,1
     [java] 203,Suite 203,1
     [java] 204,Suite 204,1
     [java] 205,Suite 205,1
     [java] 206,Suite 206,1
     [java] 207,Suite 207,1
     [java] 209,Suite 209,1

BUILD SUCCESSFUL

Suite 201 has two beds but is not shown as available. This omission is correct, because Mr. Bill Burke has booked that suite.

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

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