
- •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++
tmstat2 = CCS::Thermostat::_narrow(o); |
// OK |
Testing for nil other than with CORBA::is_nil
CCS::Thermostat_ptr tmstat = CCS::Thermostat::_nil();
if |
(tmstat) ... |
// |
Illegal! |
if |
(tmstat != 0) ... |
// |
Illegal! |
if |
(tmstat != CCS::Thermostat::_nil()) ... // Illegal! |
|
if |
(!CORBA::is_nil(tmstat)) ... |
// OK |
7.7 Pseudo-Objects
So far, we have skirted the issue of how a client actually obtains an object reference. To address this, we must look at pseudo-objects and examine how a client initializes the ORB and how it gets its initial object references.
The CORBA specification defines a number of interfaces to the ORB run time. Because CORBA supports several different implementation languages, these interfaces must be specified in a language-independent way. IDL is perfectly suited to this; a single IDL specification describes an interface for all supported implementation languages.
To avoid polluting the global namespace, interfaces defined by CORBA are placed in the CORBA module. Following is a small part of the contents of that module.
module CORBA { |
// PIDL |
interface ORB { |
|
// ... |
|
}; |
|
// ... |
|
}; |
|
Note the PIDL comment for the module. It stands for pseudo-IDL. Pseudo-IDL definitions are like ordinary IDL definitions and use the same data types, operations, attributes, and so on. There is almost no syntactic difference between PIDL and IDL— but see the definition of ORB_init on page 242.
Why bother with PIDL? The answer is that some interfaces to the ORB cannot be implemented as ordinary CORBA objects but instead must be implemented by library code that ships with the ORB. In particular, interfaces to the ORB run time must be implemented this way, and the PIDL comment marks such interfaces.
Interfaces defined in PIDL are subject to a number of restrictions.
Pseudo-interfaces do not implicitly inherit from Object.
220

IT-SC book: Advanced CORBA® Programming with C++
Pseudo-interfaces cannot be passed as arguments to operations on ordinary interfaces. (The TypeCode pseudo-interface is exempt from this rule—see Section 16.3.3.)
Operations on pseudo-interfaces cannot be invoked via the Dynamic Invocation Interface (DII).
Pseudo-interfaces do not have definitions in the Interface Repository.
Pseudo-interfaces may have a special-purpose language mapping that deviates from the normal rules.
All this sounds terribly restrictive, but it is not because there is no need ever to use any of the restricted features for pseudo-objects. The one noticeable difference between PIDL and ordinary objects is that PIDL objects may have a special-purpose language mapping. We point out such differences as we discuss the relevant PIDL. Usually, differences from the normal mapping rules exist to avoid restricting ORB implementers in their range of choices or to make the relevant interface easier to use.
7.8 ORB Initialization
Before a client can do anything, it must initialize the ORB run time. The initialization call is defined in the CORBA module:
module CORBA { |
ORBid; |
// PIDL |
typedef string |
arg_list; |
|
typedef sequence<string> |
||
interface ORB; |
// Forward declaration |
ORB ORB_init(inout arg_list argv, in ORBid orb_identifier);
// ...
};
The CORBA module defines an operation ORB_init, which initializes the ORB run time and returns a pseudo-reference to the ORB object. Note that the ORB_init operation is not declared inside an interface. This is legal in PIDL, whereas in normal IDL it would be an error (operation declarations can occur only inside an interface).
Before we discuss the details of ORB_init, let us take a look at its C++ mapping:
namespace CORBA { |
|
// ... |
|
ORB_ptr ORB_init( |
argc, |
int & |
|
char ** |
argv, |
const char * |
orb_identifier = "" |
); |
|
// ... |
|
221

IT-SC book: Advanced CORBA® Programming with C++
}
The ORB_init function expects three arguments.
argc is the number of entries in argv.
argv is the command-line argument vector passed to main. orb_identifier is a vendor-specific string (defaulted to the empty string).
A typical client main looks something like this:
int
main(int argc, char * argv[])
{
CORBA::ORB_ptr orb;
try {
orb = CORBA::ORB_init(argc, argv);
}
catch (...) {
cerr << "Cannot initialize ORB" << endl; exit(1);
}
// Use ORB...
CORBA::release(orb);
return 0;
}
ORB_init receives a reference to argc and an argv vector from the client and examines argv for ORB-specific options beginning with -ORB. ORB_init removes any ORB-specific options from argv so when the call returns, the argument vector contains only the remaining options that concern the application rather than the ORB.
The orb_identifier argument to ORB_init identifies the particular ORB to initialize. This behavior is useful if an application needs to initialize more than one ORB run-time environment. The application can also use orb_identifier to select a particular set of configuration values or quality-of-service parameters. CORBA does not precisely specify the effects of the orb_identifier argument, so you must consult your ORB's documentation for details.
The default orb_identifier is the empty string, which instructs the implementation to use whatever default behavior has been configured. If orb_identifier is the empty string, ORB_init scans the argument vector for an option of the form -ORBid arg. If this option is present, the value of arg determines the behavior. If orb_identifier is a non-empty string and if -ORBid is also used, orb_identifier overrides the value of the -ORBid option.
222

IT-SC book: Advanced CORBA® Programming with C++
ORB_init returns a reference to the ORB pseudo-object. Clients and servers always obtain their first object reference this way; the ORB pseudo-object contains operations that can be called to obtain further references. Note that you must eventually release the returned reference (pseudo-references must be released just as normal references are). Releasing the ORB reference instructs the ORB run time to clean up. This means that you must release the ORB reference last because other ORB-related calls may no longer work after the run time has cleaned up.
Note that you cannot use the ORB before the code has entered main because you must pass argc and argv parameters to ORB_init. In particular, you cannot make CORBA-related calls from constructors for global or static C++ objects. Do not try to cheat by passing dummy argc and argv parameters to ORB_init before the code has entered main; the result may be a core dump. For example, ORB_init could fail catastrophically because it may itself depend on side effects from global constructors in the ORB run-time libraries.
In general, you should ban global objects from your code. As shown in [11], global objects inevitably cause more problems than they solve. However, the ORB pseudoobject typically must be accessible from anywhere in your source code. A good way to make the object globally accessible is to use the Singleton pattern [4].
7.9 Initial References
After the client has initialized the ORB, it can obtain further references by invoking operations on the ORB interface:
module CORBA { // PIDL // ...
interface ORB {
string object_to_string(in Object obj); Object string_to_object(in string str); // ...
}; // ...
};
The ORB interface contains two operations that can be used to create and obtain initial references.
object_to_string This operation converts a reference into a printable string—for example, for storing a reference on disk.
string_to_object This operation converts a stringified reference back into an object reference.
The C++ mapping for these operations is as follows:
223

IT-SC book: Advanced CORBA® Programming with C++
namespace CORBA {
//...
class ORB { public:
char * object_to_string(Object_ptr p); Object_ptr string_to_object(const char * s);
};
//...
}
A client uses these operations by invoking them on the ORB pseudo-object.
7.9.1 Conversion from String to Reference
The following example shows how a client obtains a reference to our climate controller object from the command line.
// Initialize ORB.
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
//Assume argv[1] is a stringified reference to a controller. CORBA::Object_ptr obj;
try {
obj = orb->string_to_object(argv[1]);
}
catch (...) {
cerr << "Bad format for stringified reference" << endl; exit(1);
}
//Check that reference is non-nil.
if (CORBA::is_nil(obj)) {
cerr << "Passed reference is nil" << endl; exit(1);
}
//Narrow to controller. CCS::Controller_ptr ctrl; try {
ctrl = CCS::Controller::_narrow(obj);
}
catch (...) {
cerr << "Narrow failed" << endl; exit(1);
}
//Don't need base interface anymore. CORBA::release(obj);
//Was the reference of the correct type? if (CORBA::is_nil(ctrl)) {
cerr << "Argument is not a controller reference" << endl; exit(1);
}
224

IT-SC book: Advanced CORBA® Programming with C++
//
//Use controller reference...
//Clean up
CORBA::release(ctrl); |
// Narrow calls _duplicate |
CORBA::release(orb); |
// Clean up |
There is quite a bit happening in this example, so we cover the code in stages.
Note that pseudo-operations such as string_to_object and _narrow can throw exceptions. We cover exception handling in detail in Section 7.15. For now, our exception handling is to print an error message and exit whenever any exception is thrown at all.
Keep in mind that calling exit is fine for operating systems such as UNIX, in which the kernel guarantees recovery of resources allocated to a process. However, in DOS or Windows, this strategy will eventually get you into trouble because memory allocated in DLLs is not necessarily recovered by the operating system when a process exits. If you are writing code for such an environment, you must release resources allocated to your process before you exit; otherwise, the machine will eventually run out of memory.
obj = orb->string_to_object(argv[1]);
This call converts a stringified object reference back to a reference. The returned reference has the type CORBA::Object_ptr. Because Object is at the root of the interface inheritance tree, string_to_object can return references of arbitrary interface type.
string_to_object creates a new proxy, so you must eventually release the reference again by calling CORBA::release.
If the passed string is syntactically invalid, string_to_object throws an exception.
if (CORBA::is_nil(obj)) ...
The string passed as argv[1] may be a valid reference, but that does not guarantee that it is non-nil. The client explicitly tests for this condition and complains if a nil reference is passed.
ctrl = CCS::Controller::_narrow(obj);
The client expects a reference to a climate controller (not to some other interface). The call to _narrow determines whether the passed reference is of the correct type. If _narrow returns nil, the passed reference is of the wrong type.
225

IT-SC book: Advanced CORBA® Programming with C++
_narrow creates a new proxy, so you must eventually release the returned reference again by calling CORBA::release.
_narrow raises an exception if the ORB cannot reliably determine whether the reference is of the expected type. Usually, the exception is either TRANSIENT or OBJECT_NOT_EXIST. We cover the semantics of these exceptions in Section 7.15.2.
CORBA::release(obj);
The client does not need to keep the reference obj (of type Object_ptr) after it has successfully narrowed it, so it might as well release it.
After the client has narrowed the reference to the correct type, the client can use it to invoke operations on the corresponding object.
CORBA::release(ctrl);
When the client is no longer interested in the reference, it calls CORBA::release to reclaim its resources.
CORBA::release(orb);
This is the final ORB-related call in all clients. Releasing the ORB pseudo-object instructs the run time that no further CORBA activity will take place and that all CORBA run-time resources should be released.
7.9.2 Conversion from Reference to String
The object_to_string operation converts an object reference into a string:
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
CCS::Controller_ptr ctrl = ...; // Get reference...
char * s; try {
s = orb->object_to_string(ctrl);
}
catch (...) {
cerr << "Cannot convert reference to string" << endl; exit(1);
}
cout << s << end; |
// Print reference |
CORBA::string_free(s); |
// Finished with string |
CORBA::release(ctrl); |
// Release controller proxy |
CORBA::release(orb); |
// Shut down run time |
object_to_string returns the stringified form of the passed reference. As always, the returned string is dynamically allocated, so the preceding code calls string_free
226