
- •Advanced CORBA® Programming with C++
- •Review
- •Dedication
- •Preface
- •Prerequisites
- •Scope of this Book
- •Acknowledgments
- •Chapter 1. Introduction
- •1.1 Introduction
- •1.2 Organization of the Book
- •1.3 CORBA Version
- •1.4 Typographical Conventions
- •1.5 Source Code Examples
- •1.6 Vendor Dependencies
- •1.7 Contacting the Authors
- •Part I: Introduction to CORBA
- •Chapter 2. An Overview of CORBA
- •2.1 Introduction
- •2.2 The Object Management Group
- •2.3 Concepts and Terminology
- •2.4 CORBA Features
- •2.5 Request Invocation
- •2.6 General CORBA Application Development
- •2.7 Summary
- •Chapter 3. A Minimal CORBA Application
- •3.1 Chapter Overview
- •3.2 Writing and Compiling an IDL Definition
- •3.3 Writing and Compiling a Server
- •3.4 Writing and Compiling a Client
- •3.5 Running Client and Server
- •3.6 Summary
- •Part II: Core CORBA
- •Chapter 4. The OMG Interface Definition Language
- •4.1 Chapter Overview
- •4.2 Introduction
- •4.3 Compilation
- •4.4 Source Files
- •4.5 Lexical Rules
- •4.6 Basic IDL Types
- •4.7 User-Defined Types
- •4.8 Interfaces and Operations
- •4.9 User Exceptions
- •4.10 System Exceptions
- •4.11 System Exceptions or User Exceptions?
- •4.12 Oneway Operations
- •4.13 Contexts
- •4.14 Attributes
- •4.15 Modules
- •4.16 Forward Declarations
- •4.17 Inheritance
- •4.18 Names and Scoping
- •4.19 Repository Identifiers and pragma Directives
- •4.20 Standard Include Files
- •4.21 Recent IDL Extensions
- •4.22 Summary
- •Chapter 5. IDL for a Climate Control System
- •5.1 Chapter Overview
- •5.2 The Climate Control System
- •5.3 IDL for the Climate Control System
- •5.4 The Complete Specification
- •Chapter 6. Basic IDL-to-C++ Mapping
- •6.1 Chapter Overview
- •6.2 Introduction
- •6.3 Mapping for Identifiers
- •6.4 Mapping for Modules
- •6.5 The CORBA Module
- •6.6 Mapping for Basic Types
- •6.7 Mapping for Constants
- •6.8 Mapping for Enumerated Types
- •6.9 Variable-Length Types and _var Types
- •6.10 The String_var Wrapper Class
- •6.11 Mapping for Wide Strings
- •6.12 Mapping for Fixed-Point Types
- •6.13 Mapping for Structures
- •6.14 Mapping for Sequences
- •6.15 Mapping for Arrays
- •6.16 Mapping for Unions
- •6.17 Mapping for Recursive Structures and Unions
- •6.18 Mapping for Type Definitions
- •6.19 User-Defined Types and _var Classes
- •6.20 Summary
- •Chapter 7. Client-Side C++ Mapping
- •7.1 Chapter Overview
- •7.2 Introduction
- •7.3 Mapping for Interfaces
- •7.4 Object Reference Types
- •7.5 Life Cycle of Object References
- •7.6 Semantics of _ptr References
- •7.7 Pseudo-Objects
- •7.8 ORB Initialization
- •7.9 Initial References
- •7.10 Stringified References
- •7.11 The Object Pseudo-Interface
- •7.12 _var References
- •7.13 Mapping for Operations and Attributes
- •7.14 Parameter Passing Rules
- •7.15 Mapping for Exceptions
- •7.16 Mapping for Contexts
- •7.17 Summary
- •Chapter 8. Developing a Client for the Climate Control System
- •8.1 Chapter Overview
- •8.2 Introduction
- •8.3 Overall Client Structure
- •8.4 Included Files
- •8.5 Helper Functions
- •8.6 The main Program
- •8.7 The Complete Client Code
- •8.8 Summary
- •Chapter 9. Server-Side C++ Mapping
- •9.1 Chapter Overview
- •9.2 Introduction
- •9.3 Mapping for Interfaces
- •9.4 Servant Classes
- •9.5 Object Incarnation
- •9.6 Server main
- •9.7 Parameter Passing Rules
- •9.8 Raising Exceptions
- •9.9 Tie Classes
- •9.10 Summary
- •Chapter 10. Developing a Server for the Climate Control System
- •10.1 Chapter Overview
- •10.2 Introduction
- •10.3 The Instrument Control Protocol API
- •10.4 Designing the Thermometer Servant Class
- •10.5 Implementing the Thermometer Servant Class
- •10.6 Designing the Thermostat Servant Class
- •10.7 Implementing the Thermostat Servant Class
- •10.8 Designing the Controller Servant Class
- •10.9 Implementing the Controller Servant Class
- •10.10 Implementing the Server main Function
- •10.11 The Complete Server Code
- •10.12 Summary
- •Chapter 11. The Portable Object Adapter
- •11.1 Chapter Overview
- •11.2 Introduction
- •11.3 POA Fundamentals
- •11.4 POA Policies
- •11.5 POA Creation
- •11.6 Servant IDL Type
- •11.7 Object Creation and Activation
- •11.8 Reference, ObjectId, and Servant
- •11.9 Object Deactivation
- •11.10 Request Flow Control
- •11.11 ORB Event Handling
- •11.12 POA Activation
- •11.13 POA Destruction
- •11.14 Applying POA Policies
- •11.15 Summary
- •Chapter 12. Object Life Cycle
- •12.1 Chapter Overview
- •12.2 Introduction
- •12.3 Object Factories
- •12.4 Destroying, Copying, and Moving Objects
- •12.5 A Critique of the Life Cycle Service
- •12.6 The Evictor Pattern
- •12.7 Garbage Collection of Servants
- •12.8 Garbage Collection of CORBA Objects
- •12.9 Summary
- •Part III: CORBA Mechanisms
- •Chapter 13. GIOP, IIOP, and IORs
- •13.1 Chapter Overview
- •13.2 An Overview of GIOP
- •13.3 Common Data Representation
- •13.4 GIOP Message Formats
- •13.5 GIOP Connection Management
- •13.6 Detecting Disorderly Shutdown
- •13.7 An Overview of IIOP
- •13.8 Structure of an IOR
- •13.9 Bidirectional IIOP
- •13.10 Summary
- •14.1 Chapter Overview
- •14.2 Binding Modes
- •14.3 Direct Binding
- •14.4 Indirect Binding via an Implementation Repository
- •14.5 Migration, Reliability, Performance, and Scalability
- •14.6 Activation Modes
- •14.7 Race Conditions
- •14.8 Security Considerations
- •14.9 Summary
- •Part VI: Dynamic CORBA
- •Chapter 15 C++ Mapping for Type any
- •15.1 Chapter Overview
- •15.2 Introduction
- •15.3 Type any C++ Mapping
- •15.4 Pitfalls in Type Definitions
- •15.5 Summary
- •Chapter 16. Type Codes
- •16.1 Chapter Overview
- •16.2 Introduction
- •16.3 The TypeCode Pseudo-Object
- •16.4 C++ Mapping for the TypeCode Pseudo-Object
- •16.5 Type Code Comparisons
- •16.6 Type Code Constants
- •16.7 Type Code Comparison for Type any
- •16.8 Creating Type Codes Dynamically
- •16.9 Summary
- •Chapter 17. Type DynAny
- •17.1 Chapter Overview
- •17.2 Introduction
- •17.3 The DynAny Interface
- •17.4 C++ Mapping for DynAny
- •17.5 Using DynAny for Generic Display
- •17.6 Obtaining Type Information
- •17.7 Summary
- •Part V: CORBAservices
- •Chapter 18. The OMG Naming Service
- •18.1 Chapter Overview
- •18.2 Introduction
- •18.3 Basic Concepts
- •18.4 Structure of the Naming Service IDL
- •18.5 Semantics of Names
- •18.6 Naming Context IDL
- •18.7 Iterators
- •18.8 Pitfalls in the Naming Service
- •18.9 The Names Library
- •18.10 Naming Service Tools
- •18.11 What to Advertise
- •18.12 When to Advertise
- •18.13 Federated Naming
- •18.14 Adding Naming to the Climate Control System
- •18.15 Summary
- •Chapter 19. The OMG Trading Service
- •19.1 Chapter Overview
- •19.2 Introduction
- •19.3 Trading Concepts and Terminology
- •19.4 IDL Overview
- •19.5 The Service Type Repository
- •19.6 The Trader Interfaces
- •19.7 Exporting Service Offers
- •19.8 Withdrawing Service Offers
- •19.9 Modifying Service Offers
- •19.10 The Trader Constraint Language
- •19.11 Importing Service Offers
- •19.12 Bulk Withdrawal
- •19.13 The Admin Interface
- •19.14 Inspecting Service Offers
- •19.15 Exporting Dynamic Properties
- •19.16 Trader Federation
- •19.17 Trader Tools
- •19.18 Architectural Considerations
- •19.19 What to Advertise
- •19.20 Avoiding Duplicate Service Offers
- •19.21 Adding Trading to the Climate Control System
- •19.22 Summary
- •Chapter 20. The OMG Event Service
- •20.1 Chapter Overview
- •20.2 Introduction
- •20.3 Distributed Callbacks
- •20.4 Event Service Basics
- •20.5 Event Service Interfaces
- •20.6 Implementing Consumers and Suppliers
- •20.7 Choosing an Event Model
- •20.8 Event Service Limitations
- •20.9 Summary
- •Part VI: Power CORBA
- •Chapter 21. Multithreaded Applications
- •21.1 Chapter Overview
- •21.2 Introduction
- •21.3 Motivation for Multithreaded Programs
- •21.4 Fundamentals of Multithreaded Servers
- •21.5 Multithreading Strategies
- •21.6 Implementing a Multithreaded Server
- •21.7 Servant Activators and the Evictor Pattern
- •21.8 Summary
- •22.1 Chapter Overview
- •22.2 Introduction
- •22.3 Reducing Messaging Overhead
- •22.4 Optimizing Server Implementations
- •22.5 Federating Services
- •22.6 Improving Physical Design
- •22.7 Summary
- •Appendix A. Source Code for the ICP Simulator
- •Appendix B. CORBA Resources
- •Bibliography

IT-SC book: Advanced CORBA® Programming with C++
Appendix A. Source Code for the ICP Simulator
A.1 Overview
If you want to experiment with the code in this book, you will require an implementation of the ICP API we used for our hypothetical instrument control protocol. The following two sections list source code to simulate the ICP network in your server.
Section A.2 shows source code for an in-memory implementation. This implementation keeps a map of the network devices in memory and provides access to the state of devices via the ICP API shown in Section 10.3. The state of the simulated network is not written to disk in this implementation, so when your server shuts down all state changes are lost.
Section A.3 augments the implementation with a very simple persistence mechanism.
A.2 Transient Simulator Code
The non-persistent ICP simulator applies to the server implementation in Chapter 10. The simulator uses an STL map that maps device IDs to structures of type DeviceState. A DeviceState structure stores the type (thermometer or thermostat), model string, location, and nominal temperature for each device. For simplicity, we use the same DeviceState structure for both types of devices even though thermometers do not have a nominal temperature. (The nominal_temp field is unused for thermometers.) The four API calls to add and remove devices and to access attributes manipulate the map of devices held in the static variable dstate.
Because we do not have real devices that would store a real model string in read-only memory, we use the asset number to assign a model string to each device: odd asset numbers denote thermometers, and even asset numbers denote thermostats.
In a real climate control system, the actual temperature of a room varies around the selected nominal temperature. The vary_temp function simulates this variation by returning a temperature that deviates by as much as three degrees from the passed temperature.
To decide what temperature to return for a particular device, we use the actual_temp function. Given an iterator that indicates the device whose temperature is to be returned, the function locates all thermostats in the same room as the given device and calculates the average of their nominal temperatures. That average is passed to vary_temp to simulate temperature fluctuations. If the given device is a thermometer in a room that contains no thermostat, the actual temperature fluctuates around DFLT_TEMP.
869

IT-SC book: Advanced CORBA® Programming with C++
#include |
<string> |
#include |
<map> |
#include |
<algorithm> |
#include |
<stdlib.h> |
#include |
"icp.h" |
//----------------------------------------------------------------
enum DeviceType { thermometer, thermostat };
struct DeviceState { |
// State for a device |
DeviceType |
type; |
const char * |
model; |
string |
location; |
short |
nominal_temp; // For thermostats only |
};
typedef map<unsigned long, DeviceState> StateMap;
//---------------------------------------------------------------- |
// Max len of string including NUL |
||
const size_t MAXSTR = 32; |
|||
const short MIN_TEMP = 40; |
// 40 |
F == 4.44 |
C |
const short MAX_TEMP = 90; |
// 90 |
F == 32.22 |
C |
const short DFLT_TEMP = 68; |
// 68 |
F == 20.00 |
C |
static StateMap dstate; |
// Map of known devices |
||
//---------------------------------------------------------------- |
|
|
|
//ICP_online() simulates adding a new device to the network by
//adding it to the dstate map.
//
//For this simple simulation, devices with odd a sset numbers
//are thermometers and devices with even asset numbers
//are thermostats.
//
//Thermostats get an initial nominal temperature of DFLT_TEMP.
//The location string is intentionally left blank because it
//must be programmed by the controller after putting the device
//on-line (as should be the nominal temperature).
//
//If a device with the specified ID is already on-line, the
//return value is -1. A zero return value indicates success. extern "C"
int
ICP_online(unsigned long id)
{
//Look for id in state map.
StateMap::iterator pos = dstate.find(id);
if (pos != |
dstate.end()) |
// Already exists |
return |
-1; |
|
// Fill in |
state. |
|
DeviceState ds;
ds.type = (id % 2) ? thermometer : thermostat; ds.model = (ds.type == thermometer)
? "Sens-A-Temp" : "Select-A-Temp"; ds.nominal_temp = DFLT_TEMP;
// Insert new device into map dstate[id] = ds;
return 0;
}
//----------------------------------------------------------------
//ICP_offline() simulates removing a device from the network by
//removing it from the dstate map. If the device isn't known, the
//return value is -1. A zero return value indicates success.
870

IT-SC book: Advanced CORBA® Programming with C++
extern "C" |
|
int |
|
ICP_offline(unsigned long id) |
|
{ |
|
// Look for id in state map |
|
StateMap::iterator pos = dstate.find(id); |
|
if (pos == dstate.end()) |
// No such device |
return -1; |
|
dstate.erase(id); |
|
return 0; |
|
} |
|
//---------------------------------------------------------------- |
|
//vary_temp() simulates the variation in actual temperature
//around a thermostat. The function randomly varies the
//temperature as a percentage of calls as follows:
// |
3 |
degrees too cold: |
5% |
// |
|||
// |
3 |
degrees too hot: |
5% |
//2 degrees too cold: 10%
// |
2 |
degrees too hot: |
10% |
// |
1 |
degree too cold: |
15% |
// |
1 |
degree too hot: |
15% |
//exact temperature: 40%
static short
vary_temp(short temp)
{
long r = lrand48() % 50; long delta;
if (r < 5) delta = 3;
else if (r < 15) delta = 2;
else if (r < 30) delta = 1;
else
delta = 0;
if (lrand48() % 2) delta = -delta; return temp + delta;
}
//----------------------------------------------------------------
//Function object. Locates a thermostat that is in the same room
//as the device at position pos.
class ThermostatInSameRoom { public:
ThermostatInSameRoom(
const StateMap::iterator & pos ) : m_pos(pos) {}
bool operator()(
pair<const unsigned long, DeviceState> & p ) const
{
return(
p.second.type == thermostat
&&p.second.location
==m_pos->second.location
871

IT-SC book: Advanced CORBA® Programming with C++
);
}
private:
const StateMap::iterator & m_pos;
}; //----------------------------------------------------------------
//actual_temp() is a helper function to determine the actual
//temperature returned by a particular thermometer or thermostat.
//The pos argument indicates the device.
//
//The function locates all thermostats that are in the same room
//as the device denoted by pos and computes the average of all
//the thermostats' nominal temperatures. (If no thermostats are
//in the same room as the device, the function assumes that the
//average of the nominal temperatures is DFLT_TEMP.)
//
//The returned temperature varies from the average as
//determined by vary_temp().
static short
actual_temp(const StateMap::iterator & pos)
{
long sum = 0; long count = 0;
StateMap::iterator where = find_if(
dstate.begin(), dstate.end(), ThermostatInSameRoom(pos)
); while (where != dstate.end()) {
count++;
sum += where->second.nominal_temp; where = find_if(
++where, dstate.end(), ThermostatInSameRoom(pos)
);
}
return vary_temp(count == 0 ? DFLT_TEMP : sum / count);
}
//----------------------------------------------------------------
//ICP_get() returns an attribute value of the device with the
//given id. The attribute is named by the attr parameter. The
//value is copied into the buffer pointed to by the value
//pointer. The len parameter is the size of the passed buffer,
//so ICP_get() can avoid overrunning the buffer.
//
//By default, thermometers report a temperature that varies
//somewhat around DFLT_TEMP. However, if there is another
//thermostat in the same room as the thermometer, the
//thermometer reports a temperature that varies around that
//thermostat's temperature. For several thermostats that are in
//the same room, the thermometer reports a temperature that
//varies around the average nominal temperature of all the
//thermostats.
//
//Attempts to read from a non-existent device or to read a
//non-existent attribute return -1. A return value of zero
//indicates success. If the supplied buffer is too short to hold
872

IT-SC book: Advanced CORBA® Programming with C++
//a value, ICP_get() silently truncates the value and
//returns success.
extern "C" |
|
int |
|
ICP_get( |
id, |
unsigned long |
|
const char * |
attr, |
void * |
value, |
size_t |
len) |
{ |
|
// Look for id in state map StateMap::iterator pos = dstate.find(id);
if (pos == |
dstate.end()) |
// No such device |
return |
-1; |
//Depending on the attribute, return the
//corresponding piece of state.
if (strcmp(attr, "model") == 0) {
strncpy((char *)value, pos->second.model, len); } else if (strcmp(attr, "location") == 0) {
strncpy((char *)value, pos->second.location.c_str(), len); } else if (strcmp(attr, "nominal_temp") == 0) {
if (pos->second.type != thermostat) |
// Must be thermostat |
return -1; |
|
memcpy( |
|
value, &pos->second.nominal_temp, min(len, sizeof(pos->second.nominal_temp))
);
}else if (strcmp(attr, "temperature") == 0) { short temp = actual_temp(pos);
memcpy(value, &temp, min(len, sizeof(temp)));
}else if (strcmp(attr, "MIN_TEMP") == 0) { memcpy(value, &MIN_TEMP, min(len, sizeof(MIN_TEMP)));
}else if (strcmp(attr, "MAX_TEMP") == 0) { memcpy(value, &MAX_TEMP, min(len, sizeof(MAX_TEMP)));
}else {
return -1; |
// No such attribute |
} |
// OK |
return 0; |
|
} |
|
//---------------------------------------------------------------- |
|
//ICP_set() sets the attribute specified by attr to the
//value specified by value for the device with ID id. Attempts to
//write a string longer than MAXSTR bytes (including the
//terminating NUL) result in silent truncation of the string.
//Attempts to access a non-existent device or attribute
//return -1. Attempts to set a nominal temperature outside the
//legal range also return -1. A zero return value
//indicates success.
extern "C" int
ICP_set(unsigned long id, const char * attr, const void * value)
{
// Look for id in state map StateMap::iterator pos = dstate.find(id); if (pos == dstate.end())
return -1; |
// |
No such device |
// Change either location or nominal temp, |
depending on attr. |
873

IT-SC book: Advanced CORBA® Programming with C++
if (strcmp(attr, "location") == 0) { pos->second.location.assign(
(const char *)value, MAXSTR - 1
);
} else if (strcmp(attr, "nominal_temp") == 0)
{ |
|
if (pos->second.type != thermostat) |
// Must be thermostat |
return -1; |
|
short temp; |
|
memcpy(&temp, value, sizeof(temp));
if (temp < MIN_TEMP || temp > MAX_TEMP)
return -1; |
|
pos->second.nominal_temp = temp; |
|
} else { |
// No such attribute |
return -1; |
|
} |
// OK |
return 0; |
}
A.3 Persistent Simulator Code
The persistent simulator applies to the server implementations discussed in Chapter 12 and later chapters. This version of the simulator stores the state of the ICP network in the text file /tmp/CCS_DB, so the server can shut down and start up again without losing previous changes made to the network. The text file contains multiline records using one line for each device attribute:
Asset number
Device type (zero indicates a thermometer, 1 indicates a thermostat) Location
Nominal temperature (for thermometers only)
Here is a small example file containing a thermometer record followed by a thermostat record:
1027
0 ENIAC 3032 1
Colossus 68
To keep the /tmp/CCS_DB file up-to-date, we use a global class instance mydb. At start-up, the constructor of mydb reads the contents of the file and initializes the dstate map; at shutdown, the destructor writes the entire map contents back to the file. This design is not terribly elegant, but it has the advantage that the existence of the ICP simulator is hidden from the rest of the source code.
874

IT-SC book: Advanced CORBA® Programming with C++
For simplicity, we keep error checking to a minimum. Also note that state changes are written out only if the server terminates cleanly. If the server terminates abnormally—for example, with a core dump or by calling _exit—the destructor of mydb never runs and all state changes are lost.
To add persistence to the implementation in Section A.2, we append the following code:
#include <fstream.h> class ICP_Persist { public:
ICP_Persist(const char * file); ~ICP_Persist();
private:
string m_filename;
};
// Read device state from a file and initialize the dstate map. ICP_Persist::
ICP_Persist(const char * file) : m_filename(file)
{
// Open input file, creating it if necessary.
fstream db(m_filename.c_str(), ios::in|ios::out, 0666); if (!db) {
cerr < "Error opening " < m_filename < endl; exit(1);
}
// Read device details, one attribute per line. DeviceState ds;
unsigned long id; while (db >> id) {
// Read device type and set model string accordingly. int dtype;
db >> dtype;
ds.type = dtype == thermometer
? thermometer : thermostat; ds.model = dtype == thermometer
? "Sens-A-Temp" : "Select-A-Temp";
char loc[MAXSTR]; |
// Skip newline |
db.get(loc[0]); |
|
db.getline(loc, sizeof(loc)); |
// Read location |
ds.location = loc; |
|
if (ds.type == thermostat) |
// Read temperature |
db >> ds.nominal_temp; |
|
dstate[id] = ds; |
// Add entry to map |
} |
|
db.close(); |
|
if (!db) { |
|
cerr < "Error closing " < m_filename < endl; exit(1);
}
}
// Write device state to the file. ICP_Persist::
~ICP_Persist()
{
// Open input file, truncating it.
875

IT-SC book: Advanced CORBA® Programming with C++
ofstream db(m_filename.c_str()); if (!db) {
cerr < "Error opening " < m_filename < endl; exit(1);
}
// Write the state details for each device. StateMap::iterator i;
for (i = dstate.begin(); i != dstate.end(); i++) { db < i->first < endl;
db < i->second.type < endl;
db < i->second.location < endl; if (i->second.type == thermostat)
db < i->second.nominal_temp < endl;
}
if (!db) {
cerr < "Error writing " < m_filename < endl; exit(1);
}
db.close(); if (!db) {
cerr < "Error closing " < m_filename < endl; exit(1);
}
}
// Instantiate a single global instance of the class. static ICP_Persist mydb("/tmp/CCS_DB");
876