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.
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:
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.
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.
// from: <install_dir>/examples/vbroker/NativeMessaging/
// stock_manager/StockManager.idl
interface StockManager {
boolean add_stock(in string symbol, in double quote);
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:
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.
|
•
|
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.
|
Here, the reply_recipient callback handler is a
NativeMessaging::ReplyRecipient object regardless of the specific application target types. The
ReplyRecipient interface is defined as:
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.
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.
•
|
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.
|
// from: <install_dir>/examples/vbroker/NativeMessaging/
// stock_manager/GroupPollingClient.java
StockManager pollers[] = new StockManager[2];
// send one tagged request
current.request_tag("0".getBytes());
stock_manager_rcv.add_stock("ACME", 100.5);
pollers[0] = StockManagerHelper.narrow(current.
the_poller());
// send another tagged request
current.
request_tag("1".getBytes());
StringHolder symbol_holder = new StringHolder("ACMA");
Stock_manager_rcv.find_closest_symbol(symbol_holder);
pollers[1] = StockManagerHelper.narrow(current.
the_poller());
// polling request availability on proxy and retrieve their replies
byte[][] tags = null;
while(true) {
// polling availability
try {
tags = proxy.
poll(max_timeout, true);
}
catch(PollingGroupIsEmpty e) {
proxy.
destroy(true);
break;
}
// retrieve replies
for(int i=0;i<tags.length;i++) {
int id = Integer.parseInt(new String(tags[i]));
switch(id) {
case 0: // the first tagged request sent above
boolean stock_added;
stock_added = pollers[0].add_stock("", 0.0);
break;
case 1: // the second tagged request sent above
boolean closest_found;
closest_found
= pollers[1].find_closest_symbol(symbol_holder);
break;
default:
break;
}
}
}
•
|
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().
|
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.
// send a requests with a cookie
current.the_cookie("add stock".getBytes());
stock_manager_rcv.add_stock("ACME", 100.5);
// send another request with a different cookie
current.
the_cookie("find symbol".getBytes());
StringHolder symbol_holder = new StringHolder("ACMA");
stock_manager_rcv.find_closest_symbol(symbol_holder);
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.
•
|
Set the wait_timeout attribute of Native Messaging Current to a non-zero value (milliseconds) before the invocations.
|
•
|
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.
|
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.
•
|
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.
|
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.
agent.destroy_request(poller);
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.
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.
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.
void yield_non_rna(org.omg.CORBA.NO_RESPONSE e) {
if(e.minor != NativeMessaging.REPLY_NOT_AVAILABLE.value) {
throw e;
}
}
...
// turn off suppress mode
current.suppress_mode(false);
// send several requests to the receiver, and get
// their reply pollers from the Native Messaging Current.
StockManager pollers[2];
try{ stock_manager_rcv.add_stock("ACME", 100.5)); }
catch(org.omg.CORBA.NO_RESPONSE e) { yield_non_rna(e); }
pollers[0] = StockManagerHelper.narrow(current.
the_poller());
StringHolder symbol_holder = new StringHolder("ACMA");
try{ stock_manager_rcv.find_closest_symbol(symbol_holder)); }
catch(org.omg.CORBA.NO_RESPONSE e) { yield_non_rna(e); }
pollers[1] = StockManagerHelper.narrow(current.
the_poller());
// poll the two associated replies.
current.
wait_timeout(max_timeout);
boolean stock_added;
do { try{ stock_added = pollers[0].add_stock("", 0.0)) }
catch(org.omg.CORBA.NO_RESPONSE e) { yield_non_rna(e); } }
while(current.
reply_not_available());
boolean closest_found;
do { try{ closest_found = pollers[1].find_closest_symbol(symbol_holder)) }
catch(org.omg.CORBA.NO_RESPONSE e) { yield_non_rna(e); } }
while(current.
reply_not_available());
The create_request_proxy() method creates a request proxy to delegate two-phase invocations to the specified target object.
•
|
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”.
|
|
|
|
if TRUE, all requests delegated by this proxy are trashed.
|
•
|
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.
|
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”.
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.
Native Messaging reserves all pseudo operations as core operations. Core operations meet the following rules:
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.
The context_id used by Native Messaging's service context is
NativeMessaging::NMService. The
context_data is an encapsulated
NativeMessaging::NMContextData defined as:
•
|
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.
|
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.
To start the Request Agent service, run the command requestagent. Run it with
requestagent -? to see the usage information.
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.
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”.
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.