- •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++
member c is of type char, it can be aligned anywhere, so the value of c is encoded immediately following the string at byte offset 10. The d member of the structure must be aligned on an 8-byte boundary, so c is followed by 5 bytes of padding, followed by the 8 bytes required to hold the value of d.
Note that the size of the structure CD in Figure 13.2 is 13 bytes, whereas in Figure 13.1, a value of the same type consumes 16 bytes. In other words, the amount of padding for a structure varies depending on the starting offset of the structure within a byte stream. This is different from the binary representation of structures in most programming languages. For example, in C++ (at least on most architectures) a structure of type CD would always be aligned on an 8-byte boundary and would consume 16 bytes of memory regardless of what data preceded or followed it. In general, CDR alignment rules apply only to primitive types; there are no separate alignment rules for structured data. Instead, structured data is aligned according to the rules for primitive members, with padding bytes (of undefined value) inserted to maintain alignment.
This example also shows that to correctly decode a CDR-encoded byte stream, the receiver must know what data to expect in advance. For example, the receiver of the byte stream in Figure 13.2 must know in advance that the first data item is a string because that in turns allows the receiver to determine at what offset in the byte stream it can find the structure that follows the string.
Summary
We do not show the encoding of other IDL types here. There are CDR encoding rules that cover all possible IDL types, such as unions, sequences, arrays, exceptions, type codes, type any, object references, and so on. The main point to remember is that all IDL types have well-defined encodings, and that ensures interoperability between ORBs. In general, CDR encoding requires advance knowledge by the receiver of what types of values to expect. This means that CDR-encoded data is not self-describing and that sender and receiver are obliged to honor the interface contract established by IDL definitions.
13.4 GIOP Message Formats
GIOP was first defined by CORBA 2.0, revised with CORBA 2.1, and revised again with CORBA 2.3. This resulted in three versions of GIOP: versions 1.0, 1.1, and 1.2. The main additions in the later versions are support for message fragmentation in GIOP 1.1 and support for bidirectional communication in GIOP 1.2.
Message fragmentation allows for more efficient marshaling of data onto the wire. It permits the sender to send data for a single request in several fragments without having to buffer and marshal in advance all the data for a request.
Bidirectional communication is important for communication through firewalls. For example, the Callback pattern (see Section 20.3) requires a server to also act as a
533
IT-SC book: Advanced CORBA® Programming with C++
client. GIOP 1.2 allows the server to initiate requests on the connection that was opened by the client. This means that the server does not have to open a separate connection for a callback, only to find itself blocked by a firewall.
Later versions of GIOP are backward-compatible with earlier versions. This permits older clients to communicate with newer servers because newer servers must support all previous protocol versions. Similarly, newer clients can communicate with older servers because clients are not allowed to use a later version than the one supported by the server. We do not cover GIOP in full detail in this book. Instead, we cover only a subset to illustrate the general principles. In addition, the discussion that follows covers GIOP versions 1.0 and 1.1. We briefly return to GIOP 1.2 in Section 13.9.
|
Table 13.2. GIOP message types. |
|
Message Type |
|
Originator |
Request |
|
Client |
Reply |
|
Server |
CancelRequest |
|
Client |
LocateRequest |
|
Client |
LocateReply |
|
Server |
CloseConnection |
|
Server[a] |
MessageError |
|
Client or Server |
Fragment[b] |
|
Client or Server |
[a]Can be sent by client or server in GIOP 1.2.
[b]GIOP 1.1 and 1.2.
GIOP has eight message types, as shown in Table 13.2. Of these message types, Request and Reply are the workhorses because they implement the basic RPC mechanism. We show these two message types in some detail and only briefly describe the remainder.
A Request message is always sent from client to server and is used to invoke an operation or to read or write an attribute. Request messages carry all in and inout parameters that are required to invoke an operation.
A Reply message is always sent from server to client, and only in response to a previous request. It contains the results of an operation invocation—that is, any return value, inout parameters, and out parameters. If an operation raises an exception, the Reply message contains the exception that was raised.
By definition, the client is the party that opens a connection, and the server is the party that accepts the connection. To invoke an operation on an object, the client opens a connection and sends a Request message. The client then waits for a Reply message from the server on that connection.
534
IT-SC book: Advanced CORBA® Programming with C++
If client and server must reverse roles—for example, because the server must invoke a callback operation on an object in the client—the server cannot send a request on the connection it accepted from the client. Instead, the server must open a separate connection for which it acts as the client. This means that GIOP is unidirectional as far as client and server roles are concerned.[3]
[3] With GIOP 1.2, client and server can reverse roles while using a single connection. This is particularly important for callback objects provided by applets because the Java sandbox prevents opening of a separate connection to an applet.
To transmit a GIOP message over the wire, the sending side sends a message header, followed by a message body (the contents of the message body depend on the exact message indicated by the header). Figure 13.3 shows the basic structure of a GIOP message.The message header is described in pseudo-IDL:
Figure 13.3 Basic structure of a GIOP message.
module GIOP { |
// PIDL |
|
struct Version { |
|
|
octet |
major; |
|
octet |
minor; |
|
}; |
|
|
enum MsgType_1_1 {
Request, Reply, CancelRequest, LocateRequest, LocateReply, CloseConnection, MessageError, Fragment
}; |
|
|
struct MessageHeader_1_1 { |
// The string "GIOP" |
|
char |
magic[4]; |
|
Version |
GIOP_version; |
|
octet |
flags; |
|
octet |
message_type; |
|
unsigned long message_size;
}; // ...
};
We show the GIOP 1.1 header here (the 1.0 header is very similar). A message header consists of 12 bytes and precedes every GIOP message. Figure 13.4 shows a graphical representation of the components of a message header. The layout of a message header is as follows.
Figure 13.4 A GIOP 1.1 message header indicating a Request message in big-endian byte ordering and without fragmentation.
535
IT-SC book: Advanced CORBA® Programming with C++
The first 4 bytes of a message header are always the characters GIOP. These characters indicate that the message is a GIOP message and also serve to delineate message boundaries.
Bytes 4 and 5 are the major and minor version numbers as 8-bit binary values. Figure 13.4 shows a GIOP 1.1 header; both major and minor version numbers are 1.
Byte 6 is a flags byte. The least significant bit of the flags byte indicates whether the remainder of the message is in big-endian or little-endian encoding: a value of 0 indicates big-endian. The second-least significant bit indicates fragmentation. A value of 1 indicates that this message is a fragment with more fragments to follow. A value of 0 indicates that this message is a complete message or is the last message in a sequence of fragments.
Byte 7 indicates the message type. Its value is the ordinal value of one of the MsgType_1_1 enumerators. The value 0 indicates a Request message.
Bytes 8-11 are a 4-byte unsigned value that indicates the size of the message (not counting the 12 header bytes). The value is encoded as big-endian or little-endian as indicated by the least significant bit of the flags byte.
13.4.1 Request Message Format
A Request message consists of three parts, as shown in Figure 13.5. Following the GIOP header, a Request message contains a Request header and a Request body. The Request header and Request body together form the GIOP message body. The Request header has the following definition:
Figure 13.5 A GIOP Request message.
module GIOP { |
// PIDL |
// ... |
|
struct RequestHeader_1_1 { IOP::ServiceContextList service_context;
unsigned long |
request_id; |
boolean |
response_expected; |
octet |
reserved[3]; |
sequence<octet> |
object_key; |
string |
operation; |
Principal |
requesting_principal; |
}; |
|
// ... |
|
}; |
|
536
IT-SC book: Advanced CORBA® Programming with C++
The fields of the Request header are as follows.
service_context
This sequence contains service data that is silently added to each request by the ORB run time. Its main use is to propagate information required by some ORB services, such as a transaction identifier if the request is made as part of a transaction, or a security context for ORBs that implement the OMG Security Service.
request_id
This field is used by the client to associate the request with its response. The client sets the request_id to a unique number when it sends the request. A Reply message also has a request_id field; when the server sends the reply for a request, it returns the corresponding request_id to the client. In that way, the client can have replies for more than one request outstanding at a time.
response_expected
This field is a Boolean value that is set to true for a normal synchronous request, meaning that the client requires a reply for the request. If the operation being invoked by the client is a oneway operation, the client-side run time can set this field to false (to indicate to the server that no reply is wanted) or to true to allow the client to receive a system exception or a LOCATION_FORWARD reply (see Section 13.4.2).
reserved
These three bytes are reserved for future use and are always set to zero for GIOP 1.1. object_key
The object_key field is the object key of the IOR that was used to invoke the request (see Section 2.5.3). It identifies the particular object in the server that the request is for.
operation
This field is a string that contains the name of the operation being invoked. If the client sends the request to read or write an attribute, the operation name is
_get_attribute_name or _set_attribute_name, respectively.
For operations on the Object base interface, the operation names are _interface,
_is_a, and _non_existent. They correspond to the get_interface, is_a, and non_existent operations on Object. Note that there are no operation names defined for the other operations on Object—namely, duplicate, release, is_nil, is_equivalent, and hash. These operations are always processed by the local ORB and never result in a remote message.
requesting_principal
This field indicates the identity of the calling client for use with the BOA. It is now deprecated because the OMG Security Service instead uses the service_context to indicate the identity of the caller.
537
IT-SC book: Advanced CORBA® Programming with C++
The important fields of a Request header are the operation name, which identifies the operation or attribute, and the object key, which identifies the target object. The remaining data for the request are part of the Request body.
The Request body, which immediately follows the variable-length Request header[3] , contains the in and inout parameters for the request, optionally followed by a Context pseudo-object. (A Context object is present only if the operation definition has a Context clause—see Section 4.13.) The in and inout parameters are marshaled as if they were members of a structure containing the leftmost in or inout parameter to the rightmost in or inout parameter. For example, consider the following operation:
[3] GIOP 1.2 aligns the Request body on an 8-byte boundary instead.
interface foo { void op(
in string param1, out double param2, inout octet param3
);
};
The parameters are sent as if they were part of the following structure:
struct params { string param1; octet param3;
};
Parameter values for the request are sent as if this structure were encoded according to CDR encoding rules. Note that the structure does not contain a member for param2. This parameter is missing because it is an out parameter; there is no point in sending an out parameter from client to server.
13.4.2 Reply Message Format
A server sends a Reply message in response to a client's Request message provided that the response_expected flag of the request was set to true. Like a Request message, a Reply message consists of three parts, as shown in Figure 13.6.
Figure 13.6 A GIOP Reply message.
538
IT-SC book: Advanced CORBA® Programming with C++
Following the GIOP header, a Reply message contains a Reply header and a Reply body that immediately follows the header.[4] The Reply header and Reply body together form the GIOP message body. The Reply header has the following definition:
[4] GIOP 1.2 aligns the Reply body on an 8-byte boundary instead.
module GIOP { // PIDL // ...
enum ReplyStatusType { NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION, LOCATION_FORWARD
};
struct ReplyHeader { IOP::ServiceContextList service_context; unsigned long request_id; ReplyStatusType reply_status;
}; // ...
};
The fields of the ReplyHeader are as follows.
service_context
As with a Request header, this field is used to transparently propagate implicit context information required by ORB services such as the Security and Transaction Services.
request_id
The request_id field returns the ID of the corresponding request to the client. The client uses it to associate replies with requests. This allows the client to have several replies outstanding simultaneously. The server need not send replies in the same order in which it receives requests because some requests may take longer to complete than others.
reply_status
The reply_status field indicates the result of the request.
NO_EXCEPTION
This indicates that the request completed successfully.
USER_EXCEPTION
The request raised a user exception.
SYSTEM_EXCEPTION
The server-side ORB or the server-side application code raised a system exception.
LOCATION_FORWARD
539
IT-SC book: Advanced CORBA® Programming with C++
This reply indicates that the request cannot be processed by this server, but the client should try again at a different address. We discuss the use of this message in Section 14.4.5.
The reply_status field also determines how the Reply body is interpreted by the client. If the operation completed successfully, the Reply body contains the return value, followed by all out and inout parameters for the operation. As with a Request body, the return value and parameters are encoded as if they were members of a structure. If reply_status indicates a user exception, the Reply body contains the repository ID of the exception, followed by the data members of the exception. If the request raised a system exception, the Reply body contains the repository ID of the system exception and its minor code and completion_status. If reply_status is
LOCATION_FORWARD, the Reply body contains an object reference that the client can use to retry the request.
13.4.3 Other Message Formats
The remaining six message formats either are control messages or are provided to permit optimizations. Because they are not relevant to the basic remote procedure call mechanism, we touch on them here only briefly (see [18] for more information).
CancelRequest
With this request, a client can inform a server that it has lost interest in the results of an operation. A client can use this request if, for example, a user cancels a long-running operation. Note that a CancelRequest never aborts an operation implementation while it is executing. Instead, it simply informs the server that it need not bother to send any reply when the operation has completed.
LocateRequest
Clients can use this request to get the current addressing information for an object. The LocateRequest message and the corresponding LocateReply message can reduce the overhead of locating an object (see Section 14.4.6 on page 644).
LocateReply
This is the reply sent by a server in response to LocateRequest message.
CloseConnection
A CloseConnection message from a server informs the client that the server is about to close the connection. If the client wants to communicate with the server again later, it must open a new connection to the server. Typically, a server sends this message if too many clients are connected and the server is about to reach its incoming connection limit.
The CloseConnection message is required because without it, clients could not distinguish intentional shutdown from disorderly shutdown: if the server were to simply
540
