Method and system for dynamic configuration of interceptors in a client-server environment6633923Abstract A computer implemented method of creating and managing one or more interceptors. The method comprises the steps of intrinsically chaining the interceptors, and storing state information, in at least one of the chained interceptors, directed to a reference to the next interceptor. A server computer in a client-server environment that includes a processor configured to generate one or more interceptors and to intriscally chain the interceptors, and a memory associated with at least one of the chained interceptors and configured to store state information directed to a reference to the next interceptor thereof. Claims What is claimed is: Description FIELD OF THE INVENTION
module ART_Binding
{
interface Interceptor
{
};
};
It should be noted that common operations or attributes, such as the ability to identify an interceptor or print its state to an ostream can also be included in the above module. 4. Initialization and Shutdown An instance of the ART_BindingManagerImpl implementation of the ART_Binding::BindingManager interface is constructed for each ORB instance during RB initialization. When the ORB is shutdown, the shutdown( ) operation is invoked on his instance before a similar shutdown( ) operation is invoked on the IORManager.
module ART_Binding
{
interface BindingManager
{
/ / . . .
void
shutdown ( ) ;
/ / . . .
};
};
It should be noted that if destruction needs to be delayed until all bindings are actually destroyed, then ITCxxDOAFlyweightFactoryServantBase can be utilized. II. Client Binding This section describes how requests are processed in an ART client. 1. Client-side Interceptors and Factories Client-side interceptors, whether request-level or message-level, are preferably constructed and assembled into chains the same way during the client-side binding process. The following IDL shows the interfaces involved:
module ART_Binding
{
exception LocationForward
{
ART_IOR : : IOR new_ior;
};
interface ClientInterceptor
: Interceptor
{
readonly attribute ClientInterceptor next_interceptor;
};
interface ClientInterceptorFactory
{
ClientInterceptor
get_interceptor (
in ART_IOR : : Profile profile,
in CORBA : : PolicyList policies, / /
in ClientInterceptor next_interceptor
) raises (LocationForward);
};
interface BindingManager
{
/ / . . .
void
register_client_interceptor_factory (
in ClientInterceptorFactory factory
);
void
unregister_client_interceptor factory
in ClientInterceptorFactory factory
);
/ / . . .
};
};
The ART_Binding::ClientInterceptor interface is preferably inherited by the client-side request-level and message-level interceptor interfaces. Although request-level interceptors precede message-level interceptors in a client-side binding, both types are created and chained together during binding in substantially the same way using the ART_Binding::ClientInterceptorFactory interface. The derived interface for request-level client-side interceptors are described below. If message-level interceptors are used, the last request-level interceptor in the binding is responsible for converting the invocation between request-level and message-level. Once an appropriate list of ClientInterceptorFactory instances and an IOR profile have been selected, using a process that will be described later, the ART core attempts to construct a binding as follows: First, it calls the get_interceptor( ) operation on the factory for the interceptor closest to the network, passing in the selected profile and effective [client-side] policies as well as a nil next_interceptor reference. If this succeeds and returns a non-nil ClientInterceptor reference, the ART core then calls the next factory, passing in the same profile and policies, plus the returned reference from the previously called factory. This process repeats until all the factories have been called and a complete chain of ClientInterceptors has been created, or an error has occurred. Note that interceptor factories, except for the one closest to the network, can simply return the ClientInterceptor that was passed in as the next_interceptor parameter if they determine that they do not need to be represented in the binding. If a ClientInterceptorFactory determines that binding should start over with a new IOR, it raises a LocationForward exception containing that new IOR. This would happen, for example, if a GIOP interceptor factory received a location forward response to a locate request it made during the binding process. If it cannot find or create an appropriate ClientInterceptor for any other reason, the ClientInterceptorFactory raises an appropriate standard system exception. The ART_Binding::BindingManager::register_client_interceptor_factory( ) operation is called by a plug-in during ORB initialization to register ClientInterceptorFactory to be used when establishing bindings for that ORB. The unregister_client_interceptor_factory( ) operation can be called to prevent a registered instance from participating in new bindings. The BindingManager holds a reference to the factory from the time register_client interceptor_factory( ) is called until either shutdown( ) or unregister client interceptor_factory( ) is called. It should be noted that a scheme can be provided to organize these factories into legal bindings. 2. Client-side Request-level Interceptors Once a client-side binding has been created, requests are preferably dispatched to the initial request-level interceptor and from one request-level interceptor to the next, using operations defined in the ART Binding::ClientRequestInterceptor interface:
module ART_Binding
{
exception Rebind
{
};
interface ClientRequest / / or struct
{
readonly attribute CORBA::Request request;
readonly attribute ART_IOR::Profile profile
readonly attribute IOP::ServiceContextList
in_service_contexts;
readonly attribute IOP::ServiceContextList
out_service_contexts;
readonly attribute ThreadContext thread_context;
};
interface ClientRequestInterceptor
: ClientInterceptor
{
void
invoke (
in ClientRequest request
) raises (LocationForward, Rebind);
void
send_async(
in ClientRequest request,
in CORBA::ReplyHandler reply_handler / / Messaging
module
) raises (LocationForward, Rebind);
CORBA::PersistentRequest / /
send_deferred(
in ClientRequest request
) raises (LocationForward, Rebind);
};
};
The ART Binding::ClientRequest interface encapsulates all the information involved in making a request, allowing the request to be passed from one interceptor to another interceptor as a single parameter, and allowing it to be queued or passed between threads. An instance is preferably created by the ART core prior to calling one of the three invocation operations on the first ClientInterceptor of the binding that will be used for the request. Contrary to normal C++ mapping rules, the caller is not responsible for releasing pointers returned from ClientRequest attribute accessors. It should be noted that IORRequest::invoke( ) can handle the SyncScopePolicy. Alternatively, an interceptor can check this. If an interceptor does it, the interceptors can queue the request and return even for synchronous invocations that are effectively oneway as known in the art. (1) Synchronous Invocations The invoke( ) operation of the ART_Binding::ClientRequestInterceptor interface preferably passes responsibility for a synchronous invocation to the client-side request-level interceptor on which it is invoked. The interceptor performs its pre-request processing and then pass responsibility on to its next interceptor. When its next interceptor returns, the interceptor performs its post-response processing, and then return to its caller. If its next interceptor is also a ClientRequestInterceptor, it passes responsibility to it by calling invoke( ) on it, passing along the same ClientRequest that was passed to it. Since all request-level client-side interceptors precede any message-level interceptors in a binding, only the last ClientRequestInterceptor in a chain will not pass responsibility for the request onto another ClientRequestInterceptor. If this last ClientRequestInterceptor is the GIOP client-side request-level interceptor, it converts the request to a Request message, or a series of Request message fragments, and passes these to the next interceptor on the chain, which would be a GIOP-specific client-side message-level interceptor. GIOP response message fragments preferably find their way back to the GIOP ClientRequestInterceptor, which will then process the response and return control to its caller. It should be noted that INV_NO_RESPONSE, and various SyncScopePolicy values can be handled for invoke( ) as known to one of ordinary skill in the art. (2) Asynchronous Invocations The send_async( ) operation preferably passes responsibility for an asynchronous invocation to a ClientRequestInterceptor. The response to the invocation is delivered, to this process or another, as an invocation on the CORBA::ReplyHandler reference passed in as a parameter. Each ClientRequestInterceptor in the chain does its pre-invoke processing and then passes responsibility to its next interceptor, by calling send_async( ) and passing along the same ClientRequest and ReplyHandler if not the last ClientRequestInterceptor on the chain. As each send_async( ) call returns to its caller, ClientRequestInterceptors can do any necessary cleanup processing, but cannot assume whether or not a response has been delivered yet. It should be noted that the messaging specification has ReplyHandler and lots of other types in modules other than CORBA. They can be moved into the CORBA module so that may become usable on CORBA module stubs. It should also be noted that if the request is not time-independent, the transport interceptor needs to remember the signature so that it can convert the response to an invocation on the ReplyHandler. The ART core might provide some assistance in this aspect. A time-independent invocation can be treated differently than a regular one. (3) Deferred Synchronous Invocations The send_deferred( ) operation passes responsibility for a deferred-synchronous invocation to a ClientRequestInterceptor, or from one ClientRequestInterceptor to the next. The last ClientRequestInterceptor in the chain then returns a CORBA::PersistentRequest that represents the state of the request and can be used to poll or pend for a response, or to install a ReplyHandler. As send_deferred( ) calls return, the caller can do cleanup processing, but cannot assume whether or not a response has been received. It is noted again that the PersistentRequest is not in the CORBA module in the current Message draft. Non-TII transports to create local non-persistent instances of Persistent Request and deferred synchronous invocations can be implemented as known in the art. III. Server-side Binding This section discusses how requests are processed in an ART server. In particular, it describes the interfaces used by transports, object adapters, and other interceptors in establishing server side bindings and in processing requests via those bindings. The ART architecture allows plug-ins to participate in the processing of requests by supplying interceptors that are included in bindings. In ART, transport protocols (e.g., GIOP, ORB), services (e.g., transactions), and object adapters (e.g., POA), are all packaged as plug-ins and implemented as interceptors. A binding is the set of interceptors involved in communication between a particular client and a particular object or set of objects. Different interceptor and binding interfaces are involved in the client and server roles that make up an invocation. 1. Server-side Interceptors A server-side binding in ART is typically composed of two separate chains of interceptors. The first chain is made up of message-level interceptors, and is present only if the transport being used is implemented in terms of message-level interceptors. The second chain is made up of request-level interceptors, and is independent of the client and transport making the request. The first chain could contain request-level interceptors when some basis exist to associate them with the connection. One possibility would be to add them in response to received service contexts. Another would be to have an interceptor in the per-endpoint chain to cause a related interceptor to get instantiated in the per-connection chain. The establishment of server-side bindings has not yet been addressed. For now, the POA is just creating and registering bindings containing its own interceptors. It should be noted that support per-session request level interceptors can be included in the present invention as well, thereby providing the advantages of UDP and Multicast transports. In particular, session that is based on connections when they are used and on a higher-level service when connections are not used but sessions are needed can also be provided in the present invention. Once a transport server has received an incoming request message header, and has processed it using any appropriate message-level interceptors, the request is dispatched to the server-side interceptors via the following basic steps: 1) The transport server preferably calls the start_server_invocation( ) operation of the BindingManager, passing in the object key from the request header. 2) The BindingManager preferably performs a best-match lookup on the object key to find an appropriate ART_Binding::ServerBinding object, which contains the list of ART_Binding::ServerRequestInterceptors to use in processing the request. It increments the ServerBinding's count of in-progress invocations, and then returns its reference to the transport server. If no matching full or partial object key is found, the BindingManager instead returns a reference to a special ServerBinding that causes the invocation to return a CORBA::OBJECT_NOT_EXIST exception to the client. 3) The transport server then preferably constructs an instance of its implementation of the ART_Binding::ServerRequest interface. This interface inherits from two other interfaces. The inherited ART_Binding::ServerRequestData interface contains the object key, operation name, and service context from the received request message, an initially empty service context for the reply, a thread context, and attributes to hold the client context, arguments, result, and exception. The inherited ART_Binding::ServerRequestCallback interface encapsulates the capabilities to read the remainder of the received message and to send a reply message. 4) The transport server passes this ServerRequest to the dispatch_request( ) operation of the ServerBinding that it obtained from the BindingManager. 5) The ServerBinding calls the invoke( ) operation on the first ServerRequestInterceptor in the chain, passing the transport's ServerRequest as separate ServerRequestData and ServerRequestCallback parameters. 6) The ServerRequestInterceptor does its initial processing for the request and then calls invoke( ) on the next ServerRequestInterceptor in the chain. It passes to this next interceptor the ServerRequestData that was passed to it, and either the same ServerRequestCallback instance that was passed to it, or an instance of its own wrapper implementation of the ServerRequestCallback interface that encapsulates the received ServerRequestCallback. This process repeats until the last ServerRequestInterceptor receives control. 7) The last ServerRequestInterceptor, typically provided by the object adapter used to implement the target object, preferably invokes a series of operations on the ServerRequestData and ServerRequestCallback instances that were passed to it. Wrapper ServerRequestCallback implementations passed along by intermediate ServerRequestInterceptor s perform their own processing as well as delegate the operations back to the encapsulated ServerRequestCallback instances, so that these operations are ultimately performed by the original ServerRequest instance created by the transport server. The operations performed by the object adapter in a typical successful invocation are as follows: Obtain the object key and the operation name from the ServerRequestData, and then determine the signature of the operation being invoked, as well as the servant whose method will execute the operation. Set the arguments attribute of the ServerRequestData instance to a properly typed NVList into which the in and inout parameters are to be demarshaled. Call read_inputs( ) on the ServerRequestCallback instance in order to demarshal the in and inout parameters from the request message into the arguments attribute of the ServerRequestData instance. Set the result( ) attribute of the ServerRequestData instance to contain a properly typed Any for the operation result. Execute the method, using the in and inout parameters values from the arguments attribute, and, placing the out and inout parameter values in the arguments attribute and the result in the result attribute. Call set_success( ) on the ServerRequestData instance to indicate that the operation succeeded. Call write_outputs( ) on the ServerRequestCallback instance to cause the reply message to be sent, containing the result and the out and inout parameters. Call complete( ) on the ServerRequestCallback instance, indicating that all resources associated with the request can be released, and no further calls will be made on the ServerRequestData or ServerRequestCallback instances. 8) When complete( ) is called on the transport server's ServerRequest instance (which inherits the ServerRequestCallback interface), the transport server calls invocation_complete( ) on the ServerBinding instance it obtained from the BindingManager in step 2. 9) The ServerBinding decrements its count of in progress invocations. 10) After the last ServerRequestInterceptor has made the appropriate calls on the ServerRequestData and ServerRequestCallback instances to carry out the operation and return the results, it returns from its invoke( ), which returns from its invoke( ), and so forth. The above described basic process is further explained below in this section with respect to memory management and threading. The memory management rules, and constraints on the order in which ServerRequestData and ServerRequestCallback operations are invoked, enable the arguments to be allocated either on the stack or on the heap, or even supplied by a collocated client. The threading rules, combined with the memory management rules, allow the request to be dispatched between threads or even queued for processing at a later time. The ability to dispatch and queue request processing is achieved by allowing a ServerRequestInterceptor in step 6 above to return to its caller (allowing step 10 to proceed) before passing control to the next interceptor in the chain. Request outcomes other than success are preferably handled by calling different operations on the ServerRequestData and ServerRequestCallback instances in step 7 above. Locate requests are processed similarly to normal requests, with the ServerLocateRequestData and ServerLocateRequest interfaces used in place of the ServerRequestData and ServerRequest interfaces, respectively. Finally, the ART core supplies a concrete implementation of the CORBA::ServerRequest interface, ART_ServerRequestImpl, that encapsulates references to instances of the ART_Binding::ServerRequestData and ART_Binding::ServerRequestCallback interfaces used at the interceptor level. This class, intended for use by the DSI implementations in the POA and other object adapters, adapts the internal ART server-side interfaces and semantics to the standard CORBA::ServerRequest interface and semantics. Extensions to the standard CORBA::ServerRequest interface and semantics are provided in order to also efficiently support skeleton-based object implementations. (1). The ServerRequestInterceptor Interface Plug-ins that implement ORB services or object adapters preferably provide locality constrained implementations of the ART_Binding::ServerRequestInterceptor interface, defined by the following EDL, in order to participate in a server-side binding's processing of invocations:
module ART_Binding
{
interface ServerRequestInterceptor
Interceptor
{
readonly attribute ServerRequestInterceptor next_interceptor;
void
invoke (
in ServerRequestData data,
in ServerRequestCallback callback
) ;
void
locate (
in ServerLocateRequestData data,
in ServerRequestCallback callback
) ;
};
};
The next_interceptor attribute provides access to the next interceptor in the binding, if any. It is always nil for the last interceptor in the binding, and non-nil for the other interceptors. It is exposed in the interface in order to allow the ART core to inspect bindings. The invoke( ) operation preferably transfers responsibility for the execution of a request to the ServerRequestInterceptor on which it is called. The request is represented by the data and callback arguments, which are references to instances of the ServerRequestData and ServerRequestCallback interfaces, respectively, as described below. The interceptor's implementation of invoke( ) may delegate to the next interceptor in the binding or process the request itself. If it delegates, it passes along the same ServerRequestData instance, and may either pass along the same ServerRequestCallback object, or create its own ServerRequestCallback object that encapsulates the one that was passed in. Also, even if invoke( ) delegates processing of the request to another thread, it does not need to duplicate either its data or callback parameters, since the lifecycles of these objects are controlled by the complete( ) operation on ServerRequestCallback rather than by reference counting. The locate( ) operation transfers responsibility for the execution of a location request to the ServerRequestInterceptor on which it is called. It is similar to the invoke( ) operation, except that its data parameter is a ServerLocateRequestData instance rather than a ServerRequestData instance. A location request is a query regarding the location of the target object, and does not imply the invocation of an operation on that object. Either the invoke( ) or locate( ) operation may throw a system exception. If this happens before write_outputs( ) has been called on the callback parameter, it can be caught and the system exception can be the outcome. But this might lead to currency issues since another thread might be processing the same request. Alternatively, throwing even system exceptions from these operations can be disallowed, treat this as an internal failure, and possibly never respond when this happens. The latter approach has the advantage of not requiring interceptors that need to be aware of exception responses to catch exceptions when they delegate. (2) The ServerRequestData Interface Transport plug-ins provide locality constrained implementations of the ART_Binding::ServerRequestData interface, defined by the following IDL, that provide interceptors access to the state associated with a received request:
module ART Binding
{
enum ServerRequestStatus
{
REQUEST_IN_PROGRESS,
REQUEST_NO_EXCEPTION,
REQUEST_USER_EXCEPTION,
REQUEST_SYSTEM_EXCEPTION,
REQUEST_LOCATION_FORWARD,
REQUEST_LOCATION_FORWARD_PERM
};
interface ServerRequestData
{
readonly attribute CORBA: :ORB orb;
readonly attribute CORBA::OctetSeq object_key;
readonly attribute CORBA: : Identifier operation;
readonly attribute IOP::ServiceConteXtList
in_service_contexts;
readonly attribute IOP: :ServiceContextList
out_service_contexts;
readonly attribute ThreadContext thread_context;
readonly attribute CORBA::IT_Context client_context;
readonly attribute ServerRequestStatus status;
attribute CORBA::NVList arguments;
attribute any result;
readonly attribute any except;
readonly attribute ART_IOR::IOR forward_ior;
void
set_success ( );
void
set_exception(
in any except,
in boolean system
);
void
set_forward_ior(
in ART IOR::IOR ior,
in boolean permanent
);
};
};
The normal C++ memory management rules do not apply to the attributes of the ART_Binding::ServerRequestData interface. The pointers returned by the attribute accessors are not, and preferably never be, freed, deleted, or released by the caller. The attribute mutators preferably do not copy or adopt the pointers or references passed in; these pointers or references are assumed to remain valid until the write_outputs( ) operation on the associated ART_Binding::ServerRequestCallback instance has been called and has returned. Finally, the pointers returned by the client_context, arguments, result, and except accessors can potentially be null. Only the immutable attributes (operation, object key, etc.) are valid between write_outputs( ) and completes. The arguments, result, except, and forward_ior attributes are not valid after write_outputs( ). It should be noted that completes may not be all that useful, since most of the details of the request outcome are no longer available when complete( ) is called. For instance, for an interceptor subsequent to one that raised an exception may not know what the exception was. The lifetime of a ServerRequestData instance is controlled by the associated ServerRequestCallback instance. A ServerRequestInterceptor can assume the ServerRequestData instance passed as the data parameter to its invoke( ) operation is valid until the complete( ) operation is called on the ServerRequestCallback instance that was passed as the callback parameter to the same invoke( ) call. Invoking any operations on a ServerRequestData instance, or using any storage previously obtained from an ServerRequestData instance, after invoke( ) has been called on the corresponding ServerRequestCallback instance can result in undefined behavior. Implementations of the ServerRequestData interface do not need to protect themselves against concurrent access. A ServerRequestData instance represents a single invocation. Processing of this invocation is serialized by passing control serially between ServerRequestInterceptor and ServerRequestCallback instances. If one ServerRequestInterceptor delegates to another in a different thread than that in which it was called, it ensures that it does not access the ServerRequestData (or the ServerRequestCallback) in the first thread after the control has been passed to the other thread. The readonly orb attribute provides access to the CORBA::ORB instance that received the request. Its accessor can be called at any time, and the result is not released. The readonly object_key attribute provides access to the sequence<octet> identifying the target object on which the request was invoked. This sequence<octet> is assumed to conform to the ART object key segmentation syntax described in Object Key Section below. Its accessor can be called at any time, and the result is not deleted. Unsegmented string object keys for Interoperable Naming specification can also be supported. The read only operation attribute provides access to the CORBA::Identifier identifying the operation invoked. Its accessor can be called at any time and the result is not freed. The read only in_service_contexts attribute preferably provides access to an IOP::ServiceContextList instance containing the service context information sent by the client to the server as part of the request message. Its accessor can be called at any time. The returned instance is not modified or deleted. The read only out_service_contexts attribute preferably provides access to the ServiceContextList that will be sent by the server to the client as part of the reply message. Its accessor can be called at any time and the result is not deleted. Interceptors that need to include service context information in the reply to the client should append their IOP::ServiceContext instances to the returned list. Appended ServiceContext instances can be copied or adopted, or just referenced. Requiring help allocation can be avoided. Also, internal (flyweight) representation like with IORs can be used. The flyweight supports large numbers of fine-grained objects efficiently in JAVA 1.2. The read only thread_context attribute provides access to an ART_Binding::ThreadContext instance carrying local ORB-service-specific information associated with the request. Its accessor can be called at any time and the result is not released. Typical ORB service implementations will use information from the in_service_contexts attribute to initialize their local state in the thread_context before calling invoke( ) on the next ServerRequestInterceptor, and then make this state available to applications via an object with an interface derived from CORBA::Current. The read only client_context attribute provides access to a CORBA::Context instance containing application-level context information included in the invocation by the client if the IDL definition of the invoked operation contains a context clause. Calling the accessor before read_inputs( ) has been called on the associated ServerRequestCallback instance will result in a CORBA::BAD_INV_ORDER exception. If called after read_inputs( ), the accessor will return a pointer to an initialized CORBA::Context if the operation declaration contains a context clause (i.e. the client marshaled one), and a nil pointer otherwise. The result is not released. The DII can always supply an indication of whether a context clause exists in the IDL. Alternatively, if the DII does not always supply the indicator discussed above, then the above described rule can be relaxed a bit, and the proper DSI semantics can't be provided without using the IFR. The read only status attribute indicates the outcome of the request. Its accessor can be called at any time. Its value is initially REQUEST_IN_PROGRESS, but modified, before write_outputs( ) is called on the corresponding ServerRequestCallback, by calling one or more of the set_success( ), set_exception( ), or set_forward_ior( ) operations as described below. The arguments attribute preferably specifies and provides access to a CORBA::NVList holding the arguments for the request being processed. The NVList, which is initialized with properly-typed anys and flags matching the operation signature, can be supplied by the creator of the ServerRequestData or by one of the ServerRequestInterceptors (usually the last) to which the ServerRequestData is passed. Calling the mutator with a nil value will result in a CORBA::BAD_PARAM exception. Calling the mutator when the attribute already contains a non-nil value, or after read_inputs( ) or write_outputs( ) has been called on the associated ServerRequestCallback instance, will result in a CORBA::BAD INV ORDER exception. The NVList passed to the mutator is not copied, and remains valid until write_outputs( ) has been called on the associated ServerRequestCallback instance and has returned. The accessor can be called at any time and the result is not released. The result attribute preferably specifies and provides access to an any holding the result to be returned from the request being processed. Like the arguments attribute, its value (the type of the any and the storage that will contain the any's value) can be supplied by the creator of the ServerRequestData or by one of the ServerRequestInterceptors to which the ServerRequestData is passed. Contrary to the normal C++ mapping rules, the accessor can return a nil pointer, indicating that no value has been supplied yet. Calling the mutator when the attribute already contains a non-nil value, or after write_outputs( ) has been called, will result in a CORBA::BAD_INV_ORDER exception. The any passed to the mutator is not copied, and remains valid until write_outputs( ) has been called on the associated ServerRequestCallback instance and has returned. At the time write_outputs( ) is called, the result attribute matches or be interchangeable with the result type specified in the IDL definition of the operation being invoked, and a nil result value is equivalent to a result value whose TypeCode has a kind attribute of tk_void. The accessor can be called at any time and the result it returns are not released. The read only except attribute provides access to an any holding an exception to be returned from the request being processed. Calling the except accessor when the status attribute has a value other than REQUEST_USER_EXCEPTION or REQUEST_SYSTEM_EXCEPTION will result in nil pointer being returned. The accessor can be called at any time, and the result is not released. The read only forward_ior attribute provides access to an ART_IOR::IOR instance to which the client should be redirected in order to invoke operations on the target object. Calling the forward_ior accessor when the status attribute has a value other than REQUEST_LOCATION_FORWARD or REQUEST_LOCATION_FORWARD_PERM will result in a nil reference being returned. The accessor can be called at any time and the result is released. The except and forward_ior accessors can raise BAD_INV_ORDER if status is not correct instead of returning nil. The set_success( ) operation sets the value of the read only status attribute to REQUEST_NO_EXCEPTION. Calling set_success( ) when the status attribute has a value other than REQUEST_IN PROGRESS, or either before the read_inputs( ) operation or after the write_outputs( ) operation has been called on the corresponding ServerRequestCallback instance, results in a CORBA::BAD_INV_ORDER_exception. The set_exception( ) operation sets the value of the read only status attribute to REQUEST_USER_EXCEPTION if its system parameter is FALSE, or to REQUEST SYSTEM EXCEPTION if its system parameter is TRUE. It also sets the value of the read only except attribute to the exception stored in its except parameter. If the system parameter is TRUE, the except parameter contains a system exception, and if it is FALSE, the except parameter contains one of the user exceptions specified in the IDL definition of the operation being invoked. An illegal except parameter value results in undefined behavior. Calling set_exception( ) after the write_outputs( ) operation has been called on _the corresponding ServerRequestCallback instance results in a CORBA::BAD INV_ORDER exception. The any passed as the except parameter is not copied, and remains valid until write outputs( ) has been called on the associated ServerRequestCallback instance and has returned. The set_forward_ior( ) operation sets the value of the read only status attribute to REQUEST_LOCATION_FORWARD if its permanent parameter is FALSE, and to REQUEST_LOCATION_FORWARD PERM if its permanent parameter is TRUE. It also sets the value of the read only forward_ior attribute to the value of the ior parameter. Passing a nil ior parameter results in a CORBA::BAD PARAM exception. The IOR passed as the ior parameter is not duplicated, and remains valid until write_outputs( ) has been called on the associated ServerRequestCallback instance and has returned. Transport protocols or versions thereof that do not support the notion of permanent location forwarding should treat REQUEST_LOCATION_FORWARD_PERM as REQUEST_LOCATION_FORWARD. (3) The ServerLocateRequestData Interface Transport plug-ins provide locality constrained implementations of the ART_Binding::ServerLocateRequestData interface, defined by the following IDL, that provides interceptors access to the state associated with a received locate request:
module ART_Binding
{
enum ServerLocateRequestStatus
{
LOCATE IN PROGRESS,
LOCATE_UNKNOWN_OBJECT,
LOCATE_OBJECT_HERE,
LOCATE_OBJECT_FORWARD,
LOCATE_OBJECT_FORWARD_PERM,
LOCATE_SYSTEM_EXCEPTION
};
interface ServerLocateRequestOata
{
read only attribute CORBA::ORB orb;
read only attribute CORBA::OctetSeq object_key;
read only attribute ThreadContext thread_context;
read only attribute ServerLocateRequestStatus status;
read only attribute any except;
read only attribute ART_IOR::IOR forward ior;
void
set_object_here( );
void
set_unknown_object( );
void
set_exception(
in any except
);
void
set_forward_ior(
in ART_IOR::IOR ior,
in boolean permanent
);
};
};
The ServerLocateRequestData interface is similar to the ServerRequestData interface used for normal requests, which is described above. It follows the same non-standard C++ memory management rules, its life span is controlled in substantially the same way, and the same substantially concurrency considerations apply. The orb and object_key attributes behave identically to these attributes of the ServerRequestData interface. The thread_context attribute is also substantially similar, except that there are no ServiceContextLists available to propagate service-specific state. The read only status attribute indicates the outcome of the locate request. Its accessor can be called at any time. Its value is initially LOCATE_IN_PROGRESS, but it is modified, before write_outputs( ) is called on the corresponding ServerRequestCallback, by calling one or more of the set_object_here( ), set_unknown_object( ), set_exception( ), or set_forward_ior( ) operations as described below. The read only except attribute provides access to an any holding an exception to be returned from the locate request being processed. Calling the except accessor when the status attribute has a value other LOCATE_SYSTEM_EXCEPTION will result in nil pointer being returned. The accessor can be called at any time, and the result is not released. The read only forward_ior attribute provides access to an ART_IOR::IOR instance to which the client should be redirected in order to invoke operations on the target object. Calling the forward_ior accessor when the status attribute has a value other than LOCATE_OBJECT_FORWARD or LOCATE_OBJECT_FORWARD_PERM will result in a nil reference being returned. The accessor can be called at any time and the result is not released. The except and forward_ior accessors can raise BAD_INV_ORDER if status is not correct instead of returning nil. The set_object_here( ) operation sets the value of the read only status attribute to LOCATE_OBJECT_HERE. Calling set_object_here( ) when the status attribute has a value other than LOCATE_IN_PROGRESS, or either before the read_inputs( ) operation or after the write_outputs( ) operation has been called on the corresponding ServerRequestCallback instance, results in a CORBA::BAD_INV_ORDER exception. The set_unknown_object( ) operation sets the value of the read only status attribute to LOCATE_UNKNOWN_OBJECT. Calling set_unknown_object( ) when the status attribute has a value other than LOCATE_IN_PROGRESS, or either before the read_inputs( ) operation or after the write_outputs( ) operation has been called on the corresponding ServerRequestCallback instance, results in a CORBA:BAD_INV_ORDER exception. A call to read_inputs( ) can either be allowed or required before calling set_object here( ) or set_unknown_object( ) when processing a locate request. The set_exception( ) operation sets the value of the read only status attribute to LOCATE_SYSTEM EXCEPTION. It also sets the value of the read only except attribute to the exception specified in its except parameter. The except parameter contains a system exception, and an illegal except parameter value results in undefined behavior. Calling set_exception( ) after the write_outputs( ) operation has been called on the corresponding ServerRequestCallback instance results in a CORBA::BAD_INV_ORDER exception. The any passed as the except parameter is not copied, and remains valid until write_outputs( ) has been called on the associated ServerRequestCallback instance and has returned. The set_forward_ior( ) operation sets the value of the read only status attribute to LOCATE_OBJECT_FORWARD if its permanent parameter is FALSE, and to LOCATE_OBJECT_FORWARD_PERM if its permanent parameter is TRUE. It also sets the value of the read only forward_ior attribute to the value of the ior parameter. Passing a nil ior parameter results in a CORBA::BAD_PARAM exception. The IOR passed as the ior parameter is not duplicated, and remains valid until write_outputs( ) has been called on the associated ServerRequestCallback instance and has returned. Transport protocols or versions thereof that do not support the notion of permanent location forwarding should treat LOCATE_OBJECT_FORWARD_PERM as LOCATE_OBJECT_FORWARD. (4) The ServerRequestCallback Interface Transport plug-ins, as well as some ORB service plug-ins, provide locality constrained implementations of the ART Binding::ServerRequestCallback interface, defined by the following IDL:
module ART_Binding
{
interface ServerRequestCallback
{
void
read inputs ( );
void
write_outputs ( );
void
complete ( );
};
};
A transport's implementation of the operations on the ART_Binding::ServerRequestCallback interface perform the major steps in processing a received request that takes place after the request header has been read and the request has been dispatched to the appropriate binding. These include reading the remainder of the request message, sending the reply, and cleaning up after processing is complete. An ORB service's implementation of the ServerRequestCallback interface allows it to intercept these actions and observe or influence the outcomes of the requests before delegating back to the ServerRequestCallback instance that it wraps. Implementations of the ServerRequestCallback interface, including wrappers provided by ServerRequestInterceptor s, do not need to protect themselves against concurrent access. A ServerRequestCallback represents a single invocation. Processing of this invocation is serialized because control is passed serially between ServerRequestInterceptor and ServerRequestCallback instances. Even though they might be invoked in different threads, the calls to the operations of a ServerRequestCallback instance are guaranteed to not overlap in time. The read_inputs( ) operation is called by a ServerRequestInterceptor, usually the last in the chain, in order to complete processing of the request message. Calling read_inputs( ) more than once, when an associated ServerRequestData's status attribute is not REQUEST_IN_PROGRESS, or when an associated ServerLocateRequestData's status attribute is not LOCATE_IN_PROGRESS, results in a CORBA:BAD_INV_ORDER exception. ServerRequestCallback implementations provided by ORB services delegate the read_inputs( ) operation back to the ServerRequestCallback instances they wrap, unless they are causing an exception to be returned. When an exception is sent, read_inputs( ) can be called. An interceptor is able to reject a request before the signature is available. Also, read_inputs( ) can be called for locate requests. The write_outputs( ) operation is called by a ServerRequestInterceptor, again usually the last, in order to indicate that all information needed to send the reply is available in the associated ServerRequestData or ServerLocateRequestData instance, and that the reply should be sent. Calling write_outputs( ) more than once, when an associated ServerRequestData's status attribute is REQUEST_IN_PROGRESS, or when an associated ServerLocateRequestData's status attribute is LOCATE_IN_PROGRESS, results in a CORBA::BAD_INV_ORDER exception. ServerRequestCallback implementations provided by ORB services delegate the write_outputs( ) operation back to the ServerRequestCallback instances they wrap. The complete( ) operation is called by the last ServerRequestInterceptor to which the ServerRequestCallback was passed, in order to indicate to the transport and to any ORB services that have wrapped the transport's ServerRequestCallback instance, that processing of the request is complete and that no more operations will be called on either that ServerRequestCallback instance, or its associated ServerRequestData or ServerLocateRequestData instance. Calling complete( ) before write_outputs( ) has been called will result in a CORBA::BAD_INV_ORDER exception. After calling read_inputs( ) on a ServerRequestCallback instance, the last interceptor in the chain checks the status attribute of the associated ServerRequestData or ServerLocateRequestData instance before proceeding to perform the requested operation and/or to call write_outputs( ) on the ServerRequestCallback instance. If the status attribute has been changed by the transport, or by an ORB service's ServerRequestCallback wrapper, to a value other than REQUEST_IN_PROGRESS or LOCATE_IN_PROGRESS, then the last interceptor does not perform the requested operation or call write_outputs( ); it should simply do any necessary cleanup and then call complete( ) on the ServerRequestCallback. Whatever entity(object adapter, ORB service, or transport) changes the status is also responsible for also calling write_outputs( ) Read_inputs( ) can return a value indicating whether write_outputs( ) has already been called so that it is not necessary to examine the status. It should be noted that the above discussed rules can be changed so that only the last interceptor ever calls write_outputs( ). This may allow all interceptors to intercept all write_outputs( ) calls (5) The ServerRequest Interface Transport plug-ins provide locality-constrained implementations of the ART_Binding::ServerRequest interface, which is defined by the following IDL:
module ART Binding
{
interface ServerRequest
ServerRequestData,
ServerRequestCallback
{ };
};
It inherits both the ServerRequestData and ServerRequestCallback interfaces. An instance of this interface is constructed by the server side of a transport and passed to the dispatch_request( ) operation of a ServerBinding obtained from the BindingManager in order to dispatch processing of the request to the proper chain of ServerRequestInterceptors. The ServerBinding then passes this ServerRequest instance to the first ServerRequestInterceptor on the chain as separate ServerRequestData and ServerRequestCallback parameters, and processing of the request proceeds. (6) The ServerLocateRequest Interface Transport plug-ins for protocols that support locate requests also provide locality-constrained implementations of the ART Binding:ServerLocateRequest interface, which is defined by the following IDL:
module ART Binding
{
interface ServerLocateRequest :
ServerLocateRequestData,
ServerRequestCallback
{ };
};
It inherits both the ServerLocateRequestData and ServerRequestCallback interfaces. An instance of this interface is constructed by the server side of a transport and passed to the dispatch_locate_request( ) operation of a ServerBinding obtained from the BindingManager in order to dispatch processing of the locate request to the proper chain of ServerRequestInterceptors. The ServerBinding then passes this ServerLocateRequest instance to the first ServerRequestInterceptor on the chain as separate ServerLocateRequestData and ServerRequestCallback parameters, and processing of the locate request proceeds. (7) The ServerBinding Interface The ART core provides a locality-constrained implementation of the ART_Binding::ServerBinding interface defined by the following DL:
module ART_Binding
{
interface ServerBinding
{
readonly attribute unsigned long active invocations;
readonly attribute unsigned long total invocations;
void
dispatch_request (
in ServerRequest request
);
void
dispatch_locate_ request (
in serverLocateRequest request
);
void
invocation_complete ( );
void
unregister ( );
boolean
unregister_if_inactive (
in unsigned long ignored_activity_count
);
void
set_callback (
in ServerBindingCallback callback
);
};
};
The BindingManager creates an instance of the core implementation of the ServerBinding interface when an object adapter plug-in registers a binding, and returns a reference to it to the object adapter so that the object adapter can manage the binding. When a transport plug-in receives a request or a locate request, it obtains a reference to the appropriate ServerBinding instance from the Binding Manager, and uses it to dispatch the request. The life span of a ServerBinding instance is controlled both by its reference count and by its count of active invocations. When a new instance is created using the BindingManager::register_server_binding( ) operation, the BindingManager holds one reference to the ServerBinding, and the returned ServerBinding reference counts as a second reference. The BindingManager's reference to the ServerBinding is released when the binding is unregistered, and the object adapter also releases its reference at some point. When a reference to a ServerBinding instance is returned to a transport by the BindingManager::start_sarver_invocation( ) operation, the reference count of the ServerBinding is not incremented, and the transport therefore does not release the returned reference; instead, the ServerBinding's count of active invocations is incremented. The ServerBinding is destroyed when both its reference count and its count of active invocations are zero. The readonly active_invocations attribute indicates how many invocations are currently active on the binding. This count includes both normal requests and locate requests. Note that the count returned from this attribute's accessor is simply a snapshot of the value at the time it was invoked, and other invocations might be dispatched to the binding unless it is unregistered. The readonly total_invocations attribute provides access to the cumulative count of invocations dispatched to the ServerBinding instance, including both normal requests and locate requests. Readonly attributes can be provided to ensure access to the object key and interceptor chain. The dispatch_request( ) operation is called by a transport server to cause a ServerBinding object, obtained from the BindingManager::start_server_invocation( ) operation, to process the received request represented by the ServerRequest instance it passes as the request argument. The dispatch_request( ) operation may return control to the transport server before processing has been completed. Because processing might proceed concurrently in a different thread, the transport server does not manipulate the ServerBinding from the thread in which it called dispatch_request( ) after dispatch_request( ) returns. Any exception thrown by dispatch_request( ) cannot be returned to the client. The dispatch_locate_request( ) operation is identical to the dispatch_request( ) operation, except that it is used to process locate requests instead of normal requests, and is passed a ServerLocateRequest instance rather than a ServerRequest instance. The invocation_complete( ) operation is called by the complete( ) method of a transport server's ServerRequest or ServerLocateRequest implementation in order to inform the ServerBinding that processing of a request is finished. This may result in an unregistered ServerBinding being deleted. Therefore, the complete( ) methods of transports' ServerRequest and ServerLocateRequest implementations, and of ORB services' ServerRequestCallback wrapper implementations, do not do anything after complete( ) has been called that would be invalid if the interceptors making up the binding have been deleted. The unregister( ) operation is called by an object adapter to immediately unregister the ServerBinding with the BindingManager. No further calls to BindingManager:start_server_invocation( ) or BindingManager::get_server binding( ) will return this ServerBinding. The BindingManager's reference to the ServerBinding will be released, and the ServerBinding will be destroyed when no other references to it exist and its active invocation count is zero. The unregister_if_inactive( ) operation is similar to the unregister( ) operation, except that no action is taken unless the ServerBinding's active invocation count is less than or equal to the value of the ignored_activity_count parameter. TRUE is returned if the binding is unregistered; otherwise FALSE is returned. An object adapter can use the ignored_activity_count parameter to allow the binding to be unregistered from within the context of an invocation dispatched to that binding. The set_callback( ) operation is used by an object adapter to associate an ART_Binding::ServerBindingCallback instance that it implements with the ServerBinding. The ServerBindingCallback interface is described below. Any existing association with a ServerBindingCallback is broken, and its reference is released. The set_callback( ) operation duplicates the reference passed in, and a nil value is legal. (8) The ServerBindingCallback Interface Plug-ins implementing object adapters optionally can provide locality-constrained implementations of the ART_Binding::ServerBindingCallback interface defined by the following IDL:
module ART_Binding
{
interface ServerBindingCallback
{
void
binding inactive ();
};
};
If a ServerBindingCallback instance is associated with a ServerBinding, its binding_inactive( ) operation will be called whenever the ServerBinding's active invocation count becomes zero. This call to binding_inactive( ) is made with the mutex protecting the ServerBinding's state unlocked, allowing its implementation to invoke operations that manipulate the ServerBinding. But this also means that the ServerBinding's active invocation count might concurrently become non-zero before or during the execution of binding_inactive( ), so unregister_if_inactive( ) should be used instead of unregister( ) in order to unregister a binding from binding_inactive( ) only if it really is inactive. It turns out that the ServerBindingCallback functionality is not needed by the current POA implementation. It can be removed. Alternatively, it is not removed if it is generally useful for other object adapters. (9) BindingManager Operations The ART_Binding::BindingManager contains the following operations that are used by transport and object adapter plug-ins to manage server-side bindings:
module ART Binding
{
interface BindingManager
{
I / / . . .
/ / server-side binding management
readonly attribute unsigned long
active_server_invocations;
readonly attribute unsigned long
total_server_invocations;
ServerBinding
register_server_binding (
in CORBA::OctetSeq key,
in ServerRequestInterceptor interceptor,
in ServerBindingcaliback callback
);
ServerBinding / / release
get_server_binding (
in CORBA::OctetSeq key
);
ServerBinding / / don't release, call
invocation_complete
start_server_invocation(
in CORBA::OctetSeq key
);
/ / object_key manipulation
void
append object key segment
inout CORBA::OctetSeq object_key,
in CORBA::OctetSeq segment_data
);
void
parse object key_segment (
in CORBA::OctetSeq object_key,
in unsigned long offset, / / offset into
object_key
out unsigned long segment data_start,
out unsigned long segment data_length
);
void
find object key_segment (
in CORBA::OctetSeq object_key,
in unsigned long index, / / segment number
out unsigned long segment_data_start,
out unsigned long segment_data_length
);
/ / . . .
};
The ART core provides a locality-constrained BindingManager instance for each ORB instance. The BindingManager supports server-side binding by maintaining a table that maps complete or partial object keys to ServerBinding instances. It also keeps an overall count of active server-side invocations that have been dispatched to those ServerBindings. The readonly active_server_invocations attribute indicates how many server-side invocations are currently active on the ORB. This count includes both normal requests and locate requests. Note that the count returned from this attribute's accessor is simply a snapshot of the value at the time it was invoked. The readonly total_server_invocations attribute provides access to a cumulative count of invocations dispatched by the ORB, including both normnal requests and locate requests. The register_server binding( ) operation is called by an object adapter to create and register a ServerBinding instance associating the chain of ServerRequestInterceptor s specified via the interceptor parameter and the ServerBindingCallback instance specified by the callback parameter with the full or partial object key specified by the key parameter. It returns a reference to that ServerBinding, which is eventually released by the object adapter. If successful, the interceptor and callback parameters are duplicated, and the key parameter is copied. A nil interceptor parameter results in a CORBA::BAD PARAM exception, while a nil callback parameter is acceptable. If a binding is already registered under the full or partial object key specified by the key parameter, CORBA::BAD_INV_ORDER is raised. If the key parameter does not conform to the ART object key segmentation syntax, CORBA::INV OBJREF is raised. Unsegmented string object keys for Interoperable Naming specification can also be supported. The get_server_binding( ) operation finds the ServerBinding that would be returned from start_server invocation( ) given the same key parameter, and duplicates and returns its reference. If there is no full or partial match, a nil reference is returned. A boolean parameter, that, if set, would require an exact match can also be provided. The start_server_invocation( ) operation is called by a transport server in order to obtain the ServerBinding instance that will be used to dispatch a received normal request or locate request. The transport passes the received object key as the key parameter. The BindingManager finds the registered ServerBinding whose object key best matches the specified object key, increments its active invocation count, and returns its reference to the transport. The transport calls invocation_complete( ) on the returned ServerBinding after processing of the request by the ServerBinding's interceptor chain is complete, and does not release the returned reference. If there is no registered ServerBinding that completely or partially matches the specified object key, a reference is returned to a special ServerBinding instance whose dispatch_request( ) implementation sets the CORBA::OBJECT_NOT_EXIST exception on the ServerRequest passed to it, and whose dispatch_locate_request( ) implementation calls set_unknown_object( ) on the ServerLocateRequest passed to it. If the key parameter does not conform to the ART object key segmentation syntax, start_server_invocation( ) raises CORBA::INV_OBJREF. The append_object_key_segment( ) operation is used by object adapter plug-ins and associated location daemon functionality to construct object keys that conform to the ART object key segmentation syntax. The octet sequence passed as the segment_data parameter is encoded as a segment and appended to the octet sequence provided as the object_key parameter. Note that in the C++ implementation of this operation, the inout object_key parameter is modified and not replaced, so performance can be optimized by constructing this octet sequence with a large enough buffer to hold typical object keys. The parse_object_key_segment( ) operation is used by object adapter plug-ins, and associated location daemon functionality to decode object keys that conform to the ART object key segmentation syntax. It decodes the encoded segment beginning at the offset indicated by the offset parameter in the octet sequence passed as the object_key parameter. Upon return, the segment_data_start out parameter indicates the offset into object_key at which the actual segment data begins, and the segment_data_length out parameter indicates the length of the actual segment data. If the object_key parameter data at the indicated offset does not conform to the ART object key segmentation syntax, CORBA::INV_OBJREF is raised. The find_object_key_segment( ) operation is similar to the parse_object_key_segment( ) operation, but provides random rather than sequential access to the segments of an encoded object key. It decodes the Nth encoded segment of the object_key parameter, where N is specified by the index parameter. An index of zero specifies the initial segment, and is equivalent to calling parse object_key_segment( ) with an offset of zero. Note that the ART segmentation format does not support true random access, but using the find_object_key_segment( ) operation to access a single segment is slightly more efficient than using an equivalent series of parse_object key_segment( ) calls. 30 2. Dynamic Server Interface This section describes support the ART core provides for an object adapter's DSI and skeleton implementations. It extends the standard CORBA::ServerRequest interface to improve performance, and provides an implementation of this extended interface in terms of the internal ART_Binding::ServerRequestData and ART Binding::ServerRequestCallback interfaces described above. (1) The CORBA::ServerRequest Interface ART extends the standard CORBA::ServerRequest interface in order to provide more flexible and efficient memory management for DSI-based object implementations and for DSI-based static skeletons, such as those used by the ART POA, that are based on CORBA::ServerRequest. The extensions allow the object implementation to determine whether argument and result storage has been provided by the client, and, if not, allow it to allocate them, and the values they contain, on the stack rather than the heap. The extended CORBA::ServerRequest interface is defined by the following IDL:
module CORBA
{
interface ServerRequest
{
readonly attribute Identifier operation;
void
arguments (
inout NVList nv
);
Context
ctx ();
void
set_result (
in any val
);
void
set_exception (
in any val
);
readonly attribute ORB it_orb;
boolean
it_setup (
out NVList arguments,
out any result
);
void
it_respond ();
};
};
The operation attribute and the arguments( ), ctx( ), set_result( ), and set_exception( ) operations all behave exactly as specified in CORBA if the it_setup( ) operation has not been previously called on the ServerRequest instance. If it_setup( ) has been called, their behavior is modified as described below. The readonly it_orb attribute provides access to the CORBA::ORB instance that received the request. Its accessor can be called at any time, and the result is not released. Calling the it_setup( ) operation before calling other CORBA::ServerRequest operations modifies management of the argument and result memory for the ServerRequest. If it_setup( ) is not called, the ServerRequest is responsible for releasing any CORBA::NVList pointer passed in to the arguments( ) operation, the DIR (Dynamic Implementation Routine; i.e., the DSI-based method or skeleton) is prepared for arguments( ) to return a different NVList, and the ServerRequest copies the Any passed to set_result( ). If the DIR calls it_setup( ), the normal memory management rules are modified as describe here, and depend on whether it_setup( ) returns TRUE or FALSE. If it_setup( ) returns TRUE, initialized argument and result storage already exists for the request, such as when a collocated client has supplied it, and is returned to the DIR via the arguments and result out parameters, which are not released. In this case, if the DIR calls arguments( ) or set_result( ), a CORBA::BAD_INV_ORDER exception will be raised. If it_setup( ) returns FALSE, initialized argument and result storage is not available, and the DIR calls arguments( ) and may call set_result( ) as usual. In this case, arguments( ) and set_result( ) do not adopt or copy the parameters passed to them, which are expected to remain valid until the required call to it_respond( ) is made, and arguments( ) is guaranteed not to return a different NVList. Note that management of the memory passed to set_exception( ) is not effected by calling it_setup( ). Calling it_setup( ) after having previously called it_setup( ), arguments( ), ctx( ), set_result( ), or set_exception( ) will result in a CORBA::BAD_INV_ORDER exception. The it_respond( ) operation signals to the ServerRequest that the out and inout arguments and result, or else an exception, are ready to be sent to the client. If it_setup( ) was previously called and returned TRUE, any storage passed in via arguments( ) or set_result( ) can then be freed once it_respond( ) returns. Calling it_respond( ) before either it_setup( ) was called and returned TRUE, or arguments( ) or set_exception( ) was called, will result in a CORBA::BAD_INV_ORDER exception, as will calling it more than once. (2) The ART_ServerRequestImpl Class The ART core provides a concrete C++ class implementing the CORBA::ServerRequest interface with the following public members:
class IT_ART_API ART_ServerRequestImpl
public CORBA: :ServerRequest,
public ITCxxDOAServantBase
{
public:
ART_ServerRequest Impl
ART_Binding::ServerRequestData_ptr data,
ART_Binding: :ServerRequestCallback_ptr callback
);
.about.ART_ServerRequest Impl 0;
const char*
operation () const;
void
arguments (
CORBA::NVList_ptr& parameters
);
CORBA: :Context_ptr
ctx ();
void
set_result (
const CORBA: :Any& val
);
void
set_exception (
const CORBA: :Any& val
);
CORBA::ORB_ptr
it_orb ();
CORBA: :Boolean
it_setup (
CORBA: :NVList_out arguments,
CORBA: :Any out result
);
void
it respond();
};
The constructor and destructor of the ART_ServerRequestImpl class are public, allowing an object adapter to allocate instances on the stack or as members of other data structures. An object adapter's ServerRequestInterceptor creates an instance of the ART_ServerRequestImpl class, passing the ServerRequestData and ServerRequestCallback references to the public constructor. It then passes the ART_ServerRequestImpl pointer as a CORBA::ServerRequest reference to the DSI or skeleton based servant that is responsible for performing the requested operation. The ART_ServerRequestImpl instance with then take care of making all required calls on the ServerRequestData and ServerRequestCallback instances passed to its constructor. The interceptor can assume that write_outputs( ) has been called on the ServerRequestCallback by the time the class instance has been destroyed. The ART_ServerRequestImpl class does not call complete( ) on the ServerRequestCallback, leaving that to the object adapter implementation. The object adapter registers an instance of its own ServerRequestInterceptor implementation. The BindingManager can be configured to allow other ORB services to have their interceptors included. In addition to ensuring that the proper interceptors are included in service-side bindings, the ORB also ensures that the proper IOR profiles and components are included in generated object references. This may involve the BindingManager. The DSI servant is not required to check if an exception has been set after calling CORBA::ServerRequest::arguments( ). However, an exception can be raised here. The servant then may try and set that exception on the ServerRequest. A WorkQueue can be passed along so that the correct thread pool is used. This should also be made available to message-level interceptors if they can determine the correct threading policy. Per-connection server bindings can also be provided. This would be more symmetric with the client side, and would allow ServerRequestInterceptors to maintain connection-specific state. It should also be noted that the order in which server side interceptors are logically chained together can be reversed. If the OA interceptor received control first, it would make read_inputs( ) and write_outputs( ) calls that are what need to be intercepted. This might eliminate the overhead of the invoke( ) call and return, and much of the threading. In exchange, the ability to "intercept" a request before application code got called can be lost, since read_inputs( ) cannot be called until the DSI has supplied the NVList. IV. GIOP Plug-in This section describes the architectural design of the GIOP transport plug-in, and relevant interfaces for the GIOP based message interceptors. In particular, this section describes the ART framework for representing GIOP based transports in ART. This is based on the most recent GIOP 1.0, 1.1 and 1.2 OMG specification described in CORBA 2.3, chapter 12, "General Inter-ORB Protocol". GIOP essentially defines message formats, data marshalling and general connection assumptions for connection oriented transports. The only current mapping of GIOP is the OMG specified IIOP protocol for mapping GIOP messages over the TCP/IP transport. The specification also defines agent roles for client and server side endpoints of a given connection ART defines a general Plug-In interface for registering plug-ins with the ORB. The GIOP and HOP plug-ins are the default plug-ins for ART. Both plug-ins are provided as dynamic libraries and may optionally be loaded and registered with the ORB depending on per ORB configuration settings. ART will also provide a plug-in developers kit (PDK) which enables plug-in developers to implement and deploy specialized request and message interceptor plug-ins. The ART architecture defines Request level interceptor interfaces which determine how the request interceptor chain is established during the binding process. These interfaces also define how request processing is initialized and passed between request interceptors, as detailed in the above sections and only mentioned in this section where relevant to the message interceptor interfaces. The ART_GIOP IDL interface defines the message level interceptor interface for GIOP based message interceptors. The message interceptor chain lies between the request interceptor chain and the network and specifically provide for stacking of protocols between Request level interceptors and the network. There are three generally described roles a message interceptor plays in the binding. Message interceptors act either as a link between request and message level interceptor chains, implemented by the GIOP plug-in described here, as intermediate message interceptors for processing or examining message data, or as connection interceptors which are placed closest to the network and act as both message interceptor and event handlers specific to an open connection. The ART GIOP plug-in provides for full CORBA defined GIOP connection usage. Connections will be fully shareable by multiple client objects, both HOP and SECP. Synchronous, deferred synchronous and full asynchronous messaging are also supported. Asynchronous messaging however, is not specified here in detail until the OMG has standardized the specification. The CORBA GIOP 1.0, 1.1 and 1.2 revisions are supported. With the 1.1 (and higher) revisions, message fragmentation is possible and 1.2 specifically allows interleaving of fragments with the 1.2 defined fragment header containing a request id. The implementation details of the various protocol revisions are contained within the GIOP plug-in and are not exposed to the message interceptor interfaces. Instead, marshalled data is represented as basic octet sequences which are passed through the message interceptor chain. The present invention is described with an assumption of knowledge of the CORBA 2.3 GIOP specification for message formats, CDR coding rules and general connection semantics. The following are some features provided in the ART GIOP plug-in design of the present invention: facilitate the needs of the ART core Binding and Interceptor interfaces fits cleanly into the Request level interceptor design support for multiple protocol versions connection sharing between multiple client bindings allow a protocol stack of straight GIOP and GIOP derived protocols. efficient connection establishment and management general access to the transport plug-in from services 1. Design Overview (1) Message Interceptors Client side bindings are established with target objects through the ART Binding interface which establishes a chain of request and message-level interceptors to represent a binding, or channel of communication between client and server. The Binding interface described above is primarily directed to request level interceptors while this section describes message-level, GIOP protocol specific, interceptors. A protocol stack is related to the message interceptors instantiated for a given binding. Message interceptors are described in terms of protocols and connections. For example, GIOP and SECIOP message interceptors control the inter-ORB protocol semantics and syntax for message transmission while the IIOP message interceptor controls both the physical TCP/IP connection and the multiplexing of incoming messages to GIOP or SECIOP message interceptors. There is a one-to-many relationship between the connection interceptors and SECIOP/GIOP interceptors, and between SECIOP and GIOP interceptors, so that multiple bindings may make use of the same connection with a unique set of participating interceptors for each. Message interceptors are preferably created using a bottom-up binding model. Interceptors closest to the connection are created first followed by each subsequent interceptor towards the request level interceptor. The immediate advantage of this model is that connections are established prior to the marshalling and attempted transmission of request data. Data is passed between message interceptors as (raw) data buffers, typically containing a complete GIOP message or message fragment possibly encapsulated in some higher level protocol (such as SECIOP). Each data block will always contain the 12 byte GIOP header structure including magic string and message size in fixed locations (note also that the SEC-IIOP header is designed to match the GIOP header in this way). Buffer management is provided by the interceptor closest to the network and is described in a later section. (2) Message Interceptors and Factories The message interceptors are constructed and assembled into chains in a similar way to the request interceptors during the binding process. The ART_GIOP module defines IDL interfaces derived from the ART_Binding interfaces as follows:
module ART_GIOP
(
typedef sequence<octet> Data;
interface ClientMessageInterceptor
: ART_Binding::ClientInterceptor
readonly attribute request_Id;
enum BlockState
(
BeginBlock, EndBlock, IgnoreBlock
);
send_message(
in ClientMessageInterceptor prev_interceptor
in Data message_data,
in BlockState block_state
);
push_message (
inout Data message_data
);
void
register_interceptor (
in Client MessageInterceptor interceptor
);
void
unregister_interceptor(
in Client MessageInterceptor interceptor
);
boolean
version(
in ART_IOR::ObjectKeyProfile profile,
out GIOP::Version giop_version
);
);
);
The ClientMessageInterceptor interface is in fact used for both client and server side binding chains. Beyond the IA release this interface will be simply a MessageInterceptor. The basic data type ART_GIOP::Data is used to pass message data between message interceptors as a sequence of octets. There is no requirement for any specific message interceptor to have knowledge of the message content represented in the sequence data. The request_id attribute holds the next unique message identifier which is allocated and returned by the connection level interceptor with each access to the value. These values are unique per connection and protocol (magic) and used to match request messages to corresponding replies. A BlockState is also defined primarily for 1.1 fragmentation support. With 1.1 GIOP, fragments do not define a fragment header containing a request id which prevents fragments from multiple Request/Reply messages being interleaved over a single connection. A block state is maintained by the connection interceptor and threads attempting to send a 1.1 fragmented message first sets the state to BeginBlock in order to obtain a logical block on the connection. Other 1.1 fragmented messages will be blocked until the state is set to EndBlock indicating the final fragment has been sent and the lock released. Messages which are non fragmented, or any GIOP messages other than 1.1 fragments, will specify an IgnoreBlock state allowing these to be freely intermixed with the 1.1 fragments. The behavior for non blocking invocations will require the queuing of 1.1 fragmented messages awaiting the release of the block state. Future QoS requirements will also effect the behavior for high priority fragmented messages. The current thread holding a 1.1 fragmentation block may be canceled via a CancelRequest message and the blocked state given to the higher priority thread. (a) GIOP and IIOP Versions The CORBA GIOP version is determined from the version of the transport profile used for the binding. The ART_GIOP::ClientMessageInterceptor::version( ) operation is provided in order for the connection interceptor to indicate the GIOP version relevant to the transport specific profile. Intermediate message interceptors, such as a secure IOP (SECIOP) interceptor, also make use of the GIOP version determined by the connection interceptor to map this to it's own protocol version. (b) Factory Implementations The transport plug-ins provide direct implementations of the ART_Binding::ClientInterceptorFactory interface. The ART_BindingManager calls each of these factories in turn when creating the message interceptor chain by passing the previously established message interceptor reference to the factory, and obtaining a message interceptor back. Each message interceptor factory's ART_Binding::ClientInterceptorFactory::getinterceptor( ) implementation is called in order to determine if the plug-in will participate in the binding. If so, the factory either creates a new interceptor specific to the policies and profile passed to it, or returns an existing interceptor already matching the policies and profile. If the plug-in will not participate in the binding, the interceptor passed to it will be returned. Since each factory may return a newly created interceptor with each call, there is a one-to-many relationship from the connection interceptors closest to the network up to the message request interceptors. Currently a GIOP interceptor is created only when it's factory does not hold a reference to the previous interceptor passed to it. This results in a one-to-one relationship with no requirement for each interceptor beyond the connection interceptor to de-multiplex messages. The issue is that interceptor defined mutexes are used for all bindings sharing the chain and a new chain established for any bindings requiring additional or alternative inteceptors. To provide maximum scaleability requires the one-to-many relationship described here. The interceptor factories are called by the binding manager in a specific order, from the connection interceptor factory up towards the request level chain. Each interceptor will hold a reference to the next interceptor (passed to it through get_interceptor( )) in the chain (closer to the network) although a link is required in the reverse direction for passing incoming message data up the chain. For this reason, the ART_GIOP::ClientMessageInterceptor::register interceptor operation is defined. When the plug-in's factory first creates each interceptor, the interceptor stores the references to the previous interceptor and then registers itself with the previous interceptor. The previous interceptor maintains a table of references for each interceptor linked to it. Similarly, the interceptor preferably invokes the unregister interceptor( ) operation when the last reference to the interceptor is released either because all bindings are closed for the open connection or as part of a plug-in shutdown sequence. The interceptor may also un-register itself during the initial binding phase. Within the GIOP plug-in, for example, with each new binding the message interceptor factory first verifies the target object is reachable by sending a LocateRequest message to the server. If the reply results in a location forward or the object is unknown to the server, the reference count for the interceptor is released by the factory and the LOCATION_FORWARD or UNKNOWN_OBJECT exception returned to the binding manager. If no other references to the interceptor are held, the interceptor will un-register itself with it's factory and then be destroyed. (c) Message Interceptors If present in the binding, multiple message interceptors may be placed between the GIOP interceptor and the ConnectionInterceptor participating in the binding. The message interceptor may play various roles in the binding chain such as monitoring of data, logging activities, message snooping etc. They may also implement other protocols on top of the GIOP protocol such as a SECIOP message interceptor which encrypts GIOP messages within SECP messages in order to implement message level secure HOP connections. In this way, the ART GIOP design provides for flexible protocol stacks represented as interceptor chains. (d) Message Request Interceptors
module ART_GIOP
(
interface MessageRequestInterceptor
: ClientMessageInterceptor,
ART_Binding::ClientRequestInterceptor
(
);
);
The GIOP plug-in defines an implementation of the ART_GIOP::MessageRequestInterceptor and is the outer most of the message-level interceptors. It controls GIOP message creation and processing, and fragmentation as defined for GIOP versions 1.1 and higher. Object location (via LocateRequest/LocateReply messages) is also handled by the GIOP message interceptor, in conjunction with the ART core so that the Binding and IOR_Proxy interfaces, also involved in object location, may forward the client to a location through another (possibly non-GIOP) protocol. The GIOP interceptor translates request level data passed through calls to invoke( ), send async( ) and send_deferred( ) operations, defined by the ART_Binding::ClientRequestInterceptor, to GIOP message data transferred via the message interceptor chain. The invoke( ) operation marshals ClientRequest data including Request parameters and service contexts into CDR encoded buffers which is then transferred between message interceptors via the ART_GIOP::ClientMessageInterceptor::send_message( ) operation. (e) Connection Interceptors In ART terms, a ConnectionInterceptor is described as a transport message interceptor which implements a specific transport mapping of the GIOP protocol. The IIOP plug-in is the only defined GIOP mapping currently defined by CORBA for TCP/IP based inter-ORB communications.
module ART GIOP
(
interface MessageRequestInterceptor
: ClientMessageInterceptor,
ART_EventHandler
(
);
);
An ART_GIOP::ConnectionInterceptor implementation maintains an open connection over which protocol specific messages are transmitted. Event handling and de-multiplexing and message dispatch of incoming messages is also controlled by the interceptor. Only an IIOP message interceptor is described here, although any connection oriented transport could be mapped to GIOP and a corresponding connection interceptor defined. For example, ART support for Asynchronous Transfer Mode (ATM), Signaling System number 7 (SS7) and other transports/protocols as well as a specialized shared memory IIOP plug-in implementation is contemplated within this invention. Alternative transport protocol implementations can also be defined such as for IIOP, where a generic socket layer is required to implement both standard socket communications and specific SSL support. Input Handler The ART_EventHandler interface defines general operations which are handlers implemented by the connection interceptor, including handle_input, handle_exception_handle_close and so on. The connection interceptors are free to adopt any event handling model although ART defines a default ART_Reactor implementation which the C++ based plug-ins implement, while the java implementations make use of a proactor. Note that in order to provide a consistent model for all native ART ORB implementations, it is likely that the proactor model be adopted for the C++ based ORB. For the current ART implementation, the Reactor pattern is assumed. As mentioned, implementations of an ART_GIOP::ConnectionInterceptor act both as a message interceptor and an event handler for the connection by providing implementations of the derived ART_EventHandler operations. For the client side binding, a message interceptor chain is created with the connection message interceptor being registered with the event handler. When incoming message data arrives, the event handler fires and a call made to the handle_input method for the interceptor instance responsible for the connection handle (open socket in the case of the IIOP interceptor). Once the incoming data has been read, the message is demultiplexed based on the magic string and passed to the relevant interceptor chain via the push_message operation defined by the ART_GIOP::MessageInterceptor interface. The server side implementation is similar to the event handling mechanism used by the client for established connections. The difference is an additional listen handler which initiates the creation of the message interceptor chain with each new connection accepted from a client. The IIOP plug-in implements a ART_IIOPListenHandler object derived from the ART_EventHandler interface and registers an instance of the object with the event handler for each published IOR transport profile. When the handle_input method is called for the listen handler, the connection is accepted and a new IIOP ConnectionInterceptor created for the binding. The new interceptor is then registered with the event handler to allow handling of incoming message data in a similar way as described for the client. The listen handler then returns to the event handler and is activated with subsequent connection attempts. A general requirement is that ART_EventHandler implementations are non-blocking in nature when using the default ART_Reactor event handler. When the reactor fires, the registered handler is activated within the reactors thread and performs non blocking reads of the message data so that no delay occurs in returning control to the reactor in order to process any events for other connections. As such, the handler may be activated multiple times for each incoming message before passing the complete message to the message interceptor chain. A complete message in this sense is the GIOP header and message size bytes making up a GIOP message fragment. The same requirement applies at this point and the push message call remains non-blocking by avoiding any processing of the message data. The message is instead placed on a queue and processed within a work queue thread. (f) Buffer and Memory Management The GIOP plug-in provides implementations of the ART_CDR:InStream and OutStream interfaces for marshalling GIOP message data. Allocated buffer space is used within the GIOP streams implementations for containing marshalled message data which is passed as raw message data between message interceptors involved in the binding. Buffer management is required to allocate, manage and free buffer resources and is controlled by the connection interceptor implementation. In this way, transport specific requirements determine the allocation and management requirements for buffer space used by all interceptors in the chain. The buffer allocation and de-allocation calls defined by the ART_GIOP follows:
module ART_GIOP
(
typedef sequence<octet> Data;
interface ClientMessageInterceptor
: ART_Binding::ClientInterceptor
(
Data
alloc_buffer (in unsigned long minimum_size);
Data
realloc_buffer(
in Data buffer, in unsigned long minimum_size
);
void
free_buffer (in Data buffer);
)
)
When message data is read from the connection it is placed in a buffer allocated by the connection interceptor implementation. The IIOP plug-in uses the default sequence allocbuf method defined in the sequence support code implemented by the core. Once a complete message fragment has been read the memory buffer is used to create an ART_GIOP::Data sequence which is then passed up the interceptor chain. Buffer ownership rules are those defined by the C++ sequence mapping and determined by the plug-in. If the sequence is created as releasable (specifying true as the release parameter passed to the constructor), a subsequent interceptor may take ownership of the raw sequence buffer by a get_buffer(true) call which returns a pointer to the buffer and releases ownership to the caller. This is pos | ||||||
