THE AIRLINE RESERVATION AGENT

Một phần của tài liệu sams java distributed objects (Trang 626 - 633)

This next section moves from the server code to the agent code (the code that moves between the client and server). In general, this code introduces the largest collection of new topics and therefore deserves the greatest portion of your attention. The agent code is all contained in a single class and the interface that describes the functionality exposed by that class. To start your exploration of this functionality, take a look at the

FlightSearcherI interface in Listing 35.4.

LISTING 35.4 THE FlightSearcherI INTERFACE

import com.objectspace.voyager.agent.*;

public interface FlightSearcherI { // client-side callback methods

public void flightSearchResults(FlightI[] flights));

public void reservationSearchResults(ReservationI[]

reservations));

// server-side callback methods

public void atServerForFlightSearch(FlightQueryI query);

public void atServerToCreateReservation(ReservationI reservation);

public void atServerToCancelReservation(ReservationI reservation);

public void atServerForReservationSearch(ReservationQueryI query);

// invoked by the client UI

public void searchForFlight(FlightQueryI query);

public void createReservation(ReservationI reservation);

public void cancelReservation(ReservationI reservation);

public void searchForReservation(ReservationQueryI query);

}

Listing 35.4 contains a collection of methods, some of which are invoked by the client UI, and some of which are invoked as callback methods by the Voyager environment. The first methods encountered in a typical interaction are contained at the bottom of the interface in the third grouping. These methods are invoked by the client UI, and they trigger a move to the server for some interaction. Once one of these methods has been invoked, the agent object asks the Voyager environment to be moved to the server and invoke one of the server-side callback methods contained in the center grouping. As a final step, the agent object asks the Voyager environment to be moved back to the client and invoke one of the client-side callback methods contained in the first grouping. Here’s a sample scenario describing the chain of actions executed when a flight search is instantiated:

1. The client UI invokes the searchForFlight() method.

2. The agent object moves to the server.

3. The Voyager environment invokes the atServerForFlightSearch() method in the agent object.

4. The agent object queries the server based on the search parameters supplied by the client.

5. The agent object moves to the client.

6. The Voyager environment invokes the flightSearchResults() method.

7. The agent object notifies the client of the search results.

For the most part, the interface and this scenario describe exactly the workflow of the agent object. However, they only describe that functionality in terms of the public method invocation chain. Additional private helper methods and implementation tricks in the public methods round out the functionality of the class. Listing 35.5 contains the

implementation of the FlightSearcher interface; once you’ve studied it, we’ll return to the previous scenario and discuss the implementation details.

LISTING 35.5 THE FlightSearcher CLASS

import com.objectspace.voyager.*;

import com.objectspace.voyager.agent.*;

import java.util.*;

/**

* Agent class that is changed with making roundtrips from client

* to server to facilitate the creation of and searches for flight

* reservations. When the client executes a search, the FlightSearcher

* object travels to the server, executes the search and then travels

* back to the client.

*/

public class FlightSearcher extends Agent

implements FlightSearcherI, java.io.Serializable {

public FlightSearcher() { }

/**

* Invoked with the results of a flight search * after we have returned to the client.

*/

public void flightSearchResults(FlightI[] flights)) { // notify the client that the results are present obtainSearchDialog().flightSearchResults(flights);

}

/**

* Invoked with the results of a reservation search

* after we have returned to the client.

*/

public void reservationSearchResults(ReservationI[]

reservations)) {

// not exposed by the client UI }

/**

* Invoked when we are at the server and need to execute a

* flight search.

*/

public void atServerForFlightSearch(FlightQueryI query) { // execute the query

FlightI[] flightSearchResults =

obtainFlightDataAccess().searchForFlight(query);

// move to the client

Object[] params = {flightSearchResults};

try{ Agent.of(this).moveTo("//localhost:9000",

"flightSearchResults", params);

}

catch( Exception e ) { System.out.println(e); }

} /**

* Invoked when we are at the server and need to

* create a reservation.

*/

public void atServerToCreateReservation(ReservationI reservation) {

obtainFlightDataAccess().createReservation(reservation);

} /**

* Invoked when we are at the server and need to

* cancel a reservation

*/

public void atServerToCancelReservation(ReservationI reservation) {

obtainFlightDataAccess().cancelReservation(reservation);

} /**

* Invoked when we are at the server and need to

* search for a reservation.

*/

public void atServerForReservationSearch(ReservationQueryI query) {

ReservationI[] searchResults;;

searchResults =

obtainFlightDataAccess().searchForReservation(query);

} /**

* Helper method that obtains a reference

* to the FlightDataAccessI object

*/

private FlightDataAccessI obtainFlightDataAccess() { try{ return (FlightDataAccessI)

Namespace.lookup("//localhost:7000/FlightDataAccess");}

catch( Exception e ) { System.out.println(e); } return null;

} /**

* Helper method that obtains a reference to the SearchDialogI object

*/

private SearchDialogI obtainSearchDialog() { try{ return (SearchDialogI)

Namespace.lookup("//localhost:9000/SearchDialog");}

catch( Exception e ) { System.out.println(e); } return null;

} /**

* Invoked at the client to trigger a flight search.

*/

public void searchForFlight(FlightQueryI flightQuery) { // move to the server

Object[] params = {flightQuery};

try{ Agent.of(this).moveTo("//localhost:7000", "atServerForFlightSearch", params);}

catch( Exception e ) { System.out.println(e); } }

/**

* Invoked at the client to trigger the creation of

* a reservation.

*/

public void createReservation(ReservationI reservation) { // move to the server

Object[] params = {reservation};

try{ Agent.of(this).moveTo("//localhost:7000",

"atServerToCreateReservation", params);}

catch( Exception e ) { System.out.println(e); } }

/**

* Invoked at the client to trigger the cancellation of

* a reservation.

*/

public void cancelReservation(ReservationI reservation) { // move to the server

Object[] params = {reservation};

try{ Agent.of(this).moveTo("//localhost:7000", "atServerToCancelReservation", params);}

catch( Exception e ) { System.out.println(e); } }

/**

* Invoked at the client to trigger a reservation search.

*/

public void searchForReservation(ReservationQueryI query) { // move to the server

Object[] params = {query};

try{ Agent.of(this).moveTo("//localhost:7000", "atServerForReservationSearch", params);}

catch( Exception e ) { System.out.println(e); } }

}

The initial scenario (step 1) involves the invocation of the searchForFlight() method by the client. Once invoked, this method triggers step 2 (that of moving to the server).

The server move is triggered by invoking the moveTo() method available from the active agent object. Once the agent object arrives at the server, the Voyager environment invokes the atServerForFlightSearch() method, which is step 3 in our scenario.

This method first obtains a reference to the server by invoking the private helper method obtainFlightDataAccess() , which binds to the existing FlightDataAccessI object. Using the FlightDataAccess object, step 4 (where the flight search occurs) is executed, and the agent object moves back to the client, which is step 5. One thing to

note here is that the reference to the FlightDataAccess object is only in scope during the method invocation; it’s not assigned to a class variable. The reason for doing this is that all objects assigned to a class variable travel with the agent when it moves. Because we want to keep all business functionality at the server, this object reference is not maintained. Finally, the agent arrives back at the client, and we begin step 6. In this step, the flightSearchResults() method is invoked, and the agent now executes step 7 by obtaining a reference to the SearchDialog object that spawned it and then passing it the results of the search. Like the atServerForFlightSearch() method,

flightSearchResults() uses a private helper method to obtain a reference to the SearchDialog object.

So far, 99 percent of the agent code has been covered. What’s still missing, as you probably noticed when studying the FlightSearcher class and the

FlightSearcherI interface, are the various classes used to model data in the system.

These entity classes allow items such as a reservation, flight, and location to be modeled in actual code. Because their implementations are simply composed of getter and setter methods that provide access to private member variables, the classes themselves are not contained in this chapter. We do, however, provide the interfaces here and all code on the CD-ROM.

Starting at the smallest unit represented by the system, Listing 35.6 contains the SeatI interface, used to model a unique seat on a plane.

LISTING 35.6 THE SeatI INTERFACE

public interface SeatI {

public int getSeatNumber();

}

The next unit exposed by the system is the LocationI interface, contained in Listing 35.7. This entity represents a physical airport and exposes methods used to retrieve its globally unique airport code, city, state, and country.

LISTING 35.7 THE LocationI INTERFACE

public interface LocationI {

public String getAirportCode();

public String getCityName();

public String getStateName();

public String getCountry();

}

Moving along in the system, we encounter the FlightI interface, modeled in Listing 35.8. This interface exposes methods that detail the departure location and date/time, along with the destination location and date/time with a collection of available seats.

LISTING 35.8 THE FlightI INTERFACE

import java.util.*;

public interface FlightI {

public LocationI getDepartureLocation();

public LocationI getDestinationLocation();

public Date getDepartureDateTime();

public Date getDestinationDateTime();

public SeatI[] getAvailableSeats(();

}

Past the level of flight information is the PassengerDemographicsI interface, contained in Listing 35.9. It exposes information about a unique passenger and includes mailing and billing information, along with items such as the frequent-flyer number.

LISTING 35.9 THE PassengerDemographicsI INTERFACE

public interface PassengerDemographicsI { public String getMethodOfPayment();

public String getFirstName();

public String getLastName();

public String getCreditCardNumber();

public String getFrequentFlyerNumber();

public String getBillingAddress1();

public String getBillingAddress2();

public String getBillingCity();

public String getBillingState();

public String getBillingZip();

public String getBillingCountry();

public String getMailingAddress1();

public String getMailingAddress2();

public String getMailingCity();

public String getMailingState();

public String getMailingZip();

public String getMailingCountry();

}

The last physical entity modeled by the system is the ReservationI interface, contained in Listing 35.10. This item references a target flight, seat, and passenger.

LISTING 35.10 THE ReservationI INTERFACE

public interface ReservationI { public FlightI getFlight();

public SeatI getSeat();

public PassengerDemographicsI getDemographics();

}

In addition to the entities used to represent data in the system, two additional entities are used to model queries. As a general rule, when designing systems, I personally feel that it’s better to aggregate all query parameters into a holder class and pass an instance of that class around, instead of creating query methods that accept a collection of query parameters in the signature. The reason for this is that if a new parameter is added, a new version of the query object needs to be created; however, the new version will be back-ward-compatible with older versions of the server. In this manner, there’s much less room for error if updated code is not fully distributed in an enterprise.

The first query entity is the FlightQueryI interface, contained in Listing 35.11. This entity is used to collect data needed when searching for a flight.

LISTING 35.11 THE FlightQueryI INTERFACE import java.util.*;

public interface FlightQueryI {

public LocationI getDepartureLocation();

public LocationI getDestinationLocation();

public Date getDepartureDateTime();

public Date getDestinationDateTime();

public void setDepartureLocation(LocationI departureLocation);

public void setDestinationLocation(LocationI destinationLocation);

public void setDepartureDateTime(Date date);

public void setDestinationDateTime(Date date);

}

Listing 35.12 contains the other query entity used in the system—the

ReservationQueryI interface. This entity is used to aggregate information used when searching for a reservation.

LISTING 35.12 THE ReservationQueryI INTERFACE

public interface ReservationQueryI { public FlightI getFlight();

public SeatI getSeat();

public String getFirstName();

public String getLastName();

public String getCreditCardNumber();

public void setFlight(FlightI flight);

public void setSeat(SeatI seat);

public void setFirstName(String sFirstName);

public void setLastName(String sLastName);

public void setCreditCardNumber(String sCreditCardNumber);

}

At this point in the chapter, you’ve covered all code that forms both the client and the agent middleware. Assuming that the agent code makes sense to you, you’re very much over the hump. If any questions do remain, don’t worry too much. The whole concept of mobile code is a touch hard to grasp at first. Once the client is developed and the application is running, everything will make perfect sense.

Một phần của tài liệu sams java distributed objects (Trang 626 - 633)

Tải bản đầy đủ (PDF)

(693 trang)