Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

// show the details of each record.

static ostream &

operator<(ostream & os, const CCS::Controller::EChange & ec)

{

for (CORBA::ULong i = 0; i < ec.errors.length(); i++) {

os < "Change failed:" < endl;

// Overloaded <

os < ec.errors[i].tmstat_ref;

os < ec.errors[i].info < endl;

// Overloaded <

}

return os;

}

The code iterates over the sequence contained in the exception. For each element, it calls the overloaded inserters we defined earlier to show the details and the error report for each thermostat whose set_nominal operation failed.

We need one final helper function: set_temp. This function sets the temperature of a thermostat given a reference and a new temperature. set_temp prints a number of trace messages so that we can see what is going on. If we call set_temp with an illegal temperature, its catch handler prints the details of a BadTemp exception by calling the ostream inserter we defined previously. This allows us to monitor when an exception is raised and also prevents the program from terminating by unwinding the stack all the way back to main:

// Change the temperature of a thermostat.

static void

set_temp(CCS::Thermostat_ptr tmstat, CCS::TempType new_temp)

{

if (CORBA::is_nil(tmstat)) // Don't call via nil reference return;

CCS::AssetType anum = tmstat->asset_num(); try {

cout < "Setting thermostat " < anum

<" to " < new_temp < " degrees." < endl; CCS::TempType old_nominal = tmstat->set_nominal(new_temp); cout < "Old nominal temperature was: "

<old_nominal < endl;

cout < "New nominal temperature is: "

<tmstat->get_nominal() < endl;

}catch (const CCS::Thermostat::BadTemp & bt) {

cerr

<

"Setting of nominal temperature failed." < endl;

cerr

<

bt.details < endl;

// Overloaded <

}

}

8.6 The main Program

299

IT-SC book: Advanced CORBA® Programming with C++

The client main consists of initialization code and the code to interact with the climate control system. For this example, the client exercises the various IDL operations to test the functionality of the server.

8.6.1 Initialization

Writing the initialization code in the client is a trivial task. The first step is to initialize the ORB:

int

main(int argc, char * argv[])

{

try {

// Initialize the ORB

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

Note that orb is a _var reference so that we will correctly release the pseudo-reference returned from ORB_init.

The next step is to convert the stringified reference to the controller that is passed on the command line and to narrow it to CCS::Controller:

//Check arguments if (argc != 2) {

cerr < "Usage: client IOR_string" < endl; throw 0;

}

//Get controller reference from argv

//and convert to object.

CORBA::Object_var obj = orb->string_to_object (argv[1]); if (CORBA::is_nil(obj)) {

cerr < "Nil controller reference" < endl; throw 0;

}

// Try to narrow to CCS::Controller. CCS::Controller_var ctrl;

try {

ctrl = CCS::Controller::_narrow(obj);

} catch (const CORBA::SystemException & se) { cerr < "Cannot narrow controller reference: "

< se < endl; throw 0;

}

if (CORBA::is_nil(ctrl)) {

cerr < "Wrong type for controller ref." < endl; throw 0;

}

Note that there are two tests for nil here: one before the call to _narrow and a second one following it. If the first test fails, we know that the original stringified reference

300

IT-SC book: Advanced CORBA® Programming with C++

passed on the command line was a nil reference. If the second test fails, we know that the original reference was non-nil but that its type was not CCS::Controller.

Also note that if we detect an error here, we deal with the error condition in a catch handler and then throw zero. That causes program termination via the handler at the end of main.

8.6.2 Interacting with the Server

At this point, the client holds an active reference to the controller object and can start interacting with the server via the reference. The first step is to retrieve the complete list of devices from the controller and to show the details for each of them:

// Get list of devices CCS::Controller::ThermometerSeq_var list = ctrl->list();

//Show number of devices. CORBA::ULong len = list->length();

cout < "Controller has " < len < " device"; if (len != 1)

cout < "s"; cout < "." < endl;

//If there are no devices at all, we are finished. if (len == 0)

return 0;

//Show details for each device.

for (CORBA::ULong i = 0; i < list->length(); i++) cout < list[i];

cout < endl;

Note that the sequence of references returned from the list operation is a variablelength type, and we use the _var type for the sequence to ensure that the return value will be deallocated. The code then shows the total number of devices in the sequence on stdout. This calls the ostream inserter we defined earlier, which in turn retrieves the details of the device from the server.

The next step is to update the location attribute of whatever device happened to be returned as the first sequence element:

// Change the location of first device in the list CCS::AssetType anum = list[0]->asset_num();

cout < "Changing location of device "

<anum < "." < endl; list[0]->location("Earth");

//Check that the location was updated cout < "New details for device "

<anum < " are:" < endl;

cout < list[0] < endl;

301

IT-SC book: Advanced CORBA® Programming with C++

The statement

anum = list[0]->asset_num();

makes a remote call to read the asset number of the device, and the statement

list[0]->location("Earth");

updates the location attribute to the string "Earth". We then print the details for the first device once more so that we can see that the updated location is now returned by the server.

The next step is to change the temperature of a thermostat to a legal and then an illegal value. To do this, we must first locate a thermostat because only thermostats support a set_nominal operation:

// Find first thermostat in list. CCS::Thermostat_var tmstat;

for ( CORBA::ULong i = 0;

i < list->length() && CORBA::is_nil(tmstat); i++) {

tmstat = CCS::Thermostat::_narrow(list[i]);

}

This loop iterates over the sequence returned from list and attempts to narrow each reference on the list. The first successful narrow causes the loop to terminate, leaving the reference to the first thermostat on the list in the variable tmstat.

Provided that a thermostat was found, we now call set_nominal with a legal temperature and a second time with an illegal temperature:

// Check that we found a thermostat on the list. if (CORBA::is_nil(tmstat)) {

cout < "No thermostat devices in list." < endl;

}else {

//Set temperature of thermostat to

//50 degrees (should work).

set_temp(tmstat, 50); cout < endl;

//Set temperature of thermostat to

//-10 degrees (should fail).

set_temp(tmstat, -10);

}

In both cases, we set the temperature by calling the set_temp helper function we described on page 325. set_temp invokes set_nominal and shows either the

302

IT-SC book: Advanced CORBA® Programming with C++

updated nominal temperature (if the operation worked) or the details of a BadTemp exception (if the operation failed).

The remainder of the client exercises the Controller object. The first step is to use the find operation to look for devices in rooms Earth and HAL:

//Look for device in Rooms Earth and HAL. This must

//locate at least one device because we earlier changed

//the location of the first device to Room Earth.

cout < "Looking for devices in Earth and HAL." < endl; CCS::Controller::SearchSeq ss;

ss.length(2); ss[0].key.loc(CORBA::string_dup("Earth")); ss[1].key.loc(CORBA::string_dup("HAL")); ctrl->find(ss);

The trick here is to correctly fill in the search sequence. The search sequence contains structures that in turn are composed of a union containing a key member and a device member (recall the IDL at the end of Chapter 5). We create a local sequence variable ss, set its length to 2, and then initialize the union member of the each sequence element to the search key. The statements

ss[0].key.loc(CORBA::string_dup("Earth")); ss[1].key.loc(CORBA::string_dup("HAL"));

initialize the first two sequence elements by modifying the key members of these elements (which in turn are unions); the loc modifier method initializes the corresponding loc member. We then pass the search sequence to the find operation on the controller.

When find completes, it will have updated the passed sequence with the devices it has found (recall that the sequence is passed to find as an inout parameter). The next few lines of code show how many devices were found and the details of each device. (Because there may be more than one device in a room, the sequence may have been updated to contain more elements than it had before the call.)

// Show the devices found in that room.

for (CORBA::ULong i = 0; i < ss.length(); i++)

cout < ss[i].device;

// Overloaded <

cout < endl;

 

Again, the code iterates over the sequence and prints the details of each device. The statement

cout < ss[i].device;

303

IT-SC book: Advanced CORBA® Programming with C++

prints the structure member device (which is an object reference) on stdout, so the overloaded insertion operator we defined earlier shows the details of the device.

The final step is to invoke the change operation on the controller. The change operation expects a list of references to thermostats together with a temperature delta value. This means that we must create a list containing only thermostats (of type ThermostatSeq) from the ThermometerSeq we obtained from the list operation. The easiest way to achieve this is to iterate over the polymorphic list we obtained earlier and to construct a new list that contains only thermostats:

//Increase the temperature of all thermostats

//by 40 degrees. First, make a new list (tss)

//containing only thermostats.

cout < "Increasing thermostats by 40 degrees." < endl; CCS::Controller::ThermostatSeq tss;

for (CORBA::ULong i = 0; i < list->length(); i++) { tmstat = CCS::Thermostat::_narrow(list[i]);

if (CORBA::is_nil(tmstat))

continue; // Skip thermometers len = tss.length();

tss.length(len + 1); tss[len] = tmstat;

}

This code creates a new list (tss) from the old one, using _narrow to identify those devices that are thermostats. After we have constructed this list, changing the temperature of all thermostats is trivial:

// Try to change all thermostats. try {

ctrl->change(tss, 40);

} catch (const

CCS::Controller::EChange & ec) {

cerr < ec;

// Overloaded <

}

} catch (const CORBA::Exception & e) {

cerr < "Uncaught CORBA exception: " < e < endl; return 1;

} catch (...) { return 1;

}

return 0;

}

If one or more thermostats cannot make the change because their legal temperature range is exceeded, the operation raises EChange, and we use the overloaded ostream inserter we defined earlier to show the details of the exception. This concludes the client code to exercise the climate control system.

Here is the output produced from an example run of the client:

304

 

 

IT-SC book: Advanced CORBA® Programming with C++

Controller has 7

devices.

Thermometer:

 

1027

Asset number:

Model

:

Sens-A-Temp

Location

:

ENIAC

Temperature :

67

Thermometer:

 

2029

Asset number:

Model

:

Sens-A-Temp

Location

:

Deep Thought

Temperature :

68

Thermostat:

 

3032

Asset number:

Model

:

Select-A-Temp

Location

:

Colossus

Temperature :

67

Nominal temp:

68

Thermostat:

 

4026

Asset number:

Model

:

Select-A-Temp

Location

:

ENIAC

Temperature :

58

Nominal temp:

60

Thermostat:

 

4088

Asset number:

Model

:

Select-A-Temp

Location

:

ENIAC

Temperature :

51

Nominal temp:

50

Thermostat:

 

8042

Asset number:

Model

:

Select-A-Temp

Location

:

HAL

Temperature :

40

Nominal temp:

40

Thermometer:

 

8053

Asset number:

Model

:

Sens-A-Temp

Location

:

HAL

Temperature :

70

Changing location of device 1027.

New details for device 1027 are:

Thermometer:

Asset number: 1027

Model : Sens-A-Temp

Location : Earth

Temperature : 71

Setting thermostat 3032 to 50 degrees.

Old nominal temperature was: 68

New nominal temperature is: 50

Setting thermostat 3032 to -10 degrees. Setting of nominal temperature failed. CCS::Thermostat::BtData details:

requested : -10

305