Portable Interceptors (PI)


If you downloaded this document as part of the J2SE TM documentation bundle, check the Web site at http://java.sun.com/j2se/1.4/docs/guide/idl/PI.html for updates.

NOTICE: This document is intended for advanced CORBA developers.

The JavaTM CORBA Object Request Broker (ORB) provides hooks, or interception points, through which ORB services can intercept the normal flow of execution of the ORB. These Portable Interceptors provide a mechanism for plugging in additional ORB behavior, or, by modifying the communications between client and server, for modifying the behavior of the ORB. The example that follows shows different ways of using Portable Interceptors.

Support for Portable Interceptors is a major recent addition to the CORBA specification. Using RequestInterceptors, one can easily write and attach portable ORB hooks that will intercept any ORB-mediated invocation. Using IORInterceptors, one can write code to annotate CORBA object references.

Interceptor Types

There are currently three types of interceptors that can be registered, as shown below. Examples of each are shown in the example that follows.

Registering ORB Initializers in Java

The class ORBInitializer facilitates interceptor registration and ORB initialization.

Interceptors are intended to be a means by which ORB services gain access to ORB processing, effectively becoming part of the ORB. Since interceptors are part of the ORB, when ORB.init returns an ORB, the interceptors shall have been registered. Interceptors cannot be registered on an ORB after it has been returned by a call to ORB.init.

ORBInitializers are registered via Java ORB properties. An interceptor is registered by registering an associated ORBInitializer object which implements the ORBInitializer interface. When an ORB is initializing, it shall call each registered ORBInitializer, passing it an ORBInitInfo object which is used to register its interceptor.

The property names are of the form:

org.omg.PortableInterceptor.ORBInitializerClass.<Service>
where <Service> is the string name of a class which implements
org.omg.PortableInterceptor.ORBInitializer
To avoid name collisions, the reverse DNS name convention should be used. For example, if company X has three initializers, it could define the following properties: During ORB.init, these ORB properties which begin with org.omg.PortableInterceptor.ORBInitializerClass shall be collected, the <Service> portion of each property shall be extracted, an object shall be instantiated with the <Service> string as its class name, and the pre_init and post_init methods shall be called on that object. If there are any exceptions, the ORB shall ignore them and proceed.

Notes About the Scope of Interceptors

Notes about Registering Interceptors

Portable Interceptor Current (PICurrent)

The PortableInterceptor::Current object (hereafter referred to as PICurrent) is a Current object that is used specifically by portable Interceptors to transfer thread context information to a request context. Portable Interceptors are not required to use PICurrent, but if information from a client's thread context is required at an Interceptor's interception points, then PICurrent can be used to propagate that information. PICurrent allows portable service code to be written regardless of an ORB's threading model.

On the client side, this information includes, but is not limited to, thread context information that shall be propagated to the server via a service context.

On the server side, this information includes, but is not limited to, service context information received from the client which is propagated to the target's thread context.

Obtaining PICurrent

Before an invocation is made, PICurrent is obtained via a call to ORB::resolve_initial_references ( PICurrent ). From within the interception points, the data on PICurrent that has moved from the thread scope to the request scope is available via the get_slot operation on the RequestInfo object. A PICurrent can still be obtained via resolve_initial_references, but that is the Interceptor's thread scope PICurrent.

Request Scope vs Thread Scope

The thread scope PICurrent (TSC) is the PICurrent that exists within a thread's context. A request scope PICurrent (RSC) is the PICurrent associated with the request. On the client-side, the thread scope PICurrent is logically copied to the request scope PICurrent from the thread s context when a request begins and is attached to the ClientRequestInfo object. On the server-side, the request scope PICurrent is attached to the ServerRequestInfo and follows the request processing. It is logically copied to the thread scope PICurrent after the list of receive_request_service_contexts interception points are processed. See the Updated Interceptors specification, Section, 21.4.4.5, Flow of PICurrent between Scopes for a detailed discussion of the scope of PICurrent.


Portable Interceptor Example: a Logging Service

This section contains an example logging service application. The sample code for this application is quite complicated because it covers even subtle "corner cases". The following scenarios are covered in this sample application:

  1. A logging service which logs invocations. Neither client nor server explicitly use the logging service.

  2. An "empty" service which passes information from the client to the server. The client and server explicitly use this service but are not aware that it is implemented using interceptors.

Note: These examples explicitly register ORBInitializers to make the code easier to experiment with and setup. Typically this would not be done. Instead, this information would be passed as -D properties to the Java virtual machine when the applications are started. That way the applications are not coupled to the fact that either the service exists (e.g., the logging service) or that a service that they explicitly use (e.g., the AService interface) is implemented as an interceptor. For example, to run a program called MyApp using this logging service, the user could type:

   java -Dorg.omg.PortableInterceptor.ORBInitializerClass.com.x.Logging.LoggingService 
     MyApp

The example application shows how to use interceptors to create a logging service application, as follows:

  1. The logging service makes simple client and server-side outcalls.

    The following diagram shows the flow of information in the logging service:

    Logging Service diagram

  2. The colocated logging service makes a complicated server-side outcall when the target is colocated with the the interceptor.

    The following diagram shows the flow of information in the colocated logging service:

    Colocated Logging Service diagram

  3. The AService uses register_initial_references, resolve_initial_references, PICurrent, and ServiceContexts to pass service information between client code and server code.

    The following diagram shows the flow of information in AService:

    AService diagram

If you would like to create and run this application, enter the code as shown in the following sections, then follow the instructions in Compiling and Running the Application.


serviceexample.idl

This is the Interface Definition Language (IDL) file that contains definitions of an arbitrary object on which to make invocations and two services which will service calls to the arbitrary object.


LoggingServiceClientORBInitializer.java

This file creates and registers the logging service interceptor used by object clients.


LoggingServiceClientInterceptor.java

This interceptor logs client side interception points. It illustrates how to make invocations on other objects from within an interceptor and how to avoid infinite recursion during those "outcall" invocations.


LoggingServiceServerORBInitializer.java

This file creates and registers the logging service interceptor used by object servers. Even though this interceptor is meant to only log server-side interception points, it is both a client and server interceptor. The code illustrates how to avoid infinite recursion in the case where the object being called is colocated in the same ORB in which the interceptors are registered.


LoggingServiceServerInterceptor.java

This interceptor logs server side interception points, and is implemented as both a ClientRequestInterceptor and a ServerRequestInterceptor to illustrate the need to set some "out call" service context data (in addition to setting an out call slot) to avoid infinite recursion for the case noted in the description of LoggingServiceServerORBInitializer.java.


LoggingServiceImpl.java

This file is the implementation of the logging object to which the logging service interceptors send their data.


AServiceORBInitializer.java

The file creates and registers the AServiceInterceptor as both a client and server interceptor, creates and registers the AServiceIORInterceptor which puts a TaggedComponent into IORs, and creates a local object to be used by applications to explicitly control the services offered by AService. This local object is registered as an initial service with the ORB such that it can be obtained by clients using org.omg.CORBA.ORB.resolve_initial_references.


AServiceImpl.java

This file contains an object that is explicitly used by client and servant code to communicate with the service. When the client calls begin() on the service, the service causes a ServiceID specific to the service to be put in the TSC PICurrent slot reserved by this service. When the client calls end() on the service, it sets the TSC slot to a non-valid value to indicate that the service is not in effect. Servants use the verify() method to determine whether the ServiceID was passed from the client side to the server side.


AServiceInterceptor.java

This interceptor is responsible for arranging to pass the client side AService information to the service side.

On the client side, if AService.begin() has been called, the send_request(ri) point will see the service id in the RSC slot. In this case, it inserts the value of that service id into an org.omg.CORBA.ServiceContext and adds that service context to the data to be passed along with the invocation.

On the server side, receive_request_service_context(ri) looks for the presence of that service context. When present, it extracts the service id value from the ServiceContext and sets the RSC slot to that value. When the servant is executing, the value of that RSC slot is available in the TSC slot.


AServiceIORInterceptor.java

This file adds a TaggedComponent to IORs.


ArbitaryObjectImpl.java

This file is an implementation and server for the ArbitraryObject IDL interface. The implementations of the IDL interface operations explicitly call the AServiceImpl.verify() method to illustrate the end-to-end passing of data from the client (via AService.begin()) to the servant.


Client.java

This is a client that calls methods on ArbitraryObject. It makes some of those calls within the context of AService and some outside of its context. It is unaware of the existence of the logging interceptor (except that it explicitly registers the LoggingServerClientORBInitializer as noted above).


ColocatedServers.java

This is a server which runs both ArbitraryObject and LoggingService in the same ORB. This means that these objects are colocated.

The server is created this way in order to exercise the code in LoggingServiceServerInterceptor that illustrates when interceptors make out calls to objects colocated in the same ORB extra steps must be taken to avoid infinite recursion.


Compiling and running the application

To compile and run this application,
.
.
.
.
.
.

See Also:
ORBInitInfo
org.omg.PortableInterceptor package.


Send questions or comments to: JavaIDL@eng.sun.com.