Chapter 11 The Dynamic Invocation Interface
The Dynamic Invocation Interface (or DII) allows applications to
invoke operations on CORBA objects about which they have no static
information. That is to say the application has not been linked with
stub code which performs the remote operation invocation. Thus using
the DII applications may invoke operations on any CORBA object,
possibly determining the object's interface dynamically by using an
Interface Repository.
This chapter presents an overview of the Dynamic Invocation Interface.
A toy example use of the DII can be found in the omniORB distribution
in the src/examples/dii directory. The DII makes extensive use
of the type Any, so ensure that you have read chapter 9.
For more information refer to the Dynamic Invocation Interface and C++
Mapping sections of the CORBA specification [OMG99].
11.1 Overview
To invoke an operation on a CORBA object an application needs an
object reference, the name of the operation and a list of the
parameters. In addition the application must know whether the
operation is one-way, what user-defined exceptions it may throw, any
user-context strings which must be supplied, a `context' to take these
values from and the type of the returned value. This information is
given by the IDL interface declaration, and so is normally made
available to the application via the stub code. In the DII this
information is encapsulated in the CORBA::Request
pseudo-object.
To perform an operation invocation the application must obtain an
instance of a Request object, supply the information listed
above and call one of the methods to actually make the invocation. If
the invocation causes an exception to be thrown then this may be
retrieved and inspected, or the return value on success.
11.2 Pseudo Objects
The DII defines a number of psuedo-object types, all defined in the
CORBA namespace. These objects behave in many ways like CORBA
objects. They should only be accessed by reference (through
foo_ptr or foo_var), may not be instantiated directly
and should be released by calling CORBA::release()1. A nil reference should only be
represented by foo::_nil().
These pseudo objects, although defined in pseudo-IDL in the
specification do not follow the normal mapping for CORBA objects. In
particular the memory management rules are different---see the CORBA
2.3 specification [OMG99] for more details. New instances
of these objects may only be created by the ORB. A number of methods
are defined in CORBA::ORB to do this.
11.2.1 Request
A Request encapsulates a single operation invocation. It may
not be re-used---even for another call with the same arguments.
class Request {
public:
virtual Object_ptr target() const;
virtual const char* operation() const;
virtual NVList_ptr arguments();
virtual NamedValue_ptr result();
virtual Environment_ptr env();
virtual ExceptionList_ptr exceptions();
virtual ContextList_ptr contexts();
virtual Context_ptr ctxt() const;
virtual void ctx(Context_ptr);
virtual Any& add_in_arg();
virtual Any& add_in_arg(const char* name);
virtual Any& add_inout_arg();
virtual Any& add_inout_arg(const char* name);
virtual Any& add_out_arg();
virtual Any& add_out_arg(const char* name);
virtual void set_return_type(TypeCode_ptr tc);
virtual Any& return_value();
virtual Status invoke();
virtual Status send_oneway();
virtual Status send_deferred();
virtual Status get_response();
virtual Boolean poll_response();
static Request_ptr _duplicate(Request_ptr);
static Request_ptr _nil();
};
11.2.2 NamedValue
A pair consisting of a string and a value---encapsulated in an Any.
The name is optional. This type is used to encapsulate parameters and
returned values.
class NamedValue {
public:
virtual const char* name() const;
// Retains ownership of return value.
virtual Any* value() const;
// Retains ownership of return value.
virtual Flags flags() const;
static NamedValue_ptr _duplicate(NamedValue_ptr);
static NamedValue_ptr _nil();
};
11.2.3 NVList
A list of NamedValue objects.
class NVList {
public:
virtual ULong count() const;
virtual NamedValue_ptr add(Flags);
virtual NamedValue_ptr add_item(const char*, Flags);
virtual NamedValue_ptr add_value(const char*, const Any&, Flags);
virtual NamedValue_ptr add_item_consume(char*,Flags);
virtual NamedValue_ptr add_value_consume(char*, Any*, Flags);
virtual NamedValue_ptr item(ULong index);
virtual Status remove (ULong);
static NVList_ptr _duplicate(NVList_ptr);
static NVList_ptr _nil();
};
11.2.4 Context
Represents a set of context strings.
class Context {
public:
virtual const char* context_name() const;
virtual CORBA::Context_ptr parent() const;
virtual CORBA::Status create_child(const char*, Context_out);
virtual CORBA::Status set_one_value(const char*, const CORBA::Any&);
virtual CORBA::Status set_values(CORBA::NVList_ptr);
virtual CORBA::Status delete_values(const char*);
virtual CORBA::Status get_values(const char* start_scope,
CORBA::Flags op_flags,
const char* pattern,
CORBA::NVList_out values);
// Throws BAD_CONTEXT if <start_scope> is not found.
// Returns a nil NVList in <values> if no matches are found.
static Context_ptr _duplicate(Context_ptr);
static Context_ptr _nil();
};
11.2.5 ContextList
A ContextList is a list of strings, and is used to specify
which strings from the `context' should be sent with an operation.
class ContextList {
public:
virtual ULong count() const;
virtual void add(const char* ctxt);
virtual void add_consume(char* ctxt);
// consumes ctxt
virtual const char* item(ULong index);
// retains ownership of return value
virtual Status remove(ULong index);
static ContextList_ptr _duplicate(ContextList_ptr);
static ContextList_ptr _nil();
};
11.2.6 ExceptionList
ExceptionLists contain a list of TypeCodes---and are used to
specify which user-defined exceptions an operation may throw.
class ExceptionList {
public:
virtual ULong count() const;
virtual void add(TypeCode_ptr tc);
virtual void add_consume(TypeCode_ptr tc);
// Consumes <tc>.
virtual TypeCode_ptr item(ULong index);
// Retains ownership of return value.
virtual Status remove(ULong index);
static ExceptionList_ptr _duplicate(ExceptionList_ptr);
static ExceptionList_ptr _nil();
};
11.2.7 UnknownUserException
When a user-defined exception is thrown by an operation it is
unmarshalled into a value of type Any. This is encapsulated in an
UnknownUserException. This type follows all the usual rules for
user-defined exceptions---it is not a pseudo object, and its resources
may be released by using delete.
class UnknownUserException : public UserException {
public:
UnknownUserException(Any* ex);
// Consumes <ex> which MUST be a UserException.
virtual ~UnknownUserException();
Any& exception();
virtual void _raise();
static const UnknownUserException* _downcast(const Exception*);
static UnknownUserException* _downcast(Exception*);
static UnknownUserException* _narrow(Exception*);
// _narrow is a deprecated function from CORBA 2.2,
// use _downcast instead.
};
11.2.8 Environment
An Environment is used to hold an instance of a system
exception or an UnknownUserException.
class Environment {
virtual void exception(Exception*);
virtual Exception* exception() const;
virtual void clear();
static Environment_ptr _duplicate(Environment_ptr);
static Environment_ptr _nil();
};
11.3 Creating Requests
CORBA::Object defines three methods which may be used to create
a Request object which may be used to perform a single
operation invocation on that object:
class Object {
...
Status _create_request(Context_ptr ctx,
const char* operation,
NVList_ptr arg_list,
NamedValue_ptr result,
Request_out request,
Flags req_flags);
Status _create_request(Context_ptr ctx,
const char* operation,
NVList_ptr arg_list,
NamedValue_ptr result,
ExceptionList_ptr exceptions,
ContextList_ptr ctxlist,
Request_out request,
Flags req_flags);
Request_ptr _request(const char* operation);
...
};
operation is the name of the operation---which is the same as
the name given in IDL. To access attributes the name should be
prefixed by _get_ or _set_.
In the first two cases above the list of parameters may be supplied.
If the parameters are not supplied in these cases, or _request()
is used then the parameters (if any) may be specified using the
add_*_arg() methods on the Request. You must use one
method or the other---not a mixture of the two. For
in/inout arguments the value must be initialised, for
out arguments only the type need be given. Similarly the type
of the result may be specified by passing a NamedValue which
contains an Any which has been initialised to contain a value of that
type, or it may be specified using the set_return_type() method
of Request.
When using _create_request(), the management of any pseudo-object
references passed in remains the responsibility of the
application. That is, the values are not consumed---and must be
released using CORBA::release(). The CORBA specification is unclear
about when these values may be released, so to be sure of portability
do not release them until after the request has been released. Values
which are not needed need not be supplied---so if no parameters are
specified then it defaults to an empty parameter list. If no result
type is specified then it defaults to void. A Context need only
be given if a non-empty ContextList is specified. The
req_flags argument is not used in the C++ mapping.
11.3.1 Examples
An operation might be specified in IDL as:
short anOpn(in string a);
An operation invocation may be created as follows:
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB3");
...
CORBA::NVList_var args;
orb->create_list(1, args);
*(args->add(CORBA::ARG_IN)->value()) <<= (const char*) "Hello World!";
CORBA::NamedValue_var result;
orb->create_named_value(result);
result->value()->replace(CORBA::_tc_short, 0);
CORBA::Request_var req = obj->_create_request(CORBA::Context::_nil(),
"anOpn", args, result, 0);
or alternatively and much more concisely:
CORBA::Request_var req = obj->_request("anOpn");
req->add_in_arg() <<= (const char*) "Hello World!";
req->set_return_type(CORBA::_tc_short);
11.4 Invoking Operations
Once the Request object has been properly constructed, the
operation may be invoked by calling one of the following methods on
the request object:
invoke()
blocks until the request has completed. The application should then
test to see if an exception was raised. Since the CORBA spec is not
clear about whether or not system exceptions should be thrown from
this method, a runtime configuration variable is supplied so that you
can specify the behavior:
namespace omniORB {
...
CORBA::Boolean diiThrowsSysExceptions;
...
};
If this is FALSE, and the application should call the env() method
of the request to retrieve an exception (it returns 0 (nil) if no
exception was generated). If it is TRUE then system exceptions will be
thrown out of invoke(). User-defined exceptions are always passed
via env(), which will return a pointer to a
CORBA::UnknownUserException. The application can determine
which type of exception was returned by env()() by calling the
_narrow() method defined for each exception type.
-
Warning
In pre-omniORB 2.8.0 releases, the default value of
diiThrowsSysExceptions is FALSE. From omniORB 2.8.0 onwards,
the default value is TRUE.
After determining that no exception was thrown the application may
retrieve any returned values by calling return_value() and
arguments().
send_oneway()
has the same semantics as a oneway IDL operation. It is
important to note that oneway operations have at-most-once semantics,
and it is not guaranteed that they will not block. Any operation may
be invoked `oneway' using the DII, even if it was not declared as
`oneway' in IDL. A system exception may be generated, in which case it
will either be thrown or may be retrieved using env() depending on
diiThrowsSysExceptions as above.
send_deferred()
initiates the invocation, and then returns without waiting for the
result. At some point in the future the application must retrieve the
result of the operation---but other than testing for completion of the
operation the application must not call any of the request's methods
in the meantime.
-
get_response() blocks until the reply is received.
- poll_response() returns TRUE if the reply has been received,
and FALSE if not. It does not block.
Once poll_response() has returned TRUE, or get_response() has
been called and returned, the application may test for an exception
and retrieve returned values as above. If
diiThrowsSysExceptions is true, then a system exception may be
thrown from get_response(). From omniORB 2.8.0 onwards,
poll_response() will raise a system exception if one has occurred
during the invocation. Previously, poll_response() would not raise
an exception, so if polling, the application also had to call another
method to give the request an opportunity to raise the exception. This
could be one of the methods to retrieve values from the request, or
get_response().
11.5 Multiple Requests
The following methods are provided by the ORB to enable multiple requests to
be invoked asynchronously.
namespace CORBA {
...
class ORB {
public:
...
Status send_multiple_requests_oneway(const RequestSeq&);
Status send_multiple_requests_deferred(const RequestSeq&);
Boolean poll_next_response();
Status get_next_response(Request_out);
...
};
...
};
send_multiple_requests_oneway()
is used to invoke a number of oneway requests. An attempt will be made
to invoke each of the requests, even if one or more of the early
requests fails. The application may check for failure of any of the
requests by testing the request's env() method. System exceptions
are never raised by this method.
send_multiple_requests_deferred()
will initiate an invocation of each of the given requests, and return
without waiting for the reply. At some point in the future the
application must retrieve the reply by calling
get_next_response(), which returns a completed request. If no
requests have yet completed it will block. This method never throws
exceptions---the request's env() method must be used to determine
if an exception was generated. If not then any returned values may
then be queried.
poll_next_response() returns TRUE if there are any completed
requests, and FALSE otherwise, without blocking. If this returns true
then the next call to get_next_response() will not block.
However, if another thread may also be calling
get_next_response() then it could retrieve the completed message
first---in which case this thread might block.
There are no guarantee as to the order in which replies will be
received. If multiple threads are using this interface then it is not
even guaranteed that a thread will receive replies to the requests it
sent. Any thread may receive replies to requests sent by any other
thread. It is legal to call get_next_response() even if no
requests have yet been invoked---in which case the calling thread
blocks until another thread invokes a request and the reply is
received.
- 1
- if not
managed by a _var type.