Chapter 16 CORBA Messaging
- Quality of Service (Policy Objects)
- Asynchronous Messaging Interface (AMI)
- Time Independent Invocations (TII)
- Further Reading
The CORBA Messaging specification encompasses three separate, but complementary, topics. Each of the topics are discussed in the following sections.
16.1 Quality of Service (Policy Objects)
The first version of CORBA did not define any portable way for programmers to specify what quality of service (QoS) they wanted in their applications. Instead, many CORBA vendors provided proprietary APIs for this purpose.
Over time, CORBA has matured to provide a mechanism for programmers to specify what QoS they want in their applications. In CORBA terminology, a QoS value is called a policy object. Policy objects were first introduced with the POA specification (Section 5.5). The POA specification initially defined 7 types of policy and separate operations for creating each of these 7 kinds of policy object. The OMG realized that the addition of new policies to CORBA would not be practical if each new policy required that a new operation be added to an existing interface because this would create a versioning nightmare. Instead, the Messaging specification (which was introduced after the POA specification) defined a more general-purpose mechanism for introducing new policies to CORBA. This mechanism involves the following:
- The base type CORBA::Policy is a local interface (Section 9.1) from which all policy types inherit.
- The ORB type contains an operation called create_policy() that can be used to create an instance of any of the subtypes of CORBA::Policy. This operation takes two parameters that specify the type and value of the policy object. For example, if the type is RELATIVE_RT_TIMEOUT_POLICY_TYPE (which is an integer constant that implies the RelativeRoundtripTimeoutPolicy subtype of CORBA::Policy) then the value expresses the desired timeout value. The type parameter is always a (typedef’d) integer constant, and there is a one-to-one mapping between these integer constants and subtypes of CORBA::Policy. However, the value’s type varies from one (sub)type of policy to another. Because of this, the value parameter is represented as an any (Section 15.3); this provides the flexibility for the embedded value to be an integer, a string or an arbitrary IDL data-structure. Internally, create_policy() uses its parameters to create an instance of the appropriate subtype of CORBA::Policy.
The programming steps required to create a policy object have the drawback of being verbose, but offer two benefits.
The first benefit is that this creation mechanism is general-purpose. This means that it can deal with more policy types (both CORBA-compliant and vendor-proprietary policy types) that might be introduced in the future.
The second benefit concerns the fact that the use of some policy types has the side-effect of embedding extra information in IORs (Chapter 10) or service contexts (Section 11.6). The fact that there is an integer constant corresponding to each subtype of CORBA::Policy means that this compact integer value can be embedded inside the IORs and service contexts. Because of this, the use of policy objects does not incur a significant bandwidth overhead.
Aside from providing a general-purpose mechanism for defining policy types, the Messaging specification also defines some specific policy types that can be used to control the QoS for making remote calls. These policies include the following:
- CORBA allows idle socket connections to be closed (to conserve network resources) and previously-closed connections to be re-established. The RebindPolicy type is used to specify whether or not the re-establishing of previously-closed connections should take place transparently to client applications.
- The signature of an IDL operation may be prefixed with the oneway keyword. The intention is to specify that the operation should be invoked asynchronously. However, early versions of CORBA did not define clear semantics of oneway calls and so the semantics provided by different CORBA vendors varied widely. The SyncScopePolicy type is now used to precisely specify the semantics of oneway invocations.
- There are two priority policies: one to specify the priority for delivering a request message, and another to specify the priority for delivering a reply message.
- There are various start-time policies that can be used to prevent a request/reply message from being delivered before a specified time. There are also various timeout policies that can be used to cancel the delivery of a request/reply message if it cannot be delivered by a specified time.
- There are various policies that are used in conjunction with request/reply routers (Section 16.3).
The Messaging specification also defines a service context (Section 11.6) that is used to propagate request- or reply-related policy values between clients and servers. It is important to note that if a client application specifies a timeout value for a remote request and the timeout occurs then the CORBA runtime system in the server can cancel the request only if the request is still queued up to be dispatched within the server; the server cannot cancel the request if it has already been dispatched to the target object.
16.2 Asynchronous Messaging Interface (AMI)
By default, IDL operations provide blocking call-and-reply semantics, that is, when a client application makes a remote call to an object in a server, the client blocks until the server sends back a reply. These semantics are suitable for many applications, but some applications can benefit from non-blocking call-and-reply semantics.
For a long time, developers had just two choices for obtaining non-blocking call-and-reply behavior:
- The client could create a new thread and get that thread to make a blocking call. In this way, the main thread of the client is not blocked. This approach cannot be used if the client application must be single-threaded. Also, even if a client can be multi-threaded, the overhead of thread creation means that this approach does not scale up to make a lot of non-blocking calls.
- The client could use the send_deferred() API of the DII (Section 15.4). However, the DII can be very tedious to use so programmers rarely wanted to use this approach. Also, with this approach, the client application had to periodically poll to check if the reply had come back. Polling can waste a lot of CPU time if it is done too frequently. On the other hand, infrequent polling can cause delays in processing replies.
CORBA has now matured to provide a different non-blocking call-and-reply mechanism, which is called Asynchronous Messaging Interface (AMI).
AMI is an optional part of CORBA and not all CORBA products support it. Also, AMI requires that additional operations be generated into proxy classes by the IDL compiler. Unfortunately, doing this would break binary portability of Java CORBA products. For this reason, no Java CORBA product can support AMI—at least, not until the IDL-to-Java mapping is updated to support AMI.1 For these reasons, if you are considering using AMI in a project then it is vitally important to check that the CORBA product you are using supports AMI.
If a CORBA product does support AMI then it works as follows:
- For each IDL operation, foo(), the IDL compiler generates: (1) a blocking operation called foo(), (2) a non-blocking operation called sendc_foo(), and (3) another non-blocking operation called sendp_foo(). The first operation is generated by all IDL compilers; the other two generated operations are new to AMI. Note that AMI support in an IDL compiler might be disabled by default, so you might have to pass a command-line option to the IDL compiler to instruct it to generate the AMI operations. The documentation provided by your CORBA vendor should mention which command-line option to use.
- The sendc version of the operation takes in and inout parameters. It also takes a callback object (Section 18.104.22.168) as a parameter. An operation on the callback object will be invoked later with the returned out and inout parameters and return value. It is up to the client developer to implement the callback object as a servant (Section 5.2) and activate it into a POA (Section 5.5). It is important to note that the callback object is a “normal” CORBA object, so it could actually be located in another CORBA application. In effect, one application can send requests to a server and the reply can be processed by either the same client or another application.2
- The sendp version of the operation takes in and inout parameters. It returns a poller object. The client can periodically invoke upon this returned poller object to determine if the reply has arrived and, if so, find out the values of the returned out and inout parameters and return value. The implementation of the poller object is generated by the IDL compiler.
It is important to note that both the callback and polling communication mechanisms are implemented entirely within the client-side ORB runtime system—a server application is unaware of, and is unaffected by, how a client has made a request.
It is also important to note that of the CORBA implementations that support AMI, most support the callback model but do not support the polling model. This is because most developers view the callback model as being superior to the polling model and so CORBA implementors have little motivation to implement the polling model.
16.3 Time Independent Invocations (TII)
GIOP (Chapter 11) is a synchronous protocol. This means that the asynchronous messaging infrastructure of CORBA must be layered on top of GIOP. For example, the callback and polling models (Section 16.2) are layered on top of GIOP by the CORBA runtime system in client applications. Another commonly desired aspect of a messaging system is time independent invocations (TII), which can be explained as follows. Let us assume that a client application sends a request to a server but then the client is killed before it receives the reply. TII means that the reply message is kept in some form of persistent storage until the client application is restarted and then the reply message is delivered to the client.
The CORBA Messaging specification provides TII by introducing another piece of infrastructure called message routers. A message router is a delegation server, that is, a server that receives an incoming message and delegates it (passes it on) to another application.
An IOR (Chapter 10) contains the contact details of an object. However, an IOR can optionally have a TaggedComponent (Section 10.2.3) that contains a sequence of embedded router IORs. The sequence of router IORs reflects the chain of delegation, from the first router to the next router and so on to the eventual target server. If a client uses such an IOR but the client does not have the relevant routing policies set then requests are sent in the normal manner, that is, direct to the object in the server. However, if the client does have the relevant routing policies set then the CORBA runtime system in the client uses a different mechanism to send requests: the client sends the request to the first router, and the IOR of a callback object is passed along with the request. Each router delegates the request message to the next router. Eventually, the last router sends the request to the target server. As far as the target server is concerned, the last router is its client, and so the server sends its reply message to this last router. The routers then pass the reply message back along the chain of routers. The router closest to the client then delivers the reply message by invoking the appropriate operation on the callback object provided by the originating client.
The “added value” provided by the routers is that each router can store request/reply messages in a persistent store, such as a database. If any process—the client, one of the routers or a server—dies during the delegation of request/reply messages then the persistent storage of the messages means that the delegation can continue when the dead process is restarted.
Several policies (Section 16.1) can be used to control the QoS of message delivery:
- The RoutingPolicy type controls whether or not requests to IORs with an embedded routing TaggedComponent are sent “normally” or via the routers. The ROUTER_NONE value means that the requests are sent directly to the target IOR. The ROUTE_FORWARD value means that the request is delivered via routers. The ROUTE_STORE_AND_FORWARD value means that the routers store messages persistently, in order to provide TII.
- The MaxHopsPolicy type is used to specify an upper limit on the number of “hops” (delegations from one router to another) that can occur when delivering a message.
- The QueueOrderPolicy type is used to tell routers in which order they should delegate messages. For example, they can delegate messages in first-come, first-served (FIFO) order, in order of a priority associated with each message, or in the “deadline” order (of when messages will timeout).
16.4 Further Reading
- There is one exception to this. The Java-based JacORB product has added experimental support for AMI. The experience gained by the JacORB development team in doing this is likely to be fed back into the OMG standardization process for enhancing the IDL-to-Java mapping in order to support AMI.
- As will be mentioned soon, AMI is implemented entirely within the CORBA runtime system of the client application. Because of this, the reply message will always be sent from the server to the CORBA runtime system of the client from which the request originated; it is the CORBA runtime system in the client that then invokes the reply on the callback object, which can be in the same client process or in another application.