VisiBroker for Java Developer’s Guide : Using VisiBroker Interceptors

Using VisiBroker Interceptors
This section provides an overview of the VisiBroker Interceptors (Interceptors) framework, walks through a Interceptor example, and describes some advanced features such as Interceptor factories and chaining Interceptors. This section also covers the expected behaviors when both Portable Interceptors and VisiBroker Interceptors are used in the same service.
Interceptors overview
Similar to Portable Interceptors, VisiBroker Interceptors offers VisiBroker ORB services a mechanism to intercept normal flow of execution of the ORB. There are two kinds of VisiBroker Interceptors:
Client Interceptors are system-level Interceptors which are called when a method is invoked on a client object.
Server Interceptors are system-level Interceptors which are called when a method is invoked on a server object.
To use VisiBroker Interceptors, you declare a class which implements one of the Interceptor interfaces. Once you have instantiated an Interceptor object, you register it with its corresponding Interceptor manager. Your Interceptor object is then notified by its manager when, for example, an object has had one of its methods invoked or its parameters marshaled or demarshaled.
An important difference between VisiBroker interceptors and Portable interceptors is that VisiBroker interceptors do not get invoked for co-located calls. Therefore, users have to make a cautious decision when choosing which interceptor to use.
Note
If you want to intercept an operation request before it is marshal on the client side or if you want to intercept an operation request before it is processed on the server side, use object wrappers, described in “Using object wrappers”.
Interceptor interfaces and managers
Interceptor developers derive classes from one or more of thle following base Interceptor API classes which are defined and implemented by the VisiBroker.
Client Interceptors
There are currently two kinds of client Interceptor and their respective managers:
BindInterceptor and BindInterceptorManager
ClientRequestInterceptor and ClientRequestInterceptorManager
For more details about client Interceptors, see “Using Portable Interceptors”.
BindInterceptor
A BindInterceptor object is a global Interceptor which is called on the client side before and after binds.
package com.inprise.vbroker.InterceptorExt;
public interface BindInterceptor {
public IORValue bind(IORValue ior,
org.omg.CORBA.Object target,
boolean rebind,
Closure closure);
public IORValue bind_failed(IORValue ior,
org.omg.CORBA.Object target,
Closure closure);
public void bind_succeeded(IORValue ior,
org.omg.CORBA.Object target,
int Index,
InterceptorManagerControl control,
Closure closure);
public void exception_occurred(IORValue ior,
org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure);
}
ClientRequestInterceptor
A ClientRequestInterceptor object may be registered during a bind_succeeded call of a BindInterceptor object, and it remains active for the duration of the connection. Two of its methods are called before the invocation on the client object, one (preinvoke_premarshal) before the parameters are marshaled and the other (preinvoke_postmarshal) after they are. The third method (postinvoke) is called after the request has completed.
package com.inprise.vbroker.InterceptorExt;
public interface ClientRequestInterceptor {
public void preinvoke_premarshal(org.omg.CORBA.Object target,
String operation,
ServiceContextListHolder service_contexts_holder,
Closure closure);
public void preinvoke_postmarshal(org.omg.CORBA.Object target,
OutputStream payload,
Closure closure);
public void postinvoke(org.omg.CORBA.Object target,
ServiceContext[] service_contexts,
InputStream payload,
org.omg.CORBA.Environment env,
Closure closure);
public void exception_occurred(org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure);
}
Server Interceptors
There are the following kinds of server Interceptors:
POALifeCycleInterceptor and POALifeCycleInterceptorManager
ActiveObjectLifeCycleInterceptor and ActiveObjectLifeCycleInterceptorManager
ServerRequestInterceptor and ServerRequestInterceptorManager
IORCreationInterceptor and IORCreationInterceptorManager
For more details about server Interceptors see “Using Portable Interceptors”.
POALifeCycleInterceptor
A POALifeCycleInterceptor object is a global Interceptor which is called every time a POA is created (using the create method) or destroyed (using the destroy method).
package com.inprise.vbroker.InterceptorExt;
public interface POALifeCycleInterceptor {
public void create(org.omg.PortableServer.POA poa,
org.omg.CORBA.PolicyListHolder policies_holder,
IORValueHolder iorTemplate,
InterceptorManagerControl control) ;
public void destroy(org.omg.PortableServer.POA poa);
}
ActiveObjectLifeCycleInterceptor
An ActiveObjectLifeCycleInterceptor object is called whenever an object is added to the Active Object Map (using the create method) or after an object has been deactivated and etherealized (using the destroy method). The Interceptor may be registered by a POALifeCycleInterceptor on a per-POA basis at POA creation time. This Interceptor may only be registered if the POA has the RETAIN policy.
package com.inprise.vbroker.InterceptorExt;
public interface ActiveObjectLifeCycleInterceptor {
public void create(byte[] oid,
org.omg.PortableServer.Servant servant,
org.omg.PortableServer.POA adapter);
public void destroy (byte[] oid,
org.omg.PortableServer.Servant servant,
org.omg.PortableServer.POA adapter);
}
ServerRequestInterceptor
A ServerRequestInterceptor object is called at various stages in the invocation of a server implementation of a remote object before the invocation (using the preinvoke method) and after the invocation both before and after the marshaling of the reply (using the postinvoke_premarshal and postinvoke_premarshal methods respectively). This Interceptor may be registered by a POALifeCycleInterceptor object at POA creation time on a per-POA basis.
package com.inprise.vbroker.InterceptorExt;
public interface ServerRequestInterceptor {
public void preinvoke(org.omg.CORBA.Object target,
String operation,
ServiceContext[] service_contexts,
InputStream payload,
Closure closure);
public void postinvoke_premarshal(org.omg.CORBA.Object target,
ServiceContextListHolder service_contexts_holder,
org.omg.CORBA.Environment env,
Closure closure);
public void postinvoke_postmarshal(org.omg.CORBA.Object target,
OutputStream payload,
Closure closure);
public void exception_occurred(org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure);
}
Note
If an org.omg.CORBA.SystemException or any sub-classes (for example org.omg.CORBA.NO_PERMISSION) is raised on the server side, the exception should not be encrypted. This is because the ORB uses some of these exceptions internally (for example TRANSIENT for doing automatic rebind).
IORCreationInterceptor
An IORCreationInterceptor object is called whenever a POA creates an object reference (using the create method). This Interceptor may be registered by a POALifeCycleInterceptor at POA creation time on a per-POA basis.
package com.inprise.vbroker.InterceptorExt;
public interface IORCreationInterceptor {
public void create(org.omg.PortableServer.POA poa,
IORValueHolder ior);
}
Service Resolver Interceptor
This Interceptor is used to install a user service that you can then dynamically load.
public interface ServiceResolverInterceptor {
public org.omg.CORBA.Object resolve (java.lang.String name):
}
public interface ServiceResolverInterceptorManager extends
com.inprise.vbroker.interceptor.InterceptorManager {
public void add (java.lang.String name,
com.inprise.vbroker.interceptor.ServiceResolverInterceptor \interceptor) ;
pubic void remove (java.lang.String name):
}
When you call resolve_initial_references, the resolve on all installed services gets called. The resolve then can return the appropriate object.
To write service initializers, you must obtain a ServiceResolver after getting an InterceptorManagerControl to be able to add your services.
Default Interceptor classes
VisiBroker provides default Interceptor Java classes that you can extend and implement. These default Interceptor classes offer the same methods as the Interceptor interfaces; however, when you extend the default Interceptor class, you can choose which methods to implement or override. When you use these classes, you can accept the default behavior that they provide or change it.
Registering Interceptors with the VisiBroker ORB
Each Interceptor interface has a corresponding Interceptor manager interface which is used to register your Interceptor objects with the VisiBroker ORB. The following steps are necessary to register an Interceptor:
1
Get a reference to an InterceptorManagerControl object by calling the resolve_initial_references method on an ORB object with the parameter VisiBrokerInterceptorControl.
2
Call the get_manager method on the InterceptorManagerControl object with one of the String values in the following table which shows the String values to pass to the get_manager method of the InterceptorManagerControl object. (Be sure to cast the object reference to its corresponding Interceptor manager interface.)
3
4
5
Creating Interceptor objects
Finally, you need to implement a factory class which creates instances of your Interceptors and registers them with the VisiBroker ORB. Your factory class must or implement the ServiceLoader interface.
package com.inprise.vbroker.interceptor;
public interface ServiceLoader {
// This method is called by the ORB when ORB.init() is called.
public abstract void init(org.omg.CORBA.ORB orb);
// Called after ORB.init() is done but control hasn't been returned to
// the user. Can be used to disable certain resources that were only
// made available to other service inits.
public abstract void init_complete(org.omg.CORBA.ORB orb);
// Called when the orb is being shutdown.
public abstract void shutdown(org.omg.CORBA.ORB orb);
}
Note
You can also create new instances of your Interceptors and register them with the VisiBroker ORB from within other Interceptors as in the examples in “Example Interceptors”.
Loading Interceptors
To load your Interceptor, you must set the vbroker.orb.dynamicLibs property. This property can be set either in the properties file (see “Using VisiBroker Interceptors”) or be passed into the VisiBroker ORB using the -D option.
Example Interceptors
The example Interceptor in this section uses all of the Interceptor API methods (listed in “Using Portable Interceptors”) so that you can see how these methods are used, and when they are invoked.
Example code
In “Code listings”, each of the Interceptor API methods is simply implemented to print informational messages to the standard output.
The following example applications are located in the directory:
<install_dir>\examples\vbroker\interceptors\
Client-server Interceptors example
To run the example, compile the files as you normally would. Then start up the server and the client as follows:
prompt>vbj -Dvbroker.orb.dynamicLibs=SampleServerLoader Server
prompt>vbj -Dvbroker.orb.dynamicLibs=SampleClientLoader Client John
You specify as VisiBroker ORB services the two classes which implement the ServiceLoader interface.
Note
The ServiceInit class used in VisiBroker 3.x is replaced by implementing two interfaces: ServiceLoader and ServiceResolverInterceptor. For an example of how to do this, see “ServiceResolverInterceptor example”.
 
The results of executing the example Interceptor are shown in the following table. The execution by the client and server is listed in sequence.
Since the OAD is not running, the bind call fails and the server proceeds. The client binds to the account object, and then calls the balance method. This request is received by the server, processed, and results are returned to the client. The client prints the results.
As demonstrated by the example code and results, the Interceptors for both the client and server are installed when the respective process starts. Information about registering an interceptor is covered in “Registering Interceptors with the VisiBroker ORB”.
ServiceResolverInterceptor example
The following code provides an example of how to implement a ServiceLoader interface:
import com.inprise.vbroker.properties.*;
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.InterceptorExt.*;

public final class UtilityServiceLoader implements ServiceLoader,
ServiceResolverInterceptor {
private com.inprise.vbroker.orb.ORB _orb = null;
private String[] _serviceNames = { "TimeService", "WeatherService"};

public void init(org.omg.CORBA.ORB orb) {
// Just in case they are needed by resolve()
_orb = (com.inprise.vbroker.orb.ORB) orb;

PropertyManager pm = _orb.getPropertyManager();
// use the PropertyManager to query property settings
// if needed (not used in this example)

/**** Installing the Initial Reference *****/
InterceptorManagerControl control = _orb.interceptorManager();
ServiceResolverInterceptorManager manager =
(ServiceResolverInterceptorManager)control.get_manager("ServiceResolver");
for (int i = 0; i < _serviceNames.length; i++) {
manager.add(_serviceNames[i], this);
}
/**** end of installation ***/

if (_orb.debug)
_orb.println("UtilityServices package has been initialized");
}

public void init_complete(org.omg.CORBA.ORB orb) {
// can be used for post-initialization processing if desired
}

public void shutdown(org.omg.CORBA.ORB orb) {
_orb = null;
_serviceNames = null;
}

public org.omg.CORBA.Object resolve(java.lang.String service) {
org.omg.CORBA.Object srv = null;
byte[] serviceId = service.getBytes();
try {
if (service == "TimeService") {
srv = UtilityServices.TimeServiceHelper.bind(_orb, "/time_service_poa", serviceId);
}
else if (service == "WeatherService") {
srv = UtilityServices.WeatherServiceHelper.bind(_orb,"/weather_service_poa",
serviceId);
}
} catch (org.omg.CORBA.SystemException e) {
if (_orb.debug)
_orb.println("UtilityServices package resolve error: " + e);
srv = null;
}

return srv;
}
}
Code listings
SampleServerLoader
The SampleServerLoader object is responsible for loading the POALifeCycleInterceptor class and instantiating an object. This class is linked to the VisiBroker ORB dynamically by vbroker.orb.dynamicLibs. The SampleServerLoader class contains the init method which is called by the VisiBroker ORB during initialization. Its sole purpose is to install a POALifeCycleInterceptor object by creating it and registering it with the InterceptorManager.
import java.util.*;
import com.inprise.vbroker.orb.*;
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.PortableServerExt.*;
public class SampleServerLoader implements ServiceLoader {
public void init(org.omg.CORBA.ORB orb) {
try {
InterceptorManagerControl control =
InterceptorManagerControlHelper.narrow(
orb.resolve_initial_references("VisiBrokerInterceptorControl"));
// Install a POA interceptor
POALifeCycleInterceptorManager poa_manager =
(POALifeCycleInterceptorManager) control.get_manager("POALifeCycle");
poa_manager.add(new SamplePOALifeCycleInterceptor());
} catch(Exception e) {
e.printStackTrace();
throw new org.omg.CORBA.INITIALIZE(e.toString());
}
System.out.println("============>SampleServerLoader:Interceptors loaded");
}
public void init_complete(org.omg.CORBA.ORB orb) {
}
public void shutdown(org.omg.CORBA.ORB orb) {
}
}
SamplePOALifeCycleInterceptor
The SamplePOALifeCycleInterceptor object is invoked every time a POA is created or destroyed. Because we have two POAs in the client_server example, this Interceptor is invoked twice, first during rootPoa creation and then at the creation of myPOA. We install the SampleServerInterceptor only at the creation of myPOA.
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.PortableServerExt.*;
import com.inprise.vbroker.IOP.*;
public class SamplePOALifeCycleInterceptor implements POALifeCycleInterceptor {
public void create(org.omg.PortableServer.POA poa,
org.omg.CORBA.PolicyListHolder policies_holder,
IORValueHolder iorTemplate,
InterceptorManagerControl control) {
if(poa.the_name().equals("bank_agent_poa")) {
// Add the Request-level interceptor
SampleServerInterceptor interceptor =
new SampleServerInterceptor("MyServerInterceptor");
// Get the IORCreation interceptor manager
ServerRequestInterceptorManager manager =
(ServerRequestInterceptorManager)control.get_manager
("ServerRequest");
// Add the interceptor
manager.add(interceptor);
System.out.println("============>In POA " + poa.the_name() +
", 1 ServerRequest interceptor installed");
} else
System.out.println("============>In POA " + poa.the_name
() + ". Nothing to do.");
}
public void destroy(org.omg.PortableServer.POA poa) {
// To be a trace!
System.out.println("============> SamplePOALifeCycleInterceptor
destroy");
}
}
SampleServerInterceptor
The SampleServerInterceptor object is invoked every time a request is received at or a reply is made by the server.
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.IOP.*;
import com.inprise.vbroker.CORBA.portable.*;
public class SampleServerInterceptor implements ServerRequestInterceptor {
private String _id;
public SampleServerInterceptor(String id) {
_id = id;
}
public void preinvoke(org.omg.CORBA.Object target,
String operation,
ServiceContext[] service_contexts,
InputStream payload,
Closure closure) {
// Put the _id of this ServerRequestInterceptor into the closure object
closure.object = new String(_id);
System.out.println("============> SampleServerInterceptor id " +
closure.object + " preinvoke => " + operation);
}
public void postinvoke_premarshal(org.omg.CORBA.Object target,
ServiceContextListHolder service_contexts_holder,
org.omg.CORBA.Environment env,
Closure closure) {
System.out.println("============> SampleServerInterceptor id " +
closure.object + " postinvoke_premarshal");
}
public void postinvoke_postmarshal(org.omg.CORBA.Object target,
OutputStream payload,
Closure closure) {
System.out.println("============> SampleServerInterceptor id " +
closure.object + " postinvoke_postmarshal");
}
public void exception_occurred(org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure) {
System.out.println("============> SampleServerInterceptor id " +
closure.object + " exception_occurred");
}
}
SampleClientInterceptor
The SampleClientInterceptor is invoked every time a request is made by or a reply is received at the client.
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.IOP.*;
import com.inprise.vbroker.CORBA.portable.*;
public class SampleClientInterceptor implements ClientRequestInterceptor {
private String _id;
public SampleClientInterceptor(String id) {
_id = id;
}
public void preinvoke_premarshal(org.omg.CORBA.Object target,
String operation,
ServiceContextListHolder service_contexts_holder,
Closure closure) {
// Put the _id of this ClientRequestInterceptor into the closure object
closure.object = new String(_id);
System.out.println("============> SampleClientInterceptor id " +
closure.object +
" preinvoke_premarshal => " + operation);
}
public void preinvoke_postmarshal(org.omg.CORBA.Object target,
OutputStream payload,
Closure closure) {
System.out.println("============> SampleClientInterceptor id " +
closure.object + " preinvoke_postmarshal");
}
public void postinvoke(org.omg.CORBA.Object target,
ServiceContext[] service_contexts,
InputStream payload,
org.omg.CORBA.Environment env,
Closure closure) {
System.out.println("============> SampleClientInterceptor id " +
closure.object + " postinvoke");
}
public void exception_occurred(org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure) {
System.out.println("============> SampleClientInterceptor id " +
closure.object + " exception_occurred");
}
}
SampleClientLoader
The SampleClientLoader is responsible for loading BindInterceptor objects. This class is linked to the VisiBroker ORB dynamically by vbroker.orb.dynamicLibs. The SampleClientLoader class contains the bind and bind_succeeded methods. These methods are called by the ORB during object binding. When the bind succeeds, bind_succeeded will be called by the ORB and a BindInterceptor object is installed by creating it and registering it the InterceptorManager.
import java.util.*;
import com.inprise.vbroker.orb.*;
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.PortableServerExt.*;
public class SampleClientLoader implements ServiceLoader {
public void init(org.omg.CORBA.ORB orb) {
try {
InterceptorManagerControl control =
InterceptorManagerControlHelper.narrow(
orb.resolve_initial_references("VisiBrokerInterceptorControl"));
BindInterceptorManager bind_manager =
(BindInterceptorManager) control.get_manager("Bind");
bind_manager.add(new SampleBindInterceptor());
} catch(Exception e) {
e.printStackTrace();
throw new org.omg.CORBA.INITIALIZE(e.toString());
}
System.out.println("Bind Interceptors loaded");
}
public void init_complete(org.omg.CORBA.ORB orb) {
}
public void shutdown(org.omg.CORBA.ORB orb) {
}
}
SampleBindInterceptor
The SampleBindInterceptor is invoked when the client attempts to bind to an object. The first step on the client side after ORB initialization is to bind to an AccountManager object. This bind invokes the SampleBindInterceptor and a SampleClientInterceptor is installed when the bind succeeds.
import com.inprise.vbroker.interceptor.*;
import com.inprise.vbroker.IOP.*;
public class SampleBindInterceptor implements BindInterceptor {
public IORValue bind(IORValue ior, org.omg.CORBA.Object target,
boolean rebind, Closure closure) {
// To be a trace!
System.out.println("============> SampleBindInterceptor bind");
return null;
}
public IORValue bind_failed(IORValue ior, org.omg.CORBA.Object target,
Closure closure) {
// To be a trace!
System.out.println("============> SampleBindInterceptor bind_failed");
return null;
}
public void bind_succeeded(IORValue ior, org.omg.CORBA.Object target,
int Index, InterceptorManagerControl control,
Closure closure) {
// To be a trace!
System.out.println("============> SampleBindInterceptor bind_succeeded");
// Create the Client Request interceptor:
SampleClientInterceptor interceptor =
new SampleClientInterceptor("MyClientInterceptor");
// Get the manager
ClientRequestInterceptorManager manager =
      (ClientRequestInterceptorManager)control.get_manager("ClientRequest");
// Add CRQ to the list:
manager.add(interceptor);
}
public void exception_occurred(IORValue ior, org.omg.CORBA.Object target,
org.omg.CORBA.Environment env,
Closure closure) {
// To be a trace!
System.out.println("=========> SampleBindInterceptor exception_occured");
}
}
Passing information between your Interceptors
Closure objects are created by the ORB at the beginning of certain sequences of Interceptor calls. The same Closure object is used for all calls in that particular sequence. The Closure object contains a single public data field object of type java.lang.Object which may be set by the Interceptor to keep state information. The sequences for which Closure objects are created vary depending on the Interceptor type. In the ClientRequestInterceptor, a new Closure is created before calling preinvoke_premarshal and the same Closure is used for that request until the request completes, successfully or not. Likewise, in the ServerInterceptor, a new Closure is created before calling preinvoke, and that Closure is used for all Interceptor calls related to processing that particular request.
For an example of how Closure is used, see the examples in the following directory:
<install_dir>/examples/vbroker/interceptors/client_server
The Closure object can be cast to ExtendedClosure to obtain response_expected and request_id as follows:
int my response_expected =
((ExtendedClosure)closure).reqInfo.response_expected;
int my request_id =
((ExtendedClosure)closure) .reqInfo.request_id;
Using both Portable Interceptors and VisiBroker Interceptors simultaneously
Both Portable Interceptors and VisiBroker Interceptors can be installed simultaneously with the VisiBroker ORB. However, as they have different implementations, there are several rules of flow and constrains that developers need to understand when using both Interceptors, as described in the following.
Order of invocation of interception points
The order of invocation of interception points follows the interception point ordering rules of individual versions of Interceptors, regardless of whether the developer actually chooses to install one of more than one version.
Client side Interceptors
When both Portable Interceptors and VisiBroker client side Interceptors are installed, the order of events, (assuming no Interceptor throws an exception) is:
1
send_request (Portable Interceptor), followed by preinvoke_premarshal (Interceptors)
2
3
preinvoke_postmarshal (Interceptor)
4
5
postinvoke (Interceptor), followed by received_reply/receive_exception/receive_other (Portable Interceptor) depending on the type of reply.
Server side Interceptors
When both Portable Interceptors and VisiBroker server side Interceptors are installed, the order of events is received (locate requests do not fire Interceptors, which is the same as VisiBroker behavior), assuming no Interceptor throws an exception, is:
1
received_request_service_contexts (Portable Interceptor), followed by preinvoke (Interceptor)
2
servantLocator.preinvoke (if using servant locator)
3
receive_request (Portable Interceptor)
4
5
postinvoke_premarshal (Interceptor)
6
servantLocator.postinvoke (if using servant locator)
7
send_reply/send_exception/send_other, depending on the outcome of the request
8
Order of ORB events during POA creation
The order of ORB events during creation of a POA is listed as follows:
1
2
An Interceptors' POA life cycle Interceptors' create() method is invoked. This method can potentially add new policies or modify the IOR template created in the previous step.
3
A Portable Interceptor's IORInfo object is created and the IORInterceptors' establish_components() method is invoked. This interception point allows the Interceptor to query the policies passed to create_POA() and those added in the previous step, and also add components to the IOR template based on those policies.
4
An object reference factory and object reference template for the POA are created, and the Portable Interceptor's IORInterceptors' components_established() method is invoked. This interception point allows the Interceptor to change the POA's object reference factory, which will be used to manufacture object references.
Order of ORB events during object reference creation
The following events occur during calls to POA that create object reference, such as create_reference(), create_reference_with_id().
1
Call the object reference factory's make_object() method to create the object reference (this does not call the VisiBroker IOR creation Interceptors, and the factory may be user -supplied). If there are no VisiBroker IOR creation Interceptors installed, this should be the object reference returned to the application; otherwise, proceed to step 2.
2
3
IOR from step 2 is returned as the object reference to the caller of create_reference(), create_reference_with_id().