- •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++
Chapter 10. Developing a Server for the Climate Control System
10.1 Chapter Overview
This chapter presents the source code for a complete climate control system server.
Section 10.2 introduces the overall implementation strategy, Section 10.3 presents the API for the instrument control protocol, and Sections 10.4 to 10.10 show the design and implementation of the classes and the main function used in the server. The complete source code for the server is listed in Section 10.11.
10.2 Introduction
Chapter 9 presents the C++ mapping for the server side, so we are now ready to look at a complete server implementation for the climate control system (CCS). (Before reading on, you may want to review the CCS IDL in Chapter 5.)
For this implementation, we use a simple strategy: the server maintains exactly one instantiated servant for each CORBA object in the system. In other words, the server contains a single servant for the controller singleton, and one servant for each device. In addition, all objects in the server are transient; if the server shuts down, the server forgets all state changes, and object references held by clients become non-functional. The set of thermometers and thermostats in the server is fixed, and clients cannot add devices to the system or remove them. For now, this simple strategy will be sufficient. (Chapters 11 and 12 show more sophisticated implementations that are persistent and offer life cycle operations.)
Throughout this chapter, we incrementally present the source code for the various server components as we discuss them. You can find the full code listing in Section 10.11 at the end of this chapter.
10.3 The Instrument Control Protocol API
To manipulate thermometers and thermostats from the server, we need an API that provides access to our proprietary instrument control protocol. To keep things simple, we use a minimal and hypothetical API known as the Instrument Control Protocol (ICP) API. ( Section A.2 shows an implementation of this API that you can use to simulate a network if you want to experiment with the source code in this book.) The API consists of four C functions defined in the header file icp.h:[1]
[1] If you believe that this API is unrealistically primitive, we beg to differ. We have seen reallife APIs for instrument control that are much worse.
#ifndef _ICP_H
347
IT-SC book: Advanced CORBA® Programming with C++
#define _ICP_H |
|
|
extern "C" { {" |
|
// Add device |
int ICP_online(unsigned long id); |
||
int ICP_offline(unsigned long id); |
// Remove device |
|
int ICP_get( |
id, |
// Get attribute |
unsigned long |
|
|
const char * |
attr, |
|
void * |
value, |
|
size_t |
len |
|
); |
|
// Set attribute |
int ICP_set( |
id, |
|
unsigned long |
|
|
const char * |
attr, |
|
const void * |
value |
|
); |
|
|
} |
|
|
#endif /* _ICP_H */
The ICP functions use an unsigned long value as a network address. The network address of each device must be unique and corresponds to the asset number for thermometers and thermostats. All four functions in the ICP API return zero on success and -1 on failure.
The ICP network views each device as a collection of attributes. A device's attributes correspond directly to its hardware state, such as its register contents. Depending on the device type (thermometer or thermostat), an attribute may be read-only or writable. Any attribute value can be read, but only writable attribute values can be changed. For the climate control system, thermometers and thermostats have the attributes shown in
Table 10.1.
Note that the MIN_TEMP, MAX_TEMP, and nominal_temp attributes are supported only by thermostats. The MIN_TEMP and MAX_TEMP attributes provide the lowest and highest permissible setting of the corresponding thermostat, and nominal_temp contains the current setting of the thermostat. Attempts to read or write one of these attributes on a thermometer are rejected by the thermometer's hardware. Similarly, attempts to write to a read-only attribute are also rejected by the hardware.
For string-valued attributes, each device has a fixed 32-byte block of memory to hold the string (including a terminating NUL byte). Writing a longer string value for the location attribute results in silent truncation.
Table 10.1.. ICP thermometer attributes.
Attribute Name |
Value Type |
Size in Bytes |
Mode |
model |
string |
= 32 |
read-only |
location |
string |
= 32 |
writable |
temperature |
short |
2 |
read-only |
MIN_TEMP[a] |
short |
2 |
read-only |
348
IT-SC book: Advanced CORBA® Programming with C++
MAX_TEMP[a] |
short |
2 |
read-only |
nominal_temp[a] |
short |
2 |
writable |
[a]Supported by thermostats only.
10.3.1Adding and Removing Devices
int ICP_online(unsigned long id); int ICP_offline(unsigned long id);
We assume that our network does not support hardware discovery. Instead, the network must be informed of the existence of a newly connected device by a call to ICP_online that specifies the ID of the new device in the id parameter. ICP_online fails if the passed ID is already in use by another device.
Similarly, the network must be informed of physical disconnection of devices with a call to ICP_offline. The function fails if the passed id does not belong to a device that is currently connected to the network.
A more realistic instrument control protocol would be able to discover new devices automatically. We have chosen not to do this because it makes it easier to simulate the network in software (see Section A.2).
10.3.2 Reading Attribute Values
The ICP_get function reads the value of an attribute from the device specified by the id parameter.
int ICP_get( |
id, |
unsigned long |
|
const char * |
attr, |
void * |
value, |
size_t |
len |
); |
|
The name of the attribute to be read must be supplied as a string in the attr parameter. The function copies the attribute value into the value buffer, whose length must be provided in the len parameter.
ICP_get uses len to avoid overrunning the value buffer. If the value of an attribute does not fit into the value buffer, the value is silently truncated. For numeric attributes, ICP_get copies two bytes into value (provided len is at least 2). For string-valued attributes, ICP_get copies the NUL-terminated string value of the attribute. If a stringvalued attribute is truncated to len bytes, the truncated string is still NUL-terminated.
349
IT-SC book: Advanced CORBA® Programming with C++
The function fails and returns -1 if attempts are made to read from a device that is not on-line or if attr names a non-existent attribute.
Here is a C code fragment that reads the nominal temperature of device 686:
short temp;
if (ICP_get(686, "nominal_temp", &temp, sizeof(temp)) != 0) { /* No such device or attribute */
} else {
/* Got temperature */ printf("nominal_temp: %d\n", temp);
}
10.3.3 Writing Attribute Values
The ICP_set function updates the value of the attribute attr in the device specified by the id parameter.
int ICP_set( |
id, |
unsigned long |
|
const char * |
attr, |
const void * |
value |
); |
|
The value of the attribute is copied from the value buffer. For string-valued attributes, the string stored in the value buffer must be NUL-terminated. If a string is longer than 32 bytes (including the terminating NUL), it is silently truncated to fit. The function fails if attempts are made to set an attribute in a device that is not on-line or attempts are made to update a non-existent or read-only attribute.
Here is a C code fragment that updates the value of the location attribute of device 686:
const char buf[] = "Nearside Kitchen"; if (ICP_set(686, "location", buf) != 0) {
/* No such device or attribute, or read-only attribute */ } else {
/* Update was successful */
}
10.4 Designing the Thermometer Servant Class
The basic shape of thermometer servants is determined by the skeleton class produced by the IDL compiler. The thermometer servant must at least provide implementations for the four attributes in the Thermometer interface, so the basic class header looks like this:
350
IT-SC book: Advanced CORBA® Programming with C++
class Thermometer_impl : public virtual POA_CCS::Thermometer { public:
// CORBA attributes virtual CCS::ModelType
virtual CCS::AssetType
virtual CCS::TempType
virtual CCS::LocType
virtual void
};
Although we could leave this class as shown, we require a few more features for convenient use in our server.
The basic strategy for this implementation is to have one servant instantiated in memory for each device on the network. Each servant keeps its own asset number in a member variable called m_anum. The asset number (which is also an ICP network address) serves as the identity of each device. As you will see in Section 10.6, we use implementation inheritance to implement thermostats; the Thermostat_impl servant class inherits from the Thermometer_impl class in order to reuse its implementation. To allow the derived Thermostat_impl class to access its own identity (provided by the base class), we make m_anum a protected member, and, because the identity of a device is immutable for its lifetime, the m_anum is a const member.
The ICP API is not exactly a model of convenience. This suggests that we add private helper functions to the Thermometer_impl class to hide the details of accessing device attributes via the ICP API. We therefore add the helper functions get_model, get_temp, get_loc, and set_loc to the class.
Our object model contains the controller as a singleton object. As you will see in Section 10.5.3f, it is useful if each servant can access its controller object. Rather than make the controller a global variable, we add to the class a public data member called m_ctrl of type Controller_impl *, which points at the controller servant singleton. Because the member is static, it is shared by all thermometer and thermostat servants.
Our class will need a constructor and a destructor. For each instantiated device, the server must specify at least the asset number of the device. For this implementation, the constructor also accepts a location string. This is necessary because for now, our simple server will have a fixed number of devices at predetermined locations. (We discuss in Chapter 12 how clients can dynamically add and remove devices.)
351