Next: The Dynamic Skeleton Up: No Title Previous: Dynamic Management of

# 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. An toy example use of the DII can be found in the omniORB2 distribution in the `<top>/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 2 specification [OMG96a].

# 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.

# 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()`. 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 specification [OMG96a] for more details.

## 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();
    };

## 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();
    };

## 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();
    };

## Context

Represents a set of context strings. User contexts are not supported by the omniidl2 IDL compiler - and so cannot be used with statically defined operations. However they are supported in the DII.

    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();
    };

## 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();
    };

## ExceptionList

`ExceptionList`s 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();
    };

## 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 UnknownUserException* _narrow(Exception*);
    };

## 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();
    };

# 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.

## Examples

An operation might be specified in IDL as:

    short anOpn(in string a);

An operation invocation may be created as follows:

    CORBA::NVList_var args;
    CORBA::ORB::create_list(1, args);
    *(args->add(CORBA::ARG_IN)->value()) <<= (const char*) "Hello World!";

    CORBA::NamedValue_var result;
    CORBA::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);

# Invoking Operations

Once the `Request` object has been properly constructed the operation may be invoked by calling `invoke()` or `send_oneway()`. When this returns the operation has completed (in the case of `invoke()`) or an exception has been thrown. If the operation generated a user-defined exception then it can be retrieved using `env()` - otherwise this value is nil. If a system exception is generated then the behavior depends on the following runtime configuration variable:

    namespace omniORB {
      ...
      CORBA::Boolean diiThrowsSysExceptions;
      ...
    };

By default this is false, and the exception will be passed via the `env()` mechanism as described above. If it is true, then the exception will be thrown. This variable may also be set on the command line using the option `-ORBdiiThrowsSysExceptions`. See section 4.1 for more details. If no exception was generated then the return value may be retrieved from `return_value()`, and out/inout arguments inspected with `arguments()`.

## Deferred Requests

If `send_deferred()` is used then the invocation is performed asynchronously. A call to `get_response()` will then block until the reply is received (and the operation has therefore completed or thrown an exception), or `poll_response()` may be used, which returns true when the operation has completed.

The runtime configuration variable `omniORB::diiThrowsSysExceptions` applies here in the same way as above. If its value is true, then `send_deferred()`, `get_response()` or `poll_response()` may throw a system exception.

Next: The Dynamic Skeleton Up: No Title Previous: Dynamic Management of