- •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 13. GIOP, IIOP, and IORs
13.1 Chapter Overview
Even though CORBA goes to great lengths to shield applications from the details of networking, it is useful to have at least a basic understanding of what happens under the hood of an ORB. In this chapter, we present an overview of the General Inter-ORB Protocol (GIOP) and the Internet Inter-ORB Protocol (IIOP), and we explain how protocol-specific information is encoded in object references. Our treatment is by no means exhaustive. We show just enough of the protocols to give you a basic understanding of how CORBA achieves interoperability without losing extensibility. Unless you are building your own ORB, the precise protocol details are irrelevant. You can consult the CORBA specification [18] if you want to learn more.
Sections 13.2 to 13.6 provide an overview of GIOP, including the requirements it makes on the underlying transport and its data encoding and message formats. Section
13.7 then describes IIOP, which is a concrete realization of the abstract GIOP specification. Section 13.8 shows how IORs encode information so that the protocols
available for communication can be extended without affecting interoperability. Section 13.9 outlines changes made to the protocols with the CORBA 2.3 revision.
13.2 An Overview of GIOP
The CORBA specification defines the GIOP as its basic interoperability frame-work. GIOP is not a concrete protocol that can be used directly to communicate between ORBs. Instead, it describes how specific protocols can be created to fit within the GIOP framework. IIOP is one concrete realization of GIOP. The GIOP specification consists of the following major elements.
Transport assumptions
GIOP makes a number of assumptions about the underlying transport layer that carries GIOP protocol implementations.
Common Data Representation (CDR)
GIOP defines an on-the-wire format for each IDL data type, so sender and receiver agree on the binary layout of data.
Message formats
GIOP defines eight message types that are used by clients and servers to communicate. Only two of these messages are necessary to achieve the basic remote procedure call semantics of CORBA. The remainder are control messages or messages that support certain optimizations.
13.2.1 Transport Assumptions
528
IT-SC book: Advanced CORBA® Programming with C++
GIOP makes the following assumptions about the underlying transport that is used to carry messages.
The transport is connection-oriented.
A connection-oriented transport allows the originator of a message to open a connection by specifying the address of the receiver. After a connection is established, the transport returns a handle to the originator that identifies the connection. The originator sends messages via the connection without specifying the destination address with each message; instead, the destination address is implicit in the handle that is used to send each message.
Connections are full-duplex.
The receiving end of a connection is notified when an originator requests a connection. The receiver can either accept or reject the connection. If the receiver accepts the connection, the transport returns a handle to the receiver. The receiver not only uses the handle to receive messages but can also use it to reply to the originator. In other words, the receiver can reply to the requests sent by the originator via the same single connection and does not need to know the address of the originator in order to send replies.
Connections are symmetric.
After a connection is established, either end of the connection can close it.
The transport is reliable.
The transport guarantees that messages sent via a connection are delivered no more than once in the order in which they were sent. If a message is not delivered, the transport returns an error indication to the sender.
The transport provides a byte-stream abstraction.
The transport does not impose limits on the size of a message and does not require or preserve message boundaries. In other words, the receiver views a connection as a continuous byte stream. Neither receiver nor sender need be concerned about issues such as message fragmentation, duplication, retransmission, or alignment.
The transport indicates disorderly loss of a connection.
If a network connection breaks down—for example, because one of the connection endpoints has crashed or the network is physically disrupted—both ends of the connection receive an error indication.
This list of assumptions exactly matches the guarantees provided by TCP/IP. However, other transports also meet these requirements. They include Systems Network Architecture (SNA), Xerox Network Systems' Internet Transport Protocol (XNS/ITP), Asynchronous Transfer Mode (ATM), HyperText Transfer Protocol Next Generation (HTTP-NG), and Frame Relay.[1]
[1] The only standardized protocol based on GIOP is IIOP, which uses TCP/IP as its transport. However, the OMG is likely to specify inter-ORB protocols for other transports in the future.
529
IT-SC book: Advanced CORBA® Programming with C++
13.3 Common Data Representation
GIOP defines a Common Data Representation that determines the binary layout of IDL types for transmission. CDR has the following main characteristics.
CDR supports both big-endian and little-endian representation.
CDR-encoded data is tagged to indicate the byte ordering of the data. This means that both big-endian and little-endian machines can send data in their native format. If the sender and receiver use different byte ordering, the receiver is responsible for byteswapping. This model, called receiver makes it right, has the advantage that if both sender and receiver have the same endianness, they can communicate using the native data representation of their respective machines. This is preferable to encodings such as XDR, which require big-endian encoding on the wire and therefore penalize communication if both sender and receiver use little-endian machines.
CDR aligns primitive types on natural boundaries.
CDR aligns primitive data types on byte boundaries that are natural for most machine architectures. For example, short values are aligned on a 2-byte boundary, long values are aligned on a 4-byte boundary, and double values are aligned on an 8-byte boundary. Encoding data according to these alignments wastes some bandwidth because part of a CDR-encoded byte stream consists of padding bytes. However, despite the padding, CDR is more efficient than a more compact encoding because, in many cases, data can be marshaled and unmarshaled simply by pointing at a value that is stored in memory in its natural binary representation. This approach avoids expensive data copying during marshaling.
CDR-encoded data is not self-identifying.
CDR is a binary encoding that is not self-identifying. For example, if an operation requires two in parameters, a long followed by a double, the marshaled data consists of 16 bytes. The first 4 bytes contain the long value, the next 4 bytes are padding with undefined contents to maintain alignment, and the final 8 bytes contain the double value. The receiver simply sees 16 bytes of data and must know in advance that these 16 bytes contain a long followed by a double in order to correctly unmarshal the parameters.
This means that CDR encoding requires an agreement between sender and receiver about the types of data that are to be exchanged. This agreement is established by the IDL definitions that are used to define the interface between sender and receiver. The receiver has no way to prevent misinterpretation of data if the agreement is violated. For example, if the sender sends two double values instead of a long followed by a double, the receiver still gets 16 bytes of data but will silently misinterpret the first 4 bytes of the first double value as a long value.
CDR encoding is a compromise that favors efficiency. Because CDR supports both littleendian and big-endian representations and aligns data on natural boundaries, marshaling
530
IT-SC book: Advanced CORBA® Programming with C++
is both simple and efficient. The downside of CDR is that certain type mismatches cannot be detected at run time. In practice, this is rarely a problem because the stubs and skeletons generated by the C++ mapping make it impossible to send data of the wrong type. However, if you use the DII or DSI, you must take care not to send data of the wrong type as operation parameters because, at least in some cases, the type mismatch will go undetected at run time.
Other encodings do not suffer from this problem. For example, the Basic Encoding Rules (BER) used by ASN.1 use a Tag-Length-Value (TLV) encoding, which tags each primitive data item with both its type and its length. Such encodings provide better type safety at run time but are less efficient in both marshaling overhead and bandwidth. For this reason, most modern RPC mechanisms use encodings similar to CDR, in which data is not tagged with its type during transmission.
13.3.1 CDR Data Alignment
This section presents an overview of the CDR encoding rules. Again, we do not cover all of CDR here. Instead, we show the encoding of a few IDL types to illustrate the basic ideas.
Alignment for Primitive Fixed-Length Types
Each primitive type must start at a particular byte boundary relative to the start of the byte stream it appears in. The same requirements apply to both little-endian and bigendian machines. Table 13.1 shows the alignment requirements for fixed-length primitive types.
|
Table 13.1. CDR alignment of primitive fixed-length types. |
Alignment |
IDL Types |
1char, octet, boolean
2short, unsigned short
4 |
long, unsigned long, float, enumerated types |
8 |
long long, unsigned long long, double, long double |
1, 2, or 4 wchar (alignment depends on codeset) |
Encoding of Strings
Strings and wide strings are encoded as an unsigned long (aligned on a 4-byte offset) that indicates the length of the string, including its terminating NUL byte, followed by the bytes of the string, terminated by a NUL byte. For example, the string "Hello" occupies 10 bytes. The first 4 bytes are an unsigned long with value 6, the next 5 bytes contain the characters Hello, and the final byte contains an ASCII NUL byte. This means that an empty string occupies 5 bytes: 4 bytes containing a length of 1, followed by a single NUL byte.
Encoding of Structures
531
IT-SC book: Advanced CORBA® Programming with C++
Structures are encoded as a sequence of structure members in the order in which they are defined in IDL. Each structure member is aligned according to the rules in Table 13.1; padding bytes of undefined value are inserted to maintain alignment. Consider the following structure:
struct CD { char c; double d;
};
This structure contains a character, which can occur anywhere in a byte stream, followed by a double value, which must be aligned on an 8-byte boundary. Figure 13.1 shows how this structure would appear on the wire, assuming it starts at the beginning of a byte stream.
Figure 13.1 Structure of type CD encoded at the beginning of a byte stream.
Figure 13.1 indicates the offsets at which each value is encoded. The first byte of the stream, at offset 0, contains the value of the member c of the structure. This is followed by 7 padding bytes at offset 1 and, beginning at offset 8, the 8 bytes for the member d of the structure.
It is interesting to note that a structure of type CD does not always appear as a 16-byte value. Depending on the other data that precedes the structure on the wire, the length of the structure may vary. For example, consider the following operation, which accepts a string followed by a structure of type CD:
interface foo {
void op(in string s, in CD ds); };
When a client marshals a request to invoke op, it sends all the in parameters end-to-end according to CDR encoding rules. Assume for the moment that the parameters when sent inside the request begin at an 8-byte offset and that the client sends the string "Hello" as the value of the parameter s. Figure 13.2 shows the resulting encoding.
Figure 13.2 CDR encoding of the string "Hello" followed by a structure of type CD.
The encoding for the value "Hello" consumes 10 bytes: 4 bytes for the length and 6 bytes for the actual string. The second parameter is the structure of type CD. Because the
532