Chapter 9 Interceptors
omniORB supports interceptors that allow the application to insert
processing in various points along the call chain, and in various
other locations. It does not (yet) support the standard Portable
Interceptors API.
The interceptor interfaces are defined in a single header,
include/omniORB4/omniInterceptors.h. Each interception point
consists of a singleton object with add() and remove() methods,
and the definition of an ‘interceptor info’ class. For example:
class omniInterceptors {
...
class clientSendRequest_T {
public:
class info_T {
public:
GIOP_C& giop_c;
IOP::ServiceContextList service_contexts;
info_T(GIOP_C& c) : giop_c(c), service_contexts(5) {}
private:
info_T();
info_T(const info_T&);
info_T& operator=(const info_T&);
};
typedef CORBA::Boolean (*interceptFunc)(info_T& info);
void add(interceptFunc);
void remove(interceptFunc);
};
...
};
You can see that the interceptors themselves are functions
that take the info_T object as their argument and return
boolean. Interceptors are called in the order they are registered;
normally, all interceptor functions return true, meaning that
processing should continue with subsequent interceptors. If an
interceptor returns false, later interceptors are not
called. You should only do that if you really know what you are doing.
Notice that the info_T contains references to omniORB internal
data types. The definitions of these types can be found in other
header files within include/omniORB4 and
include/omniORB4/internal.
9.1 Interceptor registration
All the interceptor singletons are registered within another singleton
object of class omniInterceptors. You retrieve a pointer to the
object with the omniORB::getInterceptors() function, which
must be called after the ORB has been initialised with
CORBA::ORB_init(), but before the ORB is used. The code to
register an interceptor looks, for example, like:
omniInterceptors* interceptors = omniORB::getInterceptors();
interceptors->clientSendRequest.add(myInterceptorFunc);
9.2 Available interceptors
The following interceptors are available:
- encodeIOR
Called when encoding an IOR to represent an object reference. This
interception point allows the application to insert extra profile
components into IORs. Note that you must understand and adhere to the
rules about data stored in IORs, otherwise the IORs created may be
invalid. omniORB itself uses this interceptor to insert various items,
so you can see an example of its use in the
insertSupportedComponents() function defined in
src/lib/omniORB/orbcore/ior.cc.- decodeIOR
Called when decoding an IOR. The application can use this to get out
whatever information they put into IORs with encodeIOR. Again, see
extractSupportedComponents() in
src/lib/omniORB/orbcore/ior.cc for an example.- clientOpenConnection
Called as a client opens a new connection to a server, after the
connection is opened but before it is used to send a request. The
interceptor function can set the info_T’s reject member
to true to cause the client to immediately close the new
connection and throw CORBA::TRANSIENT to the calling code. In that
case, the interceptor function can also set the why member to
provide a message that is logged.- clientSendRequest
Called just before a request header is sent over the network. The
application can use it to insert service contexts in the header. See
the setCodeSetServiceContext() function in
src/lib/omniORB/orbcore/cdrStream.cc for an example of its use.- clientReceiveReply
Called as the client receives a reply, just after unmarshalling the
reply header. Called for normal replies and exceptions.- serverAcceptConnection
Called when a server accepts a new incoming connection, but before it
reads any data from it. The interceptor function can set the
info_T’s reject member to true to cause the
server to immediately close the new connection. In that case, the
interceptor function can also set the why member to provide a
message that is logged.- serverReceiveRequest
Called when the server receives a request, just after unmarshalling
the request header. See the getCodeSetServiceContext() function in
src/lib/omniORB/orbcore/cdrStream.cc for an example.- serverSendReply
Called just before the server marshals a reply header.- serverSendException
Called just before the server marshals an exception reply header.- createRope
Called when the ORB is about to create a ‘rope’ that encapsulates a
bundle of connections (‘strands’) to a remote address space. It allows
application code to override omniORB’s normal connection management.- createIdentity
Called when the ORB is about to create an ‘identity’ object to
represent a CORBA object. It allows application code to provide its
own identity implementations. It is very unlikely that an application
will need to do this.- createORBServer
Used internally by the ORB to register different kinds of server. At
present, only a GIOP server is registered. It is very unlikely that
application code will need to do this.- createThread
Called whenever the ORB creates a thread. The info_T class for
this interceptor is class info_T {
public:
virtual void run() = 0;
virtual omni_thread* self() = 0;
};
The interceptor is called in the context of the newly created thread.
The function must call the info_T’s run() method, to
pass control to the thread body. run() returns just before the
thread exits. This arrangement allows the interceptor to initialise
some per-thread state before the thread body runs, then release it
just before the thread exits.
The info_T’s self() method returns a pointer to the
omni_thread object for the thread, equivalent to calling
omni_thread::self().
- assignUpcallThread
The ORB maintains a general thread pool, from which threads are drawn
for various purposes. One purpose is for performing upcalls to
application code, in response to incoming CORBA calls. The
assignUpcallThread interceptor is called when a thread is
assigned to perform upcalls. In the thread per connection model, the
thread stays assigned to performing upcalls for the entire lifetime of
the underlying network connection; in the thread pool model, threads
are assigned for upcalls on a per call basis, so this interceptor is
triggered for every incoming call1. As with the createThread interceptor, the
interceptor function must call the info_T’s run() method to
pass control to the upcall.When a thread finishes its assignment of processing upcalls, it
returns to the pool (even in thread per connection mode), so the same
thread can be reassigned to perform more upcalls, or reused for a
different purpose.
- assignAMIThread
Asynchronous Method Invocation (AMI) uses threads to perform outgoing
calls. The assignAMIThread interceptor is called when a thread
is assigned to perform AMI calls. As with the other thread
interceptors, the interceptor function must call the info_T’s
run() method to pass control to the AMI call.Unlike the other interceptors, the interceptor functions for
createThread, assignUpcallThread and
assignAMIThread have no return values. Interceptor chaining is
performed by calls through the info_T::run() method, rather than
by visiting interceptor functions in turn.
9.3 Server-side call interceptor
Calls can be intercepted on the server just before the upcall into
application code. This interceptor is registered with omniORB’s
callDescriptor class, which is responsible for encapsulating
the state of a call. Unlike the transport-related
serverReceiveRequest, serverSendReply and
serverSendException interceptors, the callDescriptor
interceptor is invoked for all calls, even ones from colocated
clients in the same address space.
The types used for the call interceptor are defined in
include/omniORB4/callDescriptor.h. The interceptor takes the
form of a bare function with two parameters. The first parameter is a
pointer to the callDescriptor; the second is a pointer to
omniServant, which is the base class of all servant
classes. The interceptor function must call the
callDescriptor’s interceptedCall() method to pass on the
call.
This interception point allows access to various parts of omniORB’s
call machinery. The callDescriptor includes access to the
operation name and, if cast to the concrete subclass defined by the
IDL compiler, the call arguments and return values too.