- •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 3. A Minimal CORBA Application
3.1 Chapter Overview
This chapter shows how to build a simple CORBA application consisting of a server that implements a single object and a client that accesses that object. The point of this chapter is to familiarize you with the basic steps required to build a minimal application, and we explain very few details of the source code here. Do not be concerned if something does not seem clear—later chapters provide all the detail.
Section 3.2 shows how to write and compile a simple interface definition, Section 3.2 covers how to write the server, Section 3.4 shows how to write the client, and
Section 3.5 illustrates how to run the complete application.
3.2 Writing and Compiling an IDL Definition
The first step for every CORBA application is to define its interfaces in IDL. For our minimal application, the IDL contains a structure definition and a single interface:
struct TimeOfDay { |
// 0 |
- 23 |
|||
short |
hour; |
||||
short |
minute; |
// |
0 |
- |
59 |
short |
second; |
// |
0 |
- |
59 |
}; |
|
|
|
|
|
interface Time { TimeOfDay get_gmt();
};
The Time interface defines an object that delivers the current time. A Time object has only a single operation, get_gmt. Clients invoke this operation to obtain the current time in the Greenwich time zone. The operation returns the current time as a structure of type TimeOfDay, which contains the current hour, minute, and second.
Having written this IDL definition and placed it in a file called time.idl, you must compile it. The CORBA specification standardizes neither how to invoke the IDL compiler nor what the names of the generated files should be, so the example that follows may need some adjustment for your particular ORB. However, the basic idea is the same for all ORBs with a C++ language mapping.
To compile the IDL, you invoke the compiler with the IDL source file name as a command-line argument. Note that for your ORB, the actual command may be something other than idl.[1]
47
IT-SC book: Advanced CORBA® Programming with C++
[1] We assume a UNIX environment and a Bourne or Korn shell whenever we show commands in this book.
$ idl time.idl
Provided there are no errors in the IDL definition, you will find several new files in the current directory. (The names of these files are ORB-dependent, so you may see file names that differ in name and number from the ones shown here.)
time.hh
This is a header file for inclusion in the client source code. It contains C++ type definitions corresponding to the IDL types used in time.idl.
timeC.cc
This file contains C++ stub code to be compiled and linked into the client application. It provides a generated API that the client application can call to communicate with objects defined in time.idl.
timeS.hh
This is a header file for inclusion in the server source code. It contains definitions that allow the application code to implement an up-call interface to the objects defined in time.idl.
timeS.cc
This file contains C++ skeleton code to be compiled and linked into the server application. It provides the run-time support required by the server application, so it can receive operation invocations sent by clients.
3.3 Writing and Compiling a Server
The source code for the entire server takes only a few lines:
#include <time.h> #include <iostream.h> #include "timeS.hh"
class Time_impl : public virtual POA_Time { public:
virtual TimeOfDay get_gmt() throw(CORBA::SystemException);
};
TimeOfDay
Time_impl::
get_gmt() throw(CORBA::SystemException)
{
48
IT-SC book: Advanced CORBA® Programming with C++
time_t time_now = time(0);
struct tm * time_p = gmtime(&time_now);
TimeOfDay tod;
tod.hour = time_p->tm_hour; tod.minute = time_p->tm_min; tod.second = time_p->tm_sec;
return tod;
}
int
main(int argc, char * argv[])
{
try {
// Initialize orb
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
//Get reference to Root POA. CORBA::Object_var obj
=orb->resolve_initial_references("RootPOA"); PortableServer::POA_var poa
=PortableServer::POA::_narrow(obj);
//Activate POA manager PortableServer::POAManager_var mgr
=poa->the_POAManager();
mgr->activate();
//Create an object Time_impl time_servant;
//Write its stringified reference to stdout Time_var tm = time_servant._this(); CORBA::String_var str = orb->object_to_string(tm); cout < str < endl;
//Accept requests
orb->run();
}
catch (const CORBA::Exception &) {
cerr < "Uncaught CORBA exception" < endl; return 1;
}
return 0;
}
The server implements one Time object. The timeS.hh header file contains an abstract base class called POA_Time. Its definition looks like this (tidied up a little to get rid of code that is irrelevant to the application):
// In file timeS.hh: class POA_Time :
public virtual PortableServer::ServantBase {
49
IT-SC book: Advanced CORBA® Programming with C++
public: |
~POA_Time(); |
virtual |
|
Time_ptr |
_this(); |
virtual TimeOfDay |
get_gmt() |
}; |
throw(CORBA::SystemException) = 0; |
|
Note that this class contains a get_gmt pure virtual method. To create an implementation object that clients can call, we must derive a concrete class from POA_Time that provides an implementation for the get_gmt method. This means that the first few lines of our server program look like this:
#include <time.h> #include <iostream.h> #include "timeS.hh"
class Time_impl : public virtual POA_Time { public:
virtual TimeOfDay get_gmt() throw(CORBA::SystemException);
};
Here, we define a class Time_impl that inherits from POA_Time. This class provides a concrete implementation of a Time object that clients actually can communicate with. Our implementation class is very simple. It has only the single method get_gmt (which is not pure virtual because we require a concrete class that can actually be instantiated).
The next step is to implement the get_gmt method of Time_impl. For now, we are ignoring error conditions. If the call to time fails with a return value of -1, get_gmt returns a garbage time value instead of raising an exception. (We discuss how to deal with errors in Chapters 7 and 9.)
TimeOfDay
Time_impl::
get_gmt() throw(CORBA::SystemException)
{
time_t time_now = time(0);
struct tm * time_p = gmtime(&time_now);
TimeOfDay tod;
tod.hour = time_p->tm_hour; tod.minute = time_p->tm_min; tod.second = time_p->tm_sec;
return tod;
}
This completes the object implementation. What remains is to provide a main function for the server. The first few lines are identical for most servers and initialize the serverside ORB run time:
int
50
IT-SC book: Advanced CORBA® Programming with C++
main(int argc, char * argv[])
{
try {
// Initialize orb
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
//Get reference to Root POA. CORBA::Object_var obj
=orb->resolve_initial_references("RootPOA"); PortableServer::POA_var poa
=PortableServer::POA::_narrow(obj);
//Activate POA manager PortableServer::POAManager_var mgr
=poa->the_POAManager();
mgr->activate();
Do not be concerned about the details of this code for the moment—we will discuss its precise purpose in later chapters.
The next step is to provide an actual servant for a Time object so that clients can send invocations to it. We do this by creating an instance of the Time_impl servant class:
// Create a Time object Time_impl time_servant;
For the client to be able to access the object, the client requires an object reference. In this simple example, we provide that reference by writing it as a string to stdout. Of course, this is not a distributed solution, but it will suffice for now:
//Write a stringified reference
//for the Time object to stdout Time_var tm = time_servant._this();
CORBA::String_var str = orb->object_to_string(tm); cout < str < endl;
The call to _this creates an object reference for the object, and object_to_string converts that reference into a printable string.
At this point we have a concrete implementation of a Time object whose reference is available to the client. The server is now ready to accept requests, something that it indicates to the ORB run time by calling run:
// Accept requests orb->run();
The run method starts an event loop that waits for incoming requests from clients.
51
IT-SC book: Advanced CORBA® Programming with C++
The remainder of the server source code sets an exception handler that prints an error message if anything goes wrong and terminates main. (The closing curly brace at the start of this code fragment completes the try block we opened at the beginning of main.)
}
catch (const CORBA::Exception &) {
cerr < "Uncaught CORBA exception" < endl; return 1;
}
return 0;
}
This completes the server source code. In this short example, most of the source code is boilerplate that you will find in every server. In a more realistic application, most of the server source code consists of the actual operation implementations.
We are now ready to compile and link the server code. The exact compile and link commands you use depend on your compiler and ORB. For example, include paths differ from vendor to vendor, and you may have to add various preprocessor or compiler options. However, the basic idea is the same for all ORBs: you compile the generated stub file (timeC.cc), the generated skeleton file (timeS.cc), and the server source code you have written, which we assume is in the file myserver.cc. Simple compilation commands could look like this:
$ CC -c -I/opt/myORB/include timeC.cc $ CC -c -I/opt/myORB/include timeS.cc
$ CC -c -I/opt/myORB/include myserver.cc
Assuming that there are no errors, this produces three object files that we can link into an executable. Again, the exact link command you use depends on your C++ compiler and ORB vendor. Also, the name and location of the ORB run-time libraries you link with will differ for each vendor. A simple link command is
$ CC -o myserver timeC.o timeS.o myserver.o \ > -L/opt/myORB/lib -lorb
Here, we assume that the ORB run-time library is called liborb. Assuming that there are no errors, we now have a complete executable we can run from the command line. On start-up, the server prints a reference to its Time object on stdout. The server then waits indefinitely for client requests. (To stop the server, we must kill it by sending it a signal.)
$ ./myserver IOR:000000000000000d49444c3a54696d653a312e300000000000000001000000 00000000f000010100000000066d6572676500060b000000d7030231310c000016 7e0000175d360aed118143582d466163653a20457348795e426e5851664e527333 3d4d7268787b72643b4b4c4e59295a526a4c3a39564628296e4345633637533d6a 2c77245879727c7b6371752b7434567d61383b3422535e514a2b48322e772f354f
52
