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.
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++) { CabinLocalcabinA = cabinhome.create
(new Integer (100+jj),shipA
,"Suite 10"+jj,1,1); cabinsA.add(cabinA); CabinLocalcabinB = 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.
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.
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>
The jboss.xml
deployment descriptor contains the
JNDI name mapping found in the previous examples. The only new entry
is the TravelAgent EJB definition.
<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.
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.
Perform the following steps:
Open a command prompt or shell terminal and change to the
ex11_2
directory created by the extraction
process.
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
|
Add ant
to your execution path.
Windows:C:workbookex11_2> set PATH=..antin;%PATH%
|
Unix:
$ export PATH=../ant/bin:$PATH
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.
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!
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.
This exercise includes three example client applications.
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.
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
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.