
- •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++
connections. POAManager objects logically represent communications endpoints where objects listen for requests. In some ORB implementations, POAManager objects encapsulate connections and perform network connection management. When created, these POAManager implementations start listening for incoming requests, and when they are deactivated, they stop listening and close their connections.
11.11 ORB Event Handling
Any CORBA application that acts as a server must listen for and handle events such as incoming connections from clients and their subsequent requests. With respect to event handling, server applications fall into one of two categories.
In some applications, only the ORB has the need to listen for and handle such events. These applications can simply turn the main thread of control over to the ORB so that it can handle requests and dispatch them to its object adapters and servants. Such applications are said to perform blocking event handling because the application main blocks until the ORB shuts down its event handling and returns control to main.
In other applications, the ORB is only one of several components that must perform event handling. For example, a CORBA application with a graphical user interface (GUI) must allow the GUI to handle windowing events in addition to allowing the ORB to handle incoming requests. These types of applications therefore perform non-blocking event handling. They turn the main thread of control over to each of the various event-handling subsystems while not allowing any of them to block for significant periods of time.
Just as POAManager objects give you control over the request flow for your POAs, the ORB provides operations that allow you to control request flow and event handling for your whole application, including all object adapters.[1] Following are the definitions of these operations.
[1] Although they are not strictly POA-specific, we cover ORB event-handling issues in this chapter because they, like the POA, relate to server applications.
#pragma prefix "omg.org"
module CORBA { interface ORB {
void |
run(); |
void |
shutdown(in boolean wait_for_completion); |
boolean |
work_pending(); |
void |
perform_work(); |
// ... |
|
}; |
|
// ... |
|
}; |
|
445

IT-SC book: Advanced CORBA® Programming with C++
These operations support both the blocking and the non-blocking varieties of eventhandling applications. The run operation, which is blocking, causes the application to start listening for requests. After an application is listening for requests, you can invoke shutdown to make it stop listening. For non-blocking event handling, you use work_pending and perform_work. We supply details of how each of these operations works in the following sections.
If the ORB did not provide such operations, applications would have to individually tell each POA or POAManager to listen for requests. This in turn would mean that your application main would have to know about all POAs in your application and deal with all of them directly. Instead, applications initiate event handling at the ORB level, and it delegates to each of its object adapters.
11.11.1 Blocking Event Handling
The ORB::run operation blocks until the ORB has shut down. By invoking run from the thread executing your application main, you permit the ORB to take over the main thread to perform its work. The ORB keeps control of the main thread and does not return until after you invoke ORB::shutdown and the ORB completely shuts itself down. Invoking run from any other thread merely blocks that thread by making it wait for ORB shutdown.
11.11.2 Non-Blocking Event Handling
The ORB::run operation suffices for applications that operate correctly when the ORB takes over the main thread. For applications that share the main thread with other event loops, however, yielding control of the main thread to the ORB is unacceptable. Instead, such applications need a way to determine when the ORB requires the main thread to perform some work and then to temporarily hand over control of the main thread to the ORB to complete that work.
To determine whether the ORB has any work items pending, you call work_pending. It returns true if the ORB needs the main thread to perform some work; otherwise, it returns false. If work_pending returns true, you can temporarily give control of the main thread to the ORB by invoking perform_work.
//The handle_gui_events function allows the user
//interface to refresh itself and handle its events.
//It returns true if the user has clicked the
//"exit" button.
extern bool handle_gui_events();
int
main(int argc, char * argv[])
{
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
// Initialize POAs and POAManagers and then activate
446

IT-SC book: Advanced CORBA® Programming with C++
//objects (not shown).
//Enter event loop. bool done = false; while (!done) {
if (orb->work_pending()) orb->perform_work();
done = handle_gui_events();
}
orb->shutdown(1);
return 0;
}
In this example we use the non-blocking ORB event-handling functions to allow our GUI to handle its events as well. After initializing the ORB and POAs and then activating our objects, we enter an event loop. In the loop we first call work_pending to see whether the ORB has any work items it needs to complete. If work_pending returns true, we invoke perform_work to let the ORB do its work. We then call our hypothetical handle_gui_events function to let our GUI handle input from the user. If the user clicks the GUI's exit button, handle_gui_events returns true, so we exit our event loop and shut down our application.
The "size" of the unit of work that the ORB performs is implementationdependent, but it could involve activities such as reading an incoming message from a socket or dispatching a request to an object adapter. This means that the amount of time that perform_work blocks varies from ORB to ORB and potentially from one invocation to the next.
Single-threaded CORBA server applications that also have other event loops must use work_pending and perform_work as shown in the preceding example. For multithreaded applications, however, an alternative to an event loop like this is to invoke ORB::run in the main thread and invoke any other event loops for other parts of the application in their own separate threads. This approach will not work if the other event loops also require the main thread to get their work done, but it is a viable alternative for most multithreaded applications. We discuss this issue in more detail in Chapter 21.
11.11.3 Application Shutdown
When you want to shut down your application, you invoke ORB::shutdown. It takes a single boolean argument that tells it whether to block waiting for all shutdown activities to finish or whether it can return before all shutdown work has completed.
Server applications generally shut down in one of three ways.
447

IT-SC book: Advanced CORBA® Programming with C++
The application can use a time-out approach. If it does not receive any requests within a certain amount of time, the application initiates its own shutdown.
The user can force a shutdown by sending a signal to the running application. For example, on UNIX the user might generate an interrupt for the server by typing the interrupt character (usually Ctrl-C). If the application has a GUI, the user might click a button to inform the application to exit.
Another application might invoke some sort of shutdown operation provided by one of the application's CORBA objects.
We discuss these approaches in more detail in the following sections.
Shutdown via Time-Out
Most ORBs supply a proprietary operation that acts like ORB::run but takes a time-out parameter. This parameter typically specifies a time-out period as a number of seconds. If the specified amount of time elapses without the application handling any CORBA requests, the ORB run time initiates application shutdown.
Because ORB systems are capable of activating server processes when needed (see Chapter 21), having a server shut itself down after a time-out has elapsed is quite practical. It prevents idle servers from running, thereby needlessly using machine and operating system resources, and it helps garbagecollect transient objects.
The time-out approach, however, is not without drawbacks. CORBA does not provide a standard time-out-based shutdown operation (and it is not clear that anything like it should be standardized), so if you want to shut down your applications based on timeouts, you must use whatever proprietary functions your ORB vendor gives you. Servers that shut down based on time-outs can also cause clients who infrequently issue requests to transient objects to occasionally find that their transient objects have unexpectedly disappeared. Section 12.7.4 discusses details of how a time-out-based shutdown approach affects object life cycles.
Shutdown via Signals
If you start your servers from a command line or from a control script, you might want to use UNIX signals, Windows console events, or GUI controls to shut them down. Shutting down due to a GUI button click is easy because your application receives the shutdown notification synchronously, but shutting down due to an asynchronous signal or console event is more difficult.
If your application's GUI provides a button for you to click to initiate shutdown, for example, the code that runs when you click the button eventually calls ORB::shutdown. This approach is easy and straightforward, as we demonstrate with the example in Section 11.11.2.
448

IT-SC book: Advanced CORBA® Programming with C++
The main difficulty with shutting your application down correctly and cleanly when it receives a signal lies mostly in non-portability issues with signal handling. This is especially true for multithreaded applications. Some operating systems require that you establish a single signal-handling thread, whereas others can deliver an asynchronous signal to whatever thread is running when the signal arrives. Windows console events are somewhat (but not quite) like UNIX signals, making portable signal handling more difficult.
Fortunately, some ORBs supply portable signal-handling abstractions that hide the details and idiosyncrasies of each platform's signal-handling mechanisms. These services generally require you to supply a callback handler function that is invoked when a signal typically used to kill a process (such as SIGINT and SIGTERM) or a console event arrives. You can write your handler function to initiate ORB shutdown, as shown here:
//File-static ORB reference. static CORBA::ORB_var orb;
//Signal-handling function. static void
async_handler()
{
if (!CORBA::is_nil(orb)) orb->shutdown(0);
}
int
main(int argc, char * argv[])
{
//First set up our asynchronous signal handler. TerminationHandler::set_handler(async_handler);
//Initialize the ORB.
orb = CORBA::ORB_init(argc, argv);
// ...
}
We pass a pointer to the async_handler function to our proprietary
TerminationHandler::set_handler function to register async_handler as the callback to handle signal events. We then code the rest of the main as usual.
Note that in the async_handler function we call ORB::shutdown with a false (0) argument. This is to avoid blocking the handler function by making it wait for all requests currently in progress to finish, then for all object adapters to shut down, and finally for the ORB itself to shut down. For portability reasons, your handler functions should perform as little work as possible because some operating systems limit the types of activities that signal handlers are allowed to perform.
We recommend using these abstractions if the ORB you use provides them. However, beware that if code in your application already makes extensive use of signals, these
449

IT-SC book: Advanced CORBA® Programming with C++
proprietary signal-handling abstractions may not work for you. Even worse, your ORB may not supply such an abstraction. If it does not, you should ask your ORB vendor how it recommends that you deal with portable signal-initiated ORB shutdown.
Shutdown via CORBA Requests
The third approach to shutting down server applications involves sending a shutdown request to an object in the server. Such an object might have an interface like the following:
interface ProcessTerminator { void shutdown();
};
Only one such object is needed per process, so you might add a shutdown operation to some other object's interface rather than create a whole new interface for it. For example, for our CCS server, we could make shutdown part of the Controller interface. The body of the shutdown method would look much like that of our signalhandling code from the preceding section:
void MyProcessTerminator::
shutdown() throw(CORBA::SystemException)
{
orb->shutdown(0);
}
In this case, we are required to pass a false (0) value to ORB::shutdown to avoid deadlock. If we passed a true value, ORB::shutdown would try to wait for all requests to complete before returning, but because we are calling it from within a request, we would be blocking it from returning.
This approach looks simple enough, but it has several drawbacks.
Initiating ORB shutdown causes the ORB to shut down all its object adapters. This means that all client connections will be closed, including the one to the client that invoked this request. This in turn might mean that the connection will be closed before the ORB sends the response, and that will cause the client ORB to raise a CORBA::COMM_FAILURE exception.
You can alleviate this problem in some cases by declaring the ProcessTerminator::shutdown operation as oneway to let the client ORB run time know that it should not expect a response (see Section 4.12). However, oneway is highly dependent on the underlying transport and protocol, and, in some cases, a full round-trip request-response cannot be avoided. For example, in the OMG standard Distributed Computing Environment Common Inter-ORB Protocol (DCE-CIOP), a
450