VisiBroker for C++ Developer’s Guide : Using VisiBroker Native Messaging

Using VisiBroker Native Messaging
Introduction
Native Messaging is a language independent, portable, interoperable, server side transparent, and OMG compliant two-phase invocation framework for CORBA and RMI/J2EE (RMI-over-IIOP) applications.
Two-phase invocation (2PI)
In object-oriented vocabulary, invocations are method calls made on target objects. Conceptually, an invocation is made up of two communication phases:
In classic object-oriented distributed frameworks, such as CORBA, RMI/J2EE, and .NET, invocations on objects are one-phased (1PI), in which the sending and receiving phases are encapsulated together inside a single operation rather than exposed individually. In a one-phased invocation the client calling thread blocks on the operation after the first phase until the second phase completes or aborts.
If a client can be unblocked after the first phase, and the second phase can be carried out separately, the invocation is called two-phased (2PI). The operation unblocking before completing its two invocation phases is called a premature return (PR) in Native Messaging.
A 2PI allows a client application to unblock immediately after the request sending phase. Consequently, the client does not have to halt its calling thread and retain the transport connection while waiting for a reply. The reply can be retrieved or received by the client from an independent client execution context and/or through a different transport connection.
Polling-Pulling and Callback models
In a two-phase invocation scenario, after sending out each request the client application can either actively poll and pull the reply using a poller object provided by the infrastructure, or the client can passively wait for the infrastructure to notify it and send back the reply on a specified asynchronous callback handler. These two scenarios are usually called the synchronous polling-pulling model and the asynchronous callback model respectively.
Non-native messaging and IDL mangling
In non-native messaging, such as CORBA Messaging, two-phase invocations are not made with native operation signatures on native IDL or RMI interfaces. Instead, at different invocation phases, and with different reply retrieve models, client applications have to call various mangled operations.
For instance, in CORBA Messaging, to make a two-phase invocation of operation foo (<parameter_list>) on a target, the request sending is not made with the native signature foo() itself, but it is made with either of the following mangled signatures:
// in polling-pulling model
sendp_foo(<input_parameter_list>);
// in callback model
sendc_foo(<callback_handler>, <input_parameter_list>);
The reply polling operation signature is:
foo(<timeout>, <return_and_output_parameter_list_as_output>);
The reply delivery callback operation signature is:
foo(<return_and_output_parameter_list_reversed_as_input>);
These mangled operations are either additional signatures added to the original application specified interface, or defined in additional type specific interfaces or valuetypes.
Problems of this non-native and mangling approach are:
Native Messaging solution
Native Messaging only uses native IDL language mapping and native RMI interfaces defined by applications, without any interface mangling and without introducing any additional application specific interface or valuetype.
For instance, in Native Messaging, sending a request to foo(<parameter_list>) and retrieving (or receiving) its reply in either the polling-pulling or callback models are made with the exact native operation foo(<parameter_list>) itself and are made on native IDL or RMI interfaces. No mangled operation signature and interfaces or valuetypes are introduced or used.
This pure native and non-mangling approach is not only elegant and intuitive but completely eliminates conflicts, name collision, and inconsistencies of operation signature mangling.
Request Agent
Similar to the OMG Security and Transaction Services, Native Messaging is an object service level solution, which is based on an fully interoperable broker server, the Request Agent, and a client side portable request interceptor fully compliant with the OMG Portable Interceptor specification.
When making two-phase invocations, Native Messaging applications do not send requests directly to their target objects. Instead, request invocations are made on delegate request proxies created on a specified Request Agent. The request proxy is responsible for delegating invocations to their specified target objects, and delivering replies to client callback handlers or returning them later on client polling-pulling.
Therefore, a request agent needs to be known by client applications. Usually, this is accomplished by initializing the client ORB using OMG standardized ORB initialization command arguments:
-ORBInitRef RequestAgent=<request_agent_ior_or_url>
This command allows client applications to resolve the request agent reference from this ORB as an initial service, for instance:
// Getting Request Agent reference in C++
CORBA::Object_var ref
= orb->resolve_initial_references("RequestAgent");
NativeMessaging::RequestAgentEx_var agent
= NativeMessaging::RequestAgentEx::_narrow(ref);
By default, the URL of a request agent is:
corbaloc::<host>:<port>/RequestAgent
Here, <host> is the host name or dotted IP address of a RequestAgent server, and <port> is the TCP listener port number of this server. By default, NativeMessaging RequestAgent uses port 5555.
Native Messaging Current
Similar to the OMG Security and Transaction Services, Native Messaging uses a thread local Current object to provide and access additional supplemental parameters for making two-phase invocations. These parameters include blocking timeout, request tag, cookie, poller reference, reply availability flag, and others. Semantic definitions and usage descriptions of these parameters are given in later sections. Similarly, the Native Messaging Current object reference can be resolved from an ORB as an initial service, for instance:
// Getting Current object reference in C++
CORBA::Object_var ref
= orb->resolve_initial_references("NativeMessagingCurrent");
NativeMessaging::Current_var current
= NativeMessaging::Current::_narrow(ref);
Core operations
A two-phase framework allows all normal invocations to be carried out in two separate phases manageable by client applications. Nevertheless, on fulfilling or using this two-phase invocation service, the framework and/or client may need some other primitive core functions from the framework. Operations used to access primitive core functions are called core operations. It is desirable that:
In Native Messaging, all pseudo operations are reserved as core operations.
Note
In this document, if not explicitly stated, “invocation” or “operation” implies a non-core two way operation.
StockManager example
The StockManager example is used in this section to illustrate the Native Messaging usage scenarios. This example is abridged from the full scale version that is shipped with the product in the <install_dir>/examples/vbe/NativeMessaging/stock_manager directory, and it is provided to illustrate functionality that is equivalent to the CORBA Messaging StockManager example.
The following example assumes a server object has its IDL interface, StockManager, defined as follows:
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/StockManager.idl
interface StockManager {
boolean add_stock(in string symbol, in float price);
boolean find_closest_symbol(inout string symbol);
};
A conventional single-phase add_stock() or find_closest_symbol() call adds a stock symbol to or finds a symbol in the targeted stock manager server. The following is an example of the invocation code:
// invoke and block until return
CORBA::Boolean stock_added
= stock_manager->add_stock("ACME", 100.5);
CORBA::String_var symbol = (const char*)"ACMA";
CORBA::Boolean closest_found
= stock_manager->find_closest_symbol(symbol.inout());
In the above one-phase invocation case, the invocations are blocked until the client receives its returns or exceptions.
Using Native Messaging, two-phase invocations can be made on the same stock manager server. Replies to these invocations can be retrieved or returned using the synchronous polling-pulling model or the asynchronous callback model, as illustrated in the following sections, “Polling-pulling model”, and “Callback model”.
Note
This document illustrates the StockManager example code in C++. The corresponding Java code is available in the “Using VisiBroker Native Messaging” chapter of the VisiBroker for Java Developer's Guide.
Polling-pulling model
In the polling-pulling model, the result of a two-phase invocation is pulled back by client applications. The steps for Native Messaging polling-pulling two-phase invocations are summarized below.
1
2
Get the typed receiver or <I> interface of this proxy. This typed receiver is used by the client application to send requests to the proxy. The typed receiver of a proxy supports the same IDL interface as the target object. In this example, the typed receiver supports the StockManager interface and can be narrowed down to a typed StockManager stub.
3
Perform the first invocation phase, making several invocations on the typed receiver stub. By default, invocations on a typed receiver are returned with dummy output and return values. This is called a premature return. Receiving a premature return from proxy's typed receiver without raising an exception indicates that a two-phase invocation has been successfully initiated. It indicates that the request has been accepted and assigned to a distinct poller object by the request agent. The poller object of a two-phase invocation is available from the local NativeMessaging Current. Like the typed receiver, all poller objects also support the same IDL interface as the target (in this example the StockManager).
4
The following code sample illustrates how to use Native Messaging to make polling-pulling two-phase invocations on a stock manager object:
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/polling_client_main.C

// 1. create a request proxy from the request agent for making
// non-blocking requests on targeted stock_manager server.
NativeMessaging::RequestProxy_var proxy = agent->
create_request_proxy(
stock_manager, "",
NULL, NativeMessaging::PropertySeq(0));

// 2. Get the request (typed) receiver of the proxy
CORBA::Object_var ref;
StockManager_var stock_manager_rcv = StockManager::_narrow(ref
        = proxy->the_receiver());

// 3. send several requests to the typed receiver, and
// get their reply pollers from Native Messaging Current.
StockManager_var pollers[2];
stock_manager_rcv->add_stock("ACME", 100.5);
pollers[0] = StockManager::_narrow(ref = current->the_poller());
CORBA::String_var symbol = (const char*)"ACMA";
stock_manager_rcv->find_closest_symbol(symbol.inout());
pollers[1] = StockManager::_narrow(ref = current->the_poller());

// 4. Poll/pull the two associated replies
current->wait_timeout(max_timeout);

CORBA::Boolean stock_added;
do { stock_added = pollers[0]->add_stock("", 0.0); }
while(current->reply_not_available());

CORBA::Boolean closest_found;
do { closest_found = pollers[1]->find_closest_symbol(symbol.inout()); }
while(current->reply_not_available());
Note
If there is an exception in polling-pulling phase, the application should use the Current reply_not_available attribute to determine whether the exception is the result of a reply polling-pulling failure, or the successful pulling of a real exceptional result of the delegated request. TRUE indicates that the exception is a polling-pulling failure between the client and agent. FALSE indicates that the exception is the real result of the delegated request.
Additional features, variances of the polling-pulling model, and Native Messaging API syntax and semantics specification are discussed in “Advanced Topics” and “Native Messaging API Specification”.
Callback model
Using the Native Messaging callback model, applications are unblocked immediately after they send out requests to a proxy's typed receiver. Replies to these invocations are delivered to a callback reply recipient that is specified upon creating the request proxy.
The steps to make Native Messaging two-phase invocations in the callback model are summarized below:
1
2
Like the second step in the polling-pulling model, get the typed receiver, or <I> interface, of this proxy and narrow it down to a typed <I> stub (a StockManager stub in this example).
3
4
The following code sample illustrates the first three steps for using Native Messaging to make callback model two-phase invocations on a stock manager object:
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/callback_client_main.C

// get type independent callback handler reference
NativeMessaging::ReplyRecipient reply_recipient = …;

// 1. create a request proxy from the request agent for
// making asynchronous requests on targeted stock_manager server.
NativeMessaging::RequestProxy_var proxy = agent->
create_request_proxy(
stock_manager, "", reply_recipient,
NativeMessaging::PropertySeq(0));

// 2. Get the request (typed) receiver of the proxy
StockManager_var stock_manager_rcv
= StockManager::_narrow(obj = proxy->the_receiver());

// 3. send two requests to the receiver
stock_manager_rcv->add_stock("ACME", 100.5);
CORBA::String_var symbol = (const char*)"ACMA";
stock_manager_rcv->find_closest_symbol(symbol.inout());
Here, the reply_recipient callback handler is a NativeMessaging::ReplyRecipient object regardless the specific application target types. The ReplyRecipient interface is defined as
// from: <install_dir>/idl/NativeMessaging.idl

interface NativeMessaging::ReplyRecipient {
void reply_available(
in object reply_holder,
in string operation,
in sequence<octet> the_cookie);
);
};
The reply_holder parameter of reply_available() is called a reflective callback reference, which is the same as a reply poller object of the polling-pulling model and can be used by the reply_available() implementation to pull back the reply result in the same way a polling-pulling client would pull back a reply result from a poller object.
Note
In delivering replies to a callback handler, Native Messaging uses the double dispatch pattern to reverse the callback model into a polling-pulling model. Here, a reply recipient implementation makes a second (reflective) callback on a typed reply_holder reference to retrieve the reply.
The following code sample is an example implementation of reply_available() method:
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/AsyncStockRecipient.C

void StockManagerReplyRecipientImpl::reply_available(
CORBA::Object_ptr reply_holder,
const char* operation,
const CORBA::OctetSequence& cookie)
{
StockManager_var poller
= StockManager::_narrow(reply_poller);

// retrieve response using reflective callback
if( strcmp(operation, "add_stock") == 0 ) {
// retrieve a add_stock() return.
CORBA::Boolean stock_added = poller->add_stock("", 0.0);
...
}
else
if( strcmp(operation, "find_closest_symbol") == 0 ) {
CORBA::String_var symbol = (const char*)"";
// retrieve a find_closest_symbol() return
CORBA::Boolean closest_found
   = poller->find_closest_symbol(symbol.inout());
...
}
...
}
Note
If an exception is raised when the reply_available() implementation retrieves a reply from the reply_holder, the application should use the Current reply_not_available attribute to determine whether the exception reports retrieving a failure or a successful reply retrieval of a real exceptional result of the delegated request. TRUE indicates that this exception is the result of a reply retrieval failure between the client and agent. FALSE indicates that this exception is a real result of delegated request.
Reply retrieval operations on reply_holder should only be made within the scope of the reply_available() method. Once the application returns from reply_available(), the reply_holder may no longer be valid.
Additional features, variances of the polling-pulling model, and the Native Messaging API specification are discussed in “Advanced Topics” and “Native Messaging API Specification”.
Advanced Topics
Group polling
As illustrated in previous sections, multiple requests can be delegated by a given request proxy. However, as different requests take different processing time, replies from them are not necessarily ready in the order in which they were invoked. Instead of polling individual requests one by one, group polling allows a polling client application, which has multiple requests delegated by a given request proxy, to determine the availability of replies in an multiplexed aggregation.
In order to participate in group polling, a request sent to a given proxy needs to be tagged. Request tags are assigned by clients to identify requests in the scope of their group, namely the request proxy. Native Messaging does not impose any constraint on request tag content, except that they must be unique within the scope (request proxy). Untagged requests (requests with empty tags) do not participate in group polling, and the availability of their replies is not reported by group polling results.
The steps for using group polling are summarized below.
1
Send tagged requests. To tag a request, a client application simply sets the request_tag attribute of the local Native Messaging Current object before making each invocation on the typed receiver interface (before delivering each request). The content of each request tag is specified by application for its own convenience, as long as it is unique within its scope (proxy).
2
Poll reply availability on the request proxy, instead of on any individual poller, by calling the proxy's poll(max_timeout, unmask) operation. This operation will block until timeout, or until any tagged requests delegated by this proxy are ready for mature return, at which time their tags will be put in the returned request tag sequence. An empty tag sequence return indicates a timeout has expired.
3
The following code sample illustrates above steps of using Native Messaging group polling feature:
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/group_polling_client.C

// send one tagged request
current->
request_tag(NativeMessaging::RequestTag(2,2, (CORBA::Octet*)"0"));
stock_manager_rcv->add_stock("ACME", 100.5);
pollers[0] = StockManager::_narrow(ref = current->the_poller());

// send another tagged request
current->request_tag(NativeMessaging::RequestTag(2,2, (CORBA::Octet*)"1"));
CORBA::String_var symbol = (const char*)"ACMA";
stock_manager_rcv->find_closest_symbol(symbol.inout());
pollers[1] = StockManager::_narrow(ref = current->the_poller());

...

// polling request availability on proxy and retrieve their replies
NativeMessaging::RequestTagSeq_var tags;
while(TRUE) {
// polling availability
try {
tags = proxy->poll(max_timeout, TRUE);
}
catch(NativeMessaging::RequestAgent::PollingGroupIsEmpty&) {
proxy->destroy(TRUE);
break;
}

// retrieve replies
for(int i=0;i<tags->length();i++) {
int id = atoi((const char*)((tags.in())[i].get_buffer()));

switch(id) {
case 0: // the first tagged request sent above
CORBA::Boolean stock_added;
stock_added = pollers[0]->add_stock("", 0.0);
break;

case 1: // the second tagged request sent above
CORBA::Boolean closest_found;
closest_found = pollers[1]->find_closest_symbol
                            (symbol.inout());
break;

default:
break;
}
}
}
Note
After each invocation, the Current request_tag attribute is automatically reset to empty or null.
Try to initiate a 2PI on a proxy with a request_tag already used by another 2PI or the proxy will end up with a CORBA BAD_INV_ORDER exception with minor code NativeMessaging::DUPLICATED_REQUEST_TAG.
The unmask parameter of the poll() operation on a request proxy specifies whether the poll() should unmask all mature requests. If they are unmasked, they will not be involved and reported by the next poll().
If all requests on a proxy are not tagged or unmasked, poll() will raise a PollingGroupIsEmpty exception.
Cookie and reply de-multiplexing in reply recipients
As illustrated in previous sections, multiple requests can be delegated by a given request proxy. In the callback model, all replies to these requests will be sent back to the same reply recipient object specified on creating the proxy. The challenge is how the client demultiplexes different replies on one ReplyRecipient callback handler.
Applications using OMG CORBA Messaging also face the same challenge. To avoid activating many callback objects, CORBA Messaging suggests that applications use a POA default servant or servant manager to manipulate callback objects, and assign different object IDs to different callback references. Although this avoids many callback objects being activated in the reply recipient process, it is inflexible and far from an efficient scenario, because it requires an object reference to be created and marshaled for sending each callback request.
Native Messaging supports two demultiplexer mechanisms, which can be used either together or alone depending on the required demultiplexer granularity. A coarse grained demultiplex, but handy mechanism, is simply demultiplexing by operation signature, which is available within the ReplyRecipient's reply_available() callback method. This is the mechanism used in some of the previous examples.
A more effective demultiplexing mechanism in the Native Messaging callback scenario is using request cookies. A request cookie is an octet sequence (or byte array). Its content is specified by client applications on the Native Messaging's Current object before sending a request. The specified cookie is passed to the reply recipient's reply_available() method on delivering the reply of that request. There is no constraint on the content of a cookie, not even a uniqueness requirement. Contents of cookies are decided solely by applications for their own convenience and efficiency on callback demultiplexing.
The following code sample illustrates how to assign cookie to a request:
// send a requests with a cookie
current->
the_cookie(CORBA::OctetSeq(9,9,"add stock"));
stock_manager_rcv->add_stock("ACME", 100.5);

// send another request with a different cookie
current->the_cookie(CORBA::OctetSeq(11,11,"find symbol"));
CORBA::String_var symbol = (const char*)"ACMA";
stock_manager_rcv.find_closest_symbol(symbol.inout());
The following code sample illustrates how to use attach cookies to demultiplex by reply recipient:
void StockManagerReplyRecipientImpl::reply_available(
CORBA::Object_ptr reply_poller,
const char* operation,
const CORBA::OctetSequence& cookie)
{
StockManager_var poller
= StockManager::_narrow(reply_poller);

CORBA::String_var id = PortableServer::
ObjectId_to_string(cookie.get_buffer());

// retrieve response using reflective callback
if( strcmp(id, "add stock") == 0 ) {
CORBA::Boolean stock_added
= poller->add_stock("", 0.0);

...
}
else
if( strcmp(id, "find symbol") == 0 ) {
CORBA::String_var symbol = (const char*)"";
CORBA::Boolean closest_found
= poller->find_closest_symbol(symbol.inout());
...
}
...
}
Evolving invocations into two-phases
Compared to conventional single-phase invocations, two-phase invocations incur additional reply polling communication round trips. For a long duration heavyweight task, latency from few additional communication round trips is insignificant. However, for a lightweight transient invocation, this latency can be undesirable.
It is ideal for applications if lightweight transient invocations can be completed in a single-phase without incurring additional latency, and heavyweight long duration invocations can automatically be performed in two separated phases without holding client execution context and transport connection.
In Native Messaging, this can be achieved with the evolve into two-phase invocation feature. By default, invocations on a proxy's typed receiver always end up with premature returns along with their reply results to be polled back or delivered through callbacks later in a separate invocation phase. The evolve into two-phase feature allows invocations on a proxy's typed receiver to block and end up with a mature return if it can be accomplished before a specified timeout expires. Otherwise, if the invocation cannot complete before the timeout expires, it will evolve into a two-phase invocation by taking a premature return. To determine whether an invocation on a proxy's typed receiver has evolved into a two-phase invocation, the application can examine the reply_not_available attribute of the local Native Messaging Current object after the return.
To use this feature:
The request proxy should be created with a WaitReply property with a value of TRUE.
Set the wait_timeout attribute of Native Messaging Current to a non-zero value (milliseconds) before the invocations.
After each invocation on the typed receiver, determine whether a return is premature by examining the reply_not_available attribute of the local Native Messaging Current object after each invocation.
The following code sample illustrates how to use the evolve invocations into two-phases:
// Create a request proxy with WaitReply property TRUE
NativeMessaging::PropertySeq props;
props.length(1);
props[0].id = (const char*)"WaitReply";
CORBA::Any::from_boolean fb((CORBA::Boolean)1);
props[0].value <<= fb;

NativeMessaging::RequestProxy_var proxy
= agent->
create_request_proxy(stock_manager, "", NULL, props);

// get the typed receiver of this proxy
CORBA::Object_var ref;
StockManager_var stock_manager_rcv
= StockManager::_narrow(ref = proxy->the_receiver());

// Set wait_timeout attribute to 3 seconds
current->wait_timeout(3000);
// make an invocation on the receiver.
CORBA::Boolean stock_added = stock_manager_rcv->add_stock("ACME", 100.5);

// check whether it has evolved into a two-phase invocation.
if( ! current->reply_not_available() ) {
// It is not evolved. The return above is mature.
// The job has done.
return;
}
// It has evolved into a two-phase invocation.
// We should get the poller and poll its reply.
StockManager_var poller = StockManager::_narrow(ref =
         current->the_poller());
do { stock_added = pollers->add_stock("", 0.0); }
while(current->reply_not_available())
Note
If an exception is raised from blocking on a proxy or polling reply, the application should use the reply_not_available attribute of Native Messaging Current to determine whether the exception reports a request delivering or reply polling failure or if it is a real result of delegating the request. A value of TRUE for this attribute indicates that this exception is a reply delivering or polling failure between the client and agent. FALSE indicates that this exception is a real result of delegating the request.
Reply dropping
In the callback model, by default, a request agent sends whatever result, return or exception, of the invocation back to the reply recipient. Reply dropping allows specified types of reply results to be filtered out. This is useful, for instance, if applications want to invoke one-way requests with no result to be returned, but would still be notified if any invocations fail.
Native Messaging allows applications to specify a ReplyDropping property on creating a request proxy. This property specifies which types of returns should be filtered out from being sent to the reply recipient. The value of this property is an octet (or byte) with the following filtering rules:
For example, a value of 0x06 for this property lets the request agent drop all exceptions, system as well as user, on requests delegated by this proxy.
The following example code illustrates setting the ReplyDropping property:
// Create a request proxy with ReplyDropping property
// with value 0x01 (dropping all normal replies).
NativeMessaging::PropertySeq props;
props.length(1);
props[0].id = (const char*)"ReplyDropping";
CORBA::Any::from_octet fo((CORBA::Octet)0x01);
props[0].value <<= fo;

NativeMessaging::RequestProxy_var proxy
= agent->
create_request_proxy(stock_manager, "",
reply_recipient, props);

...
Note
Reply dropping only applies to the callback model. If the reply_recipient reference passed to the create_request_proxy() is null, the reply dropping property is ignored.
If the value of the reply dropping property in create_request_proxy() is not 0x00, and the reply_recipient reference is not null, invocation on this proxy's typed receiver will not return a poller object on Native Messaging Current.
Manual trash collection
By default, a poller object will be trashed immediately after a polling operation on it results in a mature return. In the callback model, once the callback is returned, a request agent also trashes the poller regardless of whether the application has retrieved the reply within the callback reply_available() operation. Polling on a trashed object raises a CORBA OBJECT_NOT_EXIST exception and the Current reply_not_available attribute is set to TRUE.
If a request proxy is created with a RequestManualTrash property of value TRUE, poller objects of requests delegated by this proxy are not trashed automatically. Polling on these poller objects after a reply becomes available is idempotent, returning the same result every time.
These poller objects can be manually trashed if an application no longer needs them. To manually trash poller objects, applications simply call the destroy_request() operation on the request agent, with the poller to be trashed as a parameter. For example,
agent->destroy_request(poller);
Note
Pollers of requests delegated by an auto-trashing proxy can also be trashed manually. This makes sense when replies on these pollers are either not yet available or have not been polled back.
Unsuppressed premature return mode
The key concept of Native Messaging is unblocking from a native operation after its first invocation phase. In Native Messaging, this is called premature return. There are two premature return modes in Native Messaging: suppressed mode and unsuppressed mode. All of the discussions so far used the default suppressed mode. In suppressed mode, the premature return is a normal operation return, except that it contains dummy output and return values. This is similar to an exceptional return in non-exception handling in the OMG C++ mapping, except that Native Messaging uses a thread local Current object instead of an additional Environment parameter.
Suppressed premature return mode is handy, however, it requires client-side mapping support. Namely, it assumes the IDL precompiler generated client-side stub code catches and suppresses premature return exceptions. To port client applications to an ORB, its IDL precompiler does not generate premature return suppressed client-side stub code, the unsuppressed premature return mode can be used.
In Native Messaging unsuppressed premature return mode, a native operation is unblocked by simply raising an RNA exception, that is a CORBA NO_RESPONSE exception with minor code REPLY_NOT_AVAILABLE. To use unsuppressed premature return mode, an application needs to turn off suppressed mode by calling suppress_mode(false) on Native Messaging Current, and it needs to catch and handle the RNA exceptions accordingly.
Note
To ensure that the code is portable to both suppressed and unsuppressed modes, it is recommended that applications use the Current reply_not_available attribute in unsuppressed mode, rather than the RNA exception and minor code to determine the maturity of a return.
The following example code illustrates the StockManager polling example in unsuppressed mode. This code is not only portable to all ORBs, but also portable to suppressed mode as well.
// from: <install_dir>/examples/vbe/NativeMessaging/
// stock_manager/polling_client_portable.C

static void yield_non_rna(const CORBA::NO_RESPONSE& e)
{
if(e.minor() != NativeMessaging::REPLY_NOT_AVAILABLE ) {
throw e;
}
}

...

// This marco suppresses an RNA, namely a NO_RESPONSE exception
// with minor code of NativeMessaging::REPLY_NOT_AVAILABLE.
#define
SUPPRESS_RNA(stmt) \
try { stmt; } \
catch(const CORBA::NO_RESPONSE& e) { yield_non_rna(e); }

...

// turn off suppress mode
current->suppress_mode(FALSE);

// send several requests to the typed receiver, and
// get their reply pollers from Native Messaging Current.
StockManager_var pollers[2];
SUPPRESS_RNA( stock_manager_rcv->add_stock("ACME", 100.5) )
pollers[0] = StockManager::_narrow(ref = current->the_poller());

CORBA::String_var symbol = (const char*)"ACMA";
SUPPRESS_RNA(stock_manager_rcv->find_closest_symbol(symbol.inout()))
pollers[1] = StockManager::_narrow(ref = current->the_poller());

// poll the associated replies
current->wait_timeout(max_timeout);

CORBA::Boolean stock_added;
do { SUPPRESS_RNA(stock_added = pollers[0]->add_stock("", 0.0)) }
while(current->reply_not_available());

CORBA::Boolean closest_found;
do { SUPPRESS_RNA(closest_found
= pollers[1]->find_closest_symbol(symbol.inout())) }
while(current->reply_not_available());
Suppress poller generation in callback model
By default, pollers are generated even in the callback model. This allows:
However, generating and sending back poller references incurs additional overhead. Native Messaging allows applications to suppress (disable) poller reference generation in the callback model.
To suppress a poller in the callback model, applications only need to create a request proxy with the CallbackOnly property set to TRUE. In this case null pollers are returned.
Native Messaging API Specification
Note
Several operations and attributes in the Native Messaging IDL definition are not specified in this document. They are either value added features, depreciated features, or reserved for further extension.
Interface RequestAgentEx
This is the interface of the Native Messaging Request Agent. A request agent is responsible for delegating invocations to their specified target object and delivering return results to client callback handlers or returning them later on client polling. See “Request Agent” for more information.
create_request_proxy()
RequestProxy
create_request_proxy(
in object target,
in string repository_id,
in ReplyRecipient reply_recipient,
in PropertySeq properties)
raises(InvalidProperty);
The create_request_proxy() method creates a request proxy to delegate two-phase invocations to the specified target object.
The reply recipient callback handler. When replies become available the request agent calls back its reply_available() operation to send back reply results. A null_reply_recipient implies the polling-pulling model.
WaitReply: A boolean property with default value FALSE. See Evolving invocations into two-phases for more information.
RequestManualTrash: A boolean property with default value FALSE. See “Manual trash collection” for more information.
ReplyDropping: An octet property with default value 0x00. See “Reply dropping” for more information.
CallbackOnly: A boolean property with default value FALSE. See “Suppress poller generation in callback model” for more information.
destroy_request()
void
destroy_request(
in object poller)
raises(RequestNotExist);
This method is used to manually trash a poller object. See “Manual trash collection” for more information.
Interface RequestProxy
Request proxies are created by an application from a request agent in order to delegate requests to the specified target and with the specified semantic properties. See “create_request_proxy()”.
the_receiver
readonly attribute object the_receiver;
This attribute is the proxy's typed receiver reference. The type receiver of a proxy supports the same IDL interface as the specified target and is where applications send their requests to be delegated by the proxy.
Note
If the proxy is created with a WaitReply property value of TRUE and the request on the_receiver is called with a non-zero wait_timeout, the request agent will try to delegate the request as single-phase invocation before the timeout expires. If the agent does not receive a reply from the target before the timeout expires, it will unblock the client and the request will evolve into a two-phase invocation. After unblocking from a call on the_receiver, applications can use the Current reply_not_available attribute to determine whether the request has evolved into a two-phase invocation. See “reply_not_available”.
poll()
RequestIdSeq
poll(
in unsigned long timeout,
in boolean unmask)
raises(PollingGroupIsEmpty);
This method performs group polling. See “Group polling” for more information.
destroy()
void
destroy (
in boolean destroy_requests);
This method destroys a request proxy.
destroy_
requests
if TRUE, all requests delegated by this proxy are trashed.
Local interface Current
A local Native Messaging Current object is used by an application to specify and access additional information before and after a two-phase invocation. The Current object can be resolved from the local ORB as an initial reference. See “Native Messaging Current” for more information.
suppress_mode()
void
suppress_mode(
in boolean mode);
This sets the current premature return mode. In suppressed mode, two-phase invocations are unblocked after the first phase in a normal return, except that it contains dummy output and return values. In unsuppressed mode, two-phase invocations are unblocked after the first phase by an RNA exception (a CORBA NO_RESPONSE exception with minor code of NativeMessaging::REPLY_NOT_AVAILABLE). See “Unsuppressed premature return mode” for more information.
wait_timeout
attribute unsigned long wait_timeout;
This attribute specifies the maximum number of milliseconds a two-phase invocation will block on sending a request or on polling a reply. On timeout, Native Messaging unblocks the call with a premature return.
the_cookie
attribute Cookie the_cookie;
This attribute specifies the cookie to be sent immediately following the invocation on a proxy's typed receiver. By default, the cookie is empty. A non-empty cookie can be used by reply_recipient to do more application-specific demultiplexing. See “Cookie and reply de-multiplexing in reply recipients” for more information.
request_tag
attribute RequestTag request_tag;
This attribute uniquely identifies the request immediately following an invocation on a proxy's typed receiver. By default the tag is initially empty, and it is reset to empty after sending the request. Requests with non-empty tags are involved in group polling. See “poll()” and “Group polling”.
Note
After each invocation, the Current request_tag attribute is automatically reset to empty or null.
Attempting to initiate a 2PI on a proxy with a request_tag previously used by another 2PI on the proxy will result in a CORBA BAD_INV_ORDER exception with minor code NativeMessaging::DUPLICATED_REQUEST_TAG.
the_poller
readonly attribute object the_poller;
This attribute returns the poller object reference just after delivering a request through an invocation made on a proxy's typed receiver. Poller objects are used by client applications to fulfill the reply polling-pulling phase of two-phase invocations.
Note
If an exception is raised in the reply polling-pulling phase, an application should use the Current reply_not_available attribute to determine whether the exception reports a reply polling-pulling failure or a successful reply pulling of a real exceptional result of the delegated request. TRUE indicates that this exception is a polling-pulling failure between the client and agent. FALSE indicates that this exception is the real result of the delegated request.
Core operations made on poller objects are orthogonal to two-phase invocations pending on them. For instance, _is_a() or _non_existent() on a poller does not imply reply polling-pulling on the pending two-phase invocation, but only implies a repository ID comparison and non-existence check on the poller object itself.
reply_not_available
readonly attribute boolean reply_not_available;
This attribute reports the consequence of an unblocked (either normal return or exception) call on a proxy's typed receiver, reply poller, or reply holder, as summarized by the following table.
The terms in the above table are defined as follows:
2PI initiated: This is the result when an operation made on a proxy's typed receiver results in a normal return or an RNA exception (in unsuppressed mode), and the Current reply_not_available attribute is TRUE. This is one of the two premature return cases in Native Messaging. By default, a reply poller of this initiated two-phase invocation is available on Current after the call.
2PI initiation failure: This is the result when an operation made on a proxy's typed receiver results in an exception other than RNA, and the Current reply_not_available attribute is TRUE. This outcome indicates either that the agent has rejected the two-phase invocation, or the client failed to receive agent's premature reply message. No reply poller is available on Current. If this is caused by a communication failure on receiving a premature reply message, the agent will still delegate the request and may even generate a callback to a reply recipient.
2PI completed: This is the result when an operation made on a proxy's typed receiver, a reply poller or reply holder, results in either a normal return or any CORBA exception, and the Current reply_not_available attribute is FALSE. If the operation results in an exception other than RNA, a TRUE reply_not_available attribute indicates that this exception is a real result of a delegated request to target.
Reply not available: This is the result when an operation made on a reply poller results in a normal return or an RNA exception, and the Current reply_not_available attribute is TRUE. This is one of the two premature return cases.
Polling-Pulling failure: This is the result when an operation made on a reply poller or reply holder results in an exception other than RNA, and the Current reply_not_available attribute is TRUE. This outcome indicates a usage or system failure on retrieving the reply, such as calling an unmatched operation or the poller has already been trashed.
N/A: Not an applicable outcome. It should never happen.
Interface ReplyRecipient
ReplyRecipient objects are implemented by Native Messaging applications to receive reply results in the callback model. See the example in “Callback model” and “Cookie and reply de-multiplexing in reply recipients”.
reply_available()
void
reply_available(
in object reply_holder,
in string operation,
in Cookie the_cookie);
This method is callback by request agent on delivering a reply. The actual reply result, either a normal return or an exception, is held by the input reply_holder object and can be retrieved by making a callback on it. If an exception is raised from a call on the reply_holder, the application should use the Current reply_not_available attribute to determine whether the exception is reporting a retrieval failure or the real result of the delegated request. TRUE indicates that this exception is the result of a retrieval failure between the client and agent. FALSE indicates that this exception is a real result of the delegated request.
See the example in “Callback model”.
Within the scope of the reply_available() method, this object reference has the same semantics as a reply poller. A reply retrieving operation on reply_holder should only be made within the scope of the reply_available() method. Once the application returns from reply_available(), the reply_holder may no longer be valid.
The original operation signature. It can be used by applications for coarse grained demultiplexing. A call made on the reply_holder reference should have same operation signature as this parameter. Making a call on the reply_holder with a different operation will end up with a CORBA BAD_OPERATION exception with Current reply_not_available attribute value of TRUE.
Semantics of core operations
Native Messaging reserves all pseudo operations as core operations. Core operations meet the following rules:
They do not initiate a two-phase invocation to be forwarded to the real target when called on a proxy's typed receiver. For instance, calling _non_existent() on a proxy's typed receiver is only a ping to check the non-existence of the receiver itself, not the target.
They are orthogonal to pending two-phase invocations on a reply poller or reply holder: For instance, calling _is_a() or _non_existent() on a reply poller or reply holder does not imply retrieving the reply result of the pending two-phase invocation, but only repository ID comparison and existence checks on these poller or holder objects themselves.
Native Messaging Interoperability Specification
The content of this section is not intended for Native Messaging application developers but for third party Native Messaging vendors.
Native Messaging uses native GIOP
In non-native messaging, such as CORBA Messaging, the OMG GIOP protocol is not used as a direct message protocol; it is used as a tunneling protocol for another ad hoc message routing protocol.
For instance, in CORBA Messaging, calling a mangled operation
sendc_foo(<input_parameter_list>);
does not incur a native OMG GIOP Request message with operation sendc_foo in the head and <input_parameter_list> as payload. Instead, a routing message tunneling through GIOP Request is sent.
Native Messaging uses the native OMG GIOP directly as its message level protocol:
A premature return is simply a native GIOP Reply message containing an RNA exception, specifically a CORBA NO_RESPONSE exception with minor code of REPLY_NOT_AVAILABLE.
A mature return is simply a native GIOP Reply message with either the exact <return_value_and_output_parameter_list> or the exact exception from the target as payload.
Native Messaging service context
Like the OMG Security and Transaction service, Native Messaging also uses a service context to achieve certain semantic results. The client-side Native Messaging engine, implemented in an OMG standardized PortableInterceptor for instance, is responsible for creating and adding required service contexts into certain outgoing requests and for extracting information from the same kind of service context inside incoming replies.
The context_id used by Native Messaging's service context is NativeMessaging::NMService. The context_data is an encapsulated NativeMessaging::NMContextData defined as:
module NativeMessaging {


const IOP::ServiceID NMService = ...

struct RequestInfo {
RequestTag request_tag;
Cookie the_cookie;
unsigned long wait_timeout;
};

union NMContextData switch(short s) {
case 0: RequestInfo req_info;
case 1: unsigned long wait_timeout;
case 2: object the_poller;
case 3: string replier_name;
};
};
Mandated usage of different context data in Native Messaging is summarized in the following table:
The terms in the above table are defined as follows:
req_info: NMContextData is mandated to all requests of two-way non-core operation sending to a proxy's typed receiver. This context has request_tag, cookie and wait_timeout from Native Messaging Current as supplement parameters for initiating a two-phase invocation. The content of this context should be used by the request agent to tag the request, to deliver callback with the cookie, and to wait before evolving into a two-phased invocation. See corresponding topics in the previous sections.
wait_timeout: NMContextData is mandated to all normal (two-way non-core) requests sent to a reply poller, with wait_timeout from Native Messaging Current as supplement parameter for polling. The content, namely the wait_timeout, should be used by the request agent to block the call before a mature or premature return. See corresponding topics in previous sections.
the_poller: NMContextData is mandated to all successful returns on initiating two-phase invocations on a proxy's typed receiver object. The content of the context, a poller reference, is extracted and copied to Native Messaging Current's the_poller attribute.
replier_name: NMContextData is mandated to all exceptional returns as a successful return of an exceptional return result from delegating a request. This context should not appear if the exceptional return is a failure not resulting from delegating the request. The actual content of the string should be empty and preserved for further extension.
Not defined: Native Messaging does not use NMService context in these cases.
N/A: Not applicable. It should never happen.
NativeMessaging tagged component
A tagged component with the NativeMessaging::TAG_NM_REF tag should be embedded in typed receivers of request proxies and poller references. The component_data of this tagged component encapsulates an octet. Namely the first octet of the component_data is the byte-order byte and second byte of it is the value octet. A value of 0x01 for this octet indicates the reference is a typed receiver of a request proxy, and a value of 0x02 indicates it is a poller reference.
This component is used by PortableInterceptor's send_request() method to determine whether a request is sending to a Native Messaging request proxy's the_receiver reference, a reply poller, or something else, and to decide whether and what service context to add to the outgoing request.
Using VisiBroker Native Messaging
Using request agent and client model
Start the Request Agent
To start the Request Agent service, run the command requestagent. Run it with requestagent -? to see the usage information.
Request Agent URL
To use Native Messaging, a request agent needs to be known by client applications. Usually, this is done by initializing the client ORB with the OMG standardized ORB initialize command arguments:
-ORBInitRef RequestAgent=<request_agent_ior_or_url>
This allows client applications to resolve the request agent reference from the ORB as an initial service, for instance:
// Getting Request Agent reference in C++
CORBA::Object_var ref
= orb->resolve_initial_references("RequestAgent");
NativeMessaging::RequestAgentEx_var agent
= NativeMessaging::RequestAgentEx::_narrow(ref);
By default, the URL of a request agent is:
corbaloc::<host>:<port>/RequestAgent
Here, <host> is the host name or dotted IP address of a Request Agent server, and <port> is the TCP listener port number of this server. By default, the Native Messaging Request Agent uses port 5555.
Using the Native Messaging client model
Native Messaging client side models in C++ are implemented as OMG portable interceptors and are referred to as the Native Messaging Client Component. Native Messaging C++ Client Components are implicitly enabled/disabled by link (or load in) with a Native Messaging Client (including callback object) application.
Request Agent vbroker properties
vbroker.requestagent.maxThreads
Specifies the maximum number of threads for request invocation. The default value is 0 (zero) which means no limit. Values cannot be negative.
vbroker.requestagent.maxOutstandingRequests
Specifies the maximum queue size for requests waiting to get serviced. This property only takes effect if the maxThreads property is set to non-zero value. The default value is 0 (zero) which means no limit. Values cannot be negative. If a request arrives when the queue size is equal to maximum size, the request waits for a timeout until there is space in the queue. See “vbroker.requestagent.blockingTimeout”.
vbroker.requestagent.blockingTimeout
Specifies the maximum time, in milliseconds, that a request can wait before it is added to the queue. The default value is 0 (zero) which means no wait. Values cannot be negative. If the value is set to 0 (zero) and a request arrives and the queue is full, the Request Agent will raise CORBA::IMP_LIMIT exception. Otherwise, the request waits for the specified timeout. After the timeout, either the request gets executed immediately if the queue is empty and worker thread is available, or the request is enqueued in the waiting queue if the queue has space and the request remains there until it gets serviced, or if the queue is still full, CORBA::IMP_LIMIT exception is raised by the Request Agent.
vbroker.requestagent.router.ior
Specifies the IOR of OMG messaging router. The default value is empty string.
vbroker.requestagent.listener.port
Specifies the TCP listener port to be used by the request agent. The default value is 5555.
vbroker.requestagent.requestTimeout
This property specifies the maximum time, in milliseconds, that the agent will hold the reply result for its client. If request agent has received reply results on a request, but the client does not pull the result or trash the request, the request agent will trash the request (together with its reply result) upon the expiration of the request timeout set by this property. The default value of this property is infinity, meaning the agent will preserve the reply results until they are trashed by client applications (manually or automatically).
Interoperability with CORBA Messaging
The Native Messaging Request Agent is forward interoperable with the OMG untyped Messaging Router. Specifically, the Request Agent can be configured to route requests through an OMG untyped router instead of sending them directly to their specified targets. To do so, the request agent needs to be started with the property vbroker.requestagent.router.ior with a valid CORBA Messaging router IOR as value.
Migrating from previous versions of VisiBroker Native Messaging
In VisiBroker 6.0 some changes in the Native Messaging IDL have been made that can impact source and binary level compatibility of the applications written using VBE 5.x Native Messaging.
There are two changes that VBE 5.x application developers have to pay attention to. One is in the Property structure and other is the definition of OctetSeq.
Property
In VisiBroker 5.x, Property structure was defined as follows:
module NativeMessaging {
struct Property {
string name;
any value;
};
};
In VisiBroker 6.0 Property has been typedef to CORBA::NameValuePair. That is:
typedef CORBA::NameValuePair Property;
typedef CORBA::NameValuePairSeq PropertySeq;
Native Messaging 5.x applications therefore have to be migrated to use CORBA::NameValuePair for Property.
OctetSeq
In VisiBroker 5.x, OctetSeq was defined as:
typedef sequence<octet> OctetSeq;
RequestTag and Cookie were defined as follows:
typedef OctetSeq RequestTag;
typedef OctetSeq Cookie;
From VisiBroker 6.0 onwards, this definition is removed and RequestTag and Cookie are defined as follows:
typedef CORBA::OctetSeq RequestTag;
typedef CORBA::OctetSeq Cookie;
With this change the reply_available() method of ReplyRecipient interface has to be changed from
void reply_available (CORBA::Object_ptr replyHolder,
const char* operation, const NativeMessaging::OctetSeq& cookie)
to
void reply_available (CORBA::Object_ptr replyHolder,
const char* operation, const CORBA::OctetSeq& cookie)
Therefore, NativeMessaging 5.x applications have to be migrated to use CORBA::OctetSeq for RequestTag and Cookie.
These changes need to be done manually; there is no migration tool available. Note that any VisiBroker 5.x Native Messaging application is “on-the-wire” compatible with VisiBroker 6.0 Request Agent.
Migrating from previous versions of VisiBroker Native Messaging
In VisiBroker 6.0 some changes in the Native Messaging IDL were made that can impact source and binary level compatibility of any applications written using VisiBroker 5.x Native Messaging. The main change is in the Property structure. In VisiBroker 5.x, this structure was defined as follows:
module NativeMessaging {
struct Property {
string name;
any value;
};
};
From VisiBroker 6.0 Property has been typedef to CORBA::NameValuePair. That is:
typedef CORBA::NameValuePair Property;
typedef CORBA::NameValuePairSeq PropertySeq;
Native Messaging 5.x applications therefore have to be migrated to use CORBA::NameValuePair for Property. These changes need to be done manually; there is no migration tool available. Note that any VisiBroker 5.x Native Messaging application is “on-the-wire” compatible with VisiBroker 6.0 and subsequent Request Agent.