- •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++
because you are free to choose whatever topology suits you best instead of being forced to adapt to a particular topology required by the service.
Your choice of topology for a federation should be governed by two considerations.
The federation structure should reflect the partitioning of your organization into administrative domains. The closer this match is, the easier it will be to maintain and modify the federation.
The federation structure should reflect the frequency distribution of names used by clients. The most frequently used names should be resolved locally, and only less frequently used names should involve more than one server in the federation. This leads to better performance, scalability, and fault tolerance.
As always, if you spend some time analyzing your federation requirements early, you will find that time amply repaid over the lifetime of a system that uses the federated service.
18.14 Adding Naming to the Climate Control System
The climate control system we have developed so far has the problem that the reference to the controller object is passed from the server to the client via a file. Clearly, this is not a distributed solution because either client and server must share a common file system, or the stringified reference to the controller must be copied from the server machine to the client machine.
The Naming Service offers a clean solution to the problem. The server advertises the controller reference in the Naming Service, and the client locates the reference using its name. There still is some coupling between client and server because we assume that both client and server machines either use the same initial naming context or at least use initial naming contexts that are part of the same federation. However, the important point is that the coupling between client and server is much looser now. The client and server are coupled via an external service instead of having to share file systems.
For the climate control system, we advertise only the controller reference in the Naming Service but do not advertise individual thermometers and thermostats. This makes sense because we already have the find operation, which allows clients to locate devices by their name (the asset number or room name). As we discuss in Section 18.11, this is not necessarily the only way to approach naming. Depending on your requirements and how much you are prepared to rely on the availability of the Naming Service, you may choose to advertise more than just a single bootstrapping object in the Naming Service.
18.14.1 Generic Helper Functions
706
IT-SC book: Advanced CORBA® Programming with C++
Before we show the details of how to update the client and the server, we present two helper functions to simplify the source code. Consider the typical sequence of steps to resolve a reference.
Call resolve_initial_references to get a reference to the initial naming context.
Narrow the returned reference to CosNaming::NamingContext. Test for nil to ensure that the reference is of the correct type.
Create a name.
Call resolve to obtain the reference corresponding to the name. Narrow the returned reference to its expected type.
Test for nil to ensure that the reference is of the correct type.
If you go through these steps as in-line code, you will find yourself writing similar code again and again. This not only makes your code harder to test and maintain but also makes it harder to understand because all the extra lines of code can obscure the intent (namely, to use a name to obtain an object reference).
As always in such cases, you can use simple helper functions to improve your code considerably.
Obtaining Initial References Generically
We can create a simple resolve_init helper function that, given a token, returns the reference to the specified initial reference as its correct type. In other words, resolve_init not only obtains the reference but also calls _narrow. To obtain an initial reference—for example, to the Naming Service—we call resolve_init this way:
CosNaming::NamingContext_var inc;
inc = resolve_init<<CosNaming::NamingContext>(orb, "NameService");
Because resolve_init is a template function, we can use it to obtain other initial references—for example, for the Root POA:
PortableServer::POA_var poa;
poa = resolve_init<<PortableServer::POA>(orb, "RootPOA");
Following is the code for resolve_init. We include simple error handling here. As usual, we throw zero for handled exceptions that should terminate the program:
template<<class T> typename T::_ptr_type
resolve_init(CORBA::ORB_ptr orb, const char * id)
{
CORBA::Object_var obj; try {
707
IT-SC book: Advanced CORBA® Programming with C++
obj = orb->resolve_initial_references(id);
}
catch (const CORBA::ORB::InvalidName & e){ throw;
}
catch (const CORBA::Exception & e) {
cerr << "Cannot get initial reference for" << id << ": " << e << endl;
throw 0;
}
assert(!CORBA::is_nil(obj)); typename T::_var_type ref; try {
ref = T::_narrow(obj);
}
catch (const CORBA::Exception & e) { cerr << "Cannot narrow reference for"
<< id << ": " << e << endl; throw 0;
}
if (CORBA::is_nil(ref)) {
cerr << "Incorrect type of reference for" << id << endl;
throw 0;
}
return ref._retn();
}
This code illustrates use of the _ptr_type and _var_type aliases you saw in Section 7.6.1. The aliases permit us to use _ptr and _var references in the template function without having to declare additional template parameters for these types. Without the aliases, resolve_init would require three template parameters instead of one:
template<<class T, class T_ptr, class T_var> T_ptr
resolve_init(CORBA::ORB_ptr orb, const char * id)
{
// ...
}
// ...
CosNaming::NamingContext_var inc; inc = resolve_init<<
CosNaming::NamingContext, CosNaming::NamingContext_ptr, CosNaming::NamingContext_var
>(orb, "NameService");
The _ptr_type and _var_type definitions allow us to avoid such verbose template instantiations.[9]
[9] The _ptr_type and _var_type aliases were added to the mapping only recently. If your ORB does not yet provide them, you must use the three-parameter version of resolve_init.
708
IT-SC book: Advanced CORBA® Programming with C++
Note that the C++ mapping also generates _var_type definitions for structures, unions, and sequences. If you want to create template functions that deal with these types, you can refer to the corresponding _var type from inside the template.
Resolving Bindings Generically
You can use a similar helper function to resolve bindings in a naming context. Again, the helper function hides the call to _narrow and provides error handling. The client calls it this way:
CosNaming::NamingContext_var inc = ...; // Get initial context
CosNaming::Name n; n.length(2);
n[0].id = CORBA::string_dup("CCS"); n[1].id = CORBA::string_dup("Controller"); CCS::Controller_var ctrl;
ctrl = resolve_name<<CCS::Controller>(inc, n);
The resolve_name template function is quite similar to resolve_init:
template<<class T> typename T::_ptr_type resolve_name(
CosNaming::NamingContext_ptr nc, const CosNaming::Name & name)
{
CORBA::Object_var obj; try {
obj = nc->resolve(name);
}
catch (const CosNaming::NamingContext::NotFound & e) { throw;
}
catch (const CORBA::Exception & e) {
cerr << "Cannot resolve binding: " << e << endl; throw 0;
}
if (CORBA::is_nil(obj)) {
cerr << "Nil binding in Naming Service" << endl; throw 0;
}
typename T::_var_type ref; try {ref = T::_narrow(obj);
}
catch (const CORBA::Exception & e) {cerr << "Cannot narrow reference: " << e << endl;
throw 0;
}
if (CORBA::is_nil(ref)) {
cerr << "Reference has incorrect type" << endl; throw 0;
709
IT-SC book: Advanced CORBA® Programming with C++
}
return ref._retn();
}
18.14.2 Updating the Climate Control System Server
The Naming Service allows us to avoid passing a stringified IOR from server to client. For this example, whenever the climate control server starts, it readvertises the controller reference using the name CCS/Controller. The code uses the resolve_init template function defined in Section 18.14.1 to get a reference to the initial naming context:
#include <CosNaming.hh> // ORB-specific
// ...
int
main(int argc, char * argv[])
{
try {
//...
//Create controller servant and get its refe
rence.
CCS::Controller_var ctrl = ...;
// Get reference to initial naming context. CosNaming::NamingContext_var inc
= resolve_init<CosNaming::NamingContext>( orb, "NameService"
);
// Attempt to create CCS context. CosNaming::Name n;
n.length(1);
n[0].id = CORBA::string_dup("CCS"); try {
CosNaming::NamingContext_var nc
=inc->bind_new_context(n);
}catch (const CosNaming::NamingContext::Alre adyBound &) {
//Fine, CCS context already exists.
}
//Force binding of controller reference to make
//sure it is always up-to-date.
n.length(2);
n[1].id = CORBA::string_dup("Controller"); inc->rebind(n, ctrl);
// ...
}
catch (const CORBA::Exception & e) {
cerr < "Uncaught CORBA exception: " < e < endl; return 1;
710
IT-SC book: Advanced CORBA® Programming with C++
} |
|
catch (...) { |
// Unexpected exception, dump core |
abort(); |
|
} |
|
return 0; |
|
}
The server code includes the generated stub header file for the Naming Service. Note that the include directive for CosNaming.hh is ORB-specific because CORBA does not standardize the names or locations of header files. However, most ORBs ship with precompiled headers and stub libraries, so you do not have to separately compile the IDL for the Naming Service.
The remainder of the code is trivial. After obtaining the initial naming context, the code attempts to create the CCS context by calling bind_new_context. If the context already exists, the operation raises AlreadyBound, which is ignored. The second step is to call rebind, which unconditionally creates a new binding for the controller or replaces whatever reference was previously bound with the name Controller. For a persistent server, strictly speaking it is not necessary to replace the reference if it is already bound. However, it does no harm and ensures that the reference is always up-to- date even if the server was moved into a different location domain (see Chapter 14).
18.14.3 Updating the Climate Control System Client
The resolve_init and resolve_name template functions defined in Section 18.14.1 make it trivial to modify the client to retrieve the controller reference from the Naming Service instead of the command line:
#include <CosNaming.hh> // ORB-specific
// ...
int
main(int argc, char * argv[])
{
try {
// Initialize the ORB
CORBA::ORB_var orb = CORBA::ORB_init(argc, ar gv);
//Check arguments if (argc != 1) {
cerr << "Usage: client" << endl; throw 0;
}
//Get reference to initial naming context. CosNaming::NamingContext_var inc
=resolve_init<<CosNaming::NamingContext>( orb, "NameService"
);
711
