![]() |
![]() |
![]() |
CORBA ORBs typically offer application developers a variety of mechanisms to control request processing. Mechanisms such as POA threading models, which control concurrent request processing, Real-Time CORBA's priority mechanisms, which provides platform-independent control of native thread priorities, Real-Time CORBA's threadpool mechanisms, which controls allocation of threads to requests at the POA level, and Asynchronous Message Invocation (AMI), which allows a client to invoke a synchronous request in an asynchronous manner, are defined by formal CORBA specifications. Other mechanisms, such as an ORB's threading and concurrency strategies are defined by ORB implementers.
TAO's Asynchronous Message Handling feature (AMH) offers application developers yet another tool to control request processing in certain situations as described in this article. Using AMH, an application can preserve a synchronous request's appearance while processing it in an asynchronous manner. Put simply, AMH allows a servant to delegate the processing of a request and transmission of a reply to a thread other than the one from which the servant was invoked. Moreover, AMH employs mechanisms that are similar to those used by AMI, so application developers familiar with AMI should have little trouble employing AMH.
AMH is particularly attractive for optimizing middle-tier servers in three-tier architectures, especially when combined with AMI. A common scenario is a middle-tier server that services requests to process large blocks of data. Often, a bulk processing task can be re-organized into a series of smaller processing tasks that may execute concurrently. The results of these subordinate processing tasks are accumulated as they are received and a reply is formulated and sent to the client as soon as all results are available. Using AMH, a servant invoked to process a large data block can delegate the task to another thread. That thread decomposes the input data block into several, smaller data blocks and forwards each of the smaller blocks to a separate back-end server using AMI. The resulting requests are processed concurrently by the various back end servers. The results from the back end servers, returned via AMI's callback mechanism, are accumulated as they are received. As soon as the sub-tasks are complete and all results have been accumulated, a reply is formulated and sent to the client using AMH's reply mechanism.
AMH may also be useful in other circumstances to:
Consider the scenario where the overhead required to process a request is substantially greater than the overhead required to receive and dispatch a request. Using AMH, a greater number of threads can be allocated to processing requests than are allocated to the ORB for receiving and dispatching requests.
A CORBA-based service that supports a large client base may employ AMH to prevent excessive consumption of network stack resources by allocating a greater number of threads to the ORB for servicing clients' connections than are allocated to request processing.
A servant in a middleware bridge delegates request processing to another thread passing the incoming parameters. The delegated thread then formulates another request (in the appropriate format), invokes the target service, and waits for a reply. When the reply is received, the delegated thread uses AMH mechanisms to send a CORBA reply to the client. Meanwhile, the thread originally dispatched to handle the CORBA request is available to perform other ORB-related tasks.
Concurrent CORBA requests can be delegated via a queuing mechanism to a single-threaded active object. The queue of requests is processed serially by the active object's thread, and replies returned to the clients using AMH's reply mechanism.
The remainder of this article compares synchronous request processing with AMH, explains how IDL-defined interfaces are modified for use via AMH, and presents a sample application. Additional information regarding AMH or AMI is available from the following sources:
AMH is included in TAO 1.3a, OCI's commercially supported release, as well as the latest TAO releases from the Distributed Object Computing (DOC) Group.
Additional information regarding Real-Time CORBA's priority and threadpool mechanisms is available in Real-Time CORBA Priority and Threadpool Mechanisms. Request Processing Aspects of CORBA and TAO contains a discussion of the POA's threading models.
Synchronous request processing, also called Synchronous Message Invocation (SMI), is the basic processing model for twoway CORBA invocations. From the client's perspective, a twoway invocation is a blocking call--control does not return to the caller until the invocation has completed. The following diagram summarizes synchronous request processing focusing on the most salient points for this discussion of AMH; many details are omitted in the interest of brevity.

Once the server-side ORB dispatches the request to a thread, that thread is bound to the request until processing is complete and a reply is sent to the client. Therefore, the thread is effectively unavailable for the request's duration. Some ORBs can, in certain scenarios, 'borrow' a thread that is already bound to a request for another task. This can happen with TAO, for example, when a thread is waiting for a reply to an outbound CORBA request made by a servant and it is dispatched to handle an inbound request. (This is commonly referred to as a nested upcall.) When the ORB borrows a thread that is processing a request, completion of that request is usually postponed until the task for which the ORB borrowed the thread, e.g., handling a nested upcall, is complete.
AMH preserves a synchronous request's semantics while allowing the application to process it asynchronously. The following diagram depicts a synchronous request processed using AMH. Additional details have been added as needed to explain AMH, but many other details are still left out.

A response handler contains the context information required to send a reply to the client. This object will be invoked when the request is complete to send a reply. Its interface is implied by the target object's interface, which is similar to the way in which an AMI reply handler's interfaces is implied. Unlike AMI, however, its implementation is also generated by the IDL compiler.
The asynchronous service can be co-located with the servant, if for example the servant queues a request for processing by an active object, or it can be remote, invoked via CORBA or perhaps another middleware. The essential requirement is that processing the request be delegated from the current thread to another thread or process. At a minimum, a reference to the response handler must be passed to the other service. Other information, including the inbound request's parameters may also be passed to the target service.
The thread that was dispatched to process the request is now available for other ORB tasks; meanwhile, the request is processed on a separate thread.
AMH allows an application to process synchronous requests in an asynchronous manner; AMI allows an application to make synchronous requests in a asynchronous manner. Combined, these features offer a means for optimizing the performance of middle-tier servers by limiting the time period that threads must be dedicated to processing requests from the front-end clients and replies from the back-end servers. Threads that service client requests are bound to a request only for the time required to forward the request to a back-end server using AMI. Threads dispatched to process back-end replies are bound to a reply only for the time needed to send a reply to the client.
The following diagram depicts a request processed using a combination of AMH and AMI. As before, some details are omitted in the interest of brevity.

At this point, control returns to the server-side ORB thus releasing the thread and making it available for other tasks. Consequently, the middle-tier server no longer has any threads bound to the client's request, which will remain the case until a reply is received from the remote server.
At this point, control returns thus releasing the thread dispatched to process the remote server's reply.
In addition to preserving the semantics of a synchronous request, AMH also preserves a CORBA object's interface as it is presented to clients. However, the semantics of AMH require a different interface to be realized by the servant as well as a new interface for the response handler, interfaces which are implied by the original interface. The IDL compiler, when directed to compile IDL for use with AMH, generates stubs and skeletons from the original IDL as well as the implied IDL. Moreover, the response handler's implementation is also generated from the implied IDL.
To propagate an exception to the client, an exception holder object and additional methods are also generated from the original interface. For each method appearing in the original interface:
The implied IDL used to produce the skeleton, which declares methods to be realized by the servant, is derived from the original interface as follows:
Interface becomes AMH_Interfaceboolean a_method(...) becomes void a_method(...)short a_method(...) becomes void a_method(in AMH_InterfaceResponseHandler handler, ...)string a_method(in short arg) becomes void a_method(in AMH_InterfaceResponseHandler handler, in short arg)long a_method(inout short arg) becomes void a_method(in AMH_InterfaceResponseHandler handler, in short arg)boolean a_method(out string reply,...) becomes void a_method(in AMH_InterfaceResponseHandler handler,...)The response handler's interface is derived from the original IDL as follows:
Interface becomes AMH_InterfaceResponseHandlerboolean a_method(...) becomes void a_method(...)string a_method(in short arg,...) becomes void a_method(in string return_value,...)string a_method(in short arg,...) becomes void a_method(...)long a_method(inout short arg) becomes void a_method(in short arg)boolean a_method(out string reply) becomes void a_method(in string reply)short a_method(...) raises (Exception) appears in the original interface,a_method_excep(in AMH_InterfaceExceptionHolder) is implied in the response handler's interface,raise_a_method() raises (Exception)Note that the exception holder object's name is formed by pre-pending the string 'AMH_' and appending the string 'ExceptionHolder' to the original interface's name, and that each of the exception holder's methods' exception specifications have the same signature as the corresponding method's exception specification appearing in the original interface.
To compile an interface for use with AMH, pass "-GH" to TAO's IDL compiler. Compile and link applications as usual.
The simple prototype application shown in the following diagram models a three tier architecture. A front-end client (Client) invokes a middle-tier service (Middle_Tier_Server), and the middle-tier service delegates each request to a back-end server (Simple_Server).

The middle-tier server presents the following interface:
interface MessengerService
{
boolean send_message( in string user_name,
in string subject,
inout string message );
};
The back-end server presents a similar interface:
interface Messenger
{
boolean send_message( in string user_name,
in string subject,
inout string message );
};
The middle-tier process employs AMH and AMI; however, this has little effect on the client and back-end server applications. Clients make what appear to be oridinary synchronous requests, and back end servers process requests synchronously. Therefore, the remainder of this article will address implementation of the middle tier server.
MessengerService requests are processed by a proxy object, called MessengerProxy_i, which is deployed in the mid-tier server. This object is organized as shown in the following diagram:

Requests from clients are handled via AMH, so the MessengerProxy_i realizes the implied interface AMH_MessengerService. To invoke the back-end service, MessengerProxy_i keeps a reference to a Messenger CORBA object. An AMI reply handler, called MessengerReplyHandler_i, which realizes the implied AMI_MessengerHandler interface, is used to handle callbacks that are invoked when the back end messenger returns. The callback object is created once and re-used for each request, so MessengerProxy_i keeps a reference to the AMH_MessengerHandler CORBA object as well as the servant. To send replies to front end clients, MessengerReplyHandler_i needs a reference to the implied AMH response handler, AMH_MessengerServiceResponseHandler.
The definition for MessengerProxy_i follows:
class MessengerProxy_i : public virtual POA_AMH_MessengerService
{
public:
MessengerProxy_i(
Messenger_ptr messenger,
MessengerReplyHandler_i *replyHandler,
AMI_MessengerHandler_ptr replyHandlerObj
);
virtual ~MessengerProxy_i (void);
virtual void send_message (
AMH_MessengerServiceResponseHandler_ptr handler,
const char * user_name,
const char * subject,
const char * message
) throw(CORBA::SystemException);
private:
Messenger_var _messenger;
MessengerReplyHandler_i *_replyHandler;
AMI_MessengerHandler_var _replyHandlerObj;
};
Note: according to standard, the string 'POA' is prepended to an interface's name to produce the corresponding skeleton's class name. The implied interface for this service when implemented via AMH is AMH_MessengerService; thus the skeleton's class name is POA_AMH_MessengerService.
When the proxy is created, it is provided with a reference to the back-end messenger service, a pointer to the reply handler servant, and a CORBA reference to the reply handler as well. The proxy's implementation of send_message demonstrates how a request is processed, and how these member variables are used:
void MessengerProxy_i::send_message(
AMH_MessengerServiceResponseHandler_ptr responseHandler,
const char * user_name,
const char * subject,
const char * message
) throw(CORBA::SystemException)
{
_replyHandler->responseHandler(responseHandler);
_messenger->sendc_send_message(
_replyHandlerObj.in(),
user_name,
subject,
message);
};
First, the reply handler is provided with a reference (via a native pointer) to the response handler created for this invocation; when the reply handler is invoked, it will use this reference to return a reply to the client. Next the back-end service is invoked asynchronously; accordingly, a reference to the reply handler is passed as the first parameter to the sendc_ method followed by the parameters defined by the service's interface.
A portion of the reply handler's definition follows:
class MessengerReplyHandler_i : public POA_AMI_MessengerHandler
{
public:
// some methods omitted
void responseHandler(
AMH_MessengerServiceResponseHandler_ptr responseHandler
);
void send_message(CORBA::Boolean ami_return_val,
const char *message
);
private:
AMH_MessengerServiceResponseHandler_var _responseHandler;
};
Note: the implied interface for an AMI reply handler for the Messenger interface is AMI_MessengerHandler; thus the skeleton's class name is POA_AMI_MessengerHandler.
The implementation of responseHandler is:
void MessengerReplyHandler_i::responseHandler(
AMH_MessengerServiceResponseHandler_ptr responseHandler
)
{
_responseHandler =
AMH_MessengerServiceResponseHandler::_duplicate(responseHandler);
}
Duplicating the response handler reference is crucial; recall that it is passed to the AMH servant as an in mode parameter and so must be duplicated to retain the reference over a context switch.
The method for sending a reply to the client is straight forward:
void MessengerReplyHandler_i::send_message(
CORBA::Boolean ami_return_val,
const char *message)
{
_responseHandler->send_message(ami_return_val, message);
}
It simply forwards its parameters to the corresponding response handler's method, which in turn sends a reply to the client.
The relevant portion of the mid-tier server's main follows (some typical code, such as checks for nil object references omitted):
// Get a reference to the back-end server
obj = orb->string_to_object( "file://simple_server.ior" );
Messenger_var messenger = Messenger::_narrow( obj.in() );
// Create the reply handler
MessengerReplyHandler_i *replyHandler = new MessengerReplyHandler_i;
// Activate reply handler and obtain object reference
PortableServer::ServantBase_var tmpServer = replyHandler;
PortableServer::ObjectId_var oid =
poa->activate_object (tmpServer.in ());
obj = poa->id_to_reference (oid.in ());
// narrow reference to appropriate type
AMI_MessengerHandler_var replyHandlerObj =
AMI_MessengerHandler::_narrow(obj.in());
// Create the messenger proxy servant,
MessengerProxy_i *messengerProxy =
new MessengerProxy_i(messenger.in(), replyHandler,
replyHandlerObj.in());
tmpServer = messengerProxy;
oid = poa->activate_object (tmpServer.in ());
CORBA::Object_var server_obj =
poa->id_to_reference (oid.in ());
First, a reference to the back-end server is obtained, in this case from a file. Next, the reply handler is created, activated with the root POA, and an object reference obtained. Then, the messenger proxy is created and initialized with the reference to the back end service, a native pointer to the reply handler servant, and an object reference to the reply handler. Finally, the proxy is activated with the root POA and an object reference obtained to be exported to clients.
The code for the client and back-end servers typical of such processes. The client is unaware that its requests are processed asynchronously, so no special setup is required. Similarly, the back end server is unaware that it is invoked asynchronously, and no special setup is required there either.
This article presented TAO's Asynchronous Message Handling (AMH) feature, another important tool for controlling request processing in CORBA servers. AMH makes it possible to tailor an application to the specific circumstances in which it will be deployed and, when combined with other capabilities such as AMI, can afford significant performance improvements.
Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.
For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.
The OCI CORBA News Brief is intended to promote CORBA and object technology in the development of distributed computing applications. Each issue of the CORBA News Brief will feature news and technical information about OCI's supported open-source ORBs (TAO and JacORB), case studies, and examples using CORBA, as well as information about OCI's educational offerings.
The OCI CORBA News Brief is published on a monthly basis. Send ideas for articles of interest to corba@ociweb.com.
To subscribe or unsubscribe from the CNB mailing list, send mail to majordomo@ociweb.com with the line "subscribe cnb" or "unsubscribe cnb" in the body of the message.
|
|
|