[Contents] [Previous] [Next]

Chapter 8
Dynamic Interfaces

This chapter discusses how client applications can use the Interface Repository to discover object interfaces and dynamically create requests for using those interfaces. It includes the following major sections:

Dynamic Invocation Interface

The Interface Repository

The Request Class

Creating a DII request

Initializing a DII Request

Sending a DII Request

Dynamic Invocation Interface

The Dynamic Invocation Interface (DII) lets your client applications use any registered object without having to first link the client stubs created for that object by the IDL compiler. With the DII, your client application can dynamically build requests for any object interface that has been stored in the Interface Repository. Object implementations are not required to provide any extra code to handle DII requests.

While client applications that use the DII are not as efficient as applications that use statically-linked client stubs, they offer some important advantages. Clients are not restricted to using just those objects that were defined at the time the client application was compiled. In addition, client applications do not need to be re-compiled in order to access new object implementations.

Steps for Dynamic Invocation

There are five steps that a client follows for dynamic invocation.

Retrieve an object's interface definition from the interface repository.

  1. Identify and retrieve the desired operation definition from the object's interface definition.

  2. Bind to the object and obtain an object reference.

  3. Create the dynamic invocation request.

  4. Invoke the request and receive the results.

The Interface Repository

The Interface Repository (IR) contains information on a variety of objects that the ORB or a client application may need to access. The IR offers an object interface that provides your client applications with a variety of methods for obtaining the interfaces offered by all currently active objects. Your client application can bind to the Repository and then invoke the methods defined by the Repository class to locate object implementations. The following table shows the various types of objects that can be contained in the IR. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

Table 8.1 Objects that can be stored in the IR.
Object type Description
Repository

Represents the top-level module that contains all other objects in this repository.

ModuleDef

Contains a grouping of interfaces. Can also contain constants, typedefs and even other ModuleDef objects.

InterfaceDef

Contains a list of operations, exceptions, typedefs, constants and attributes that make up an interface.

AttributeDef

Defines an attribute associated with an interface.

OperationDef

Defines an operation on an interface. It includes a list of parameters required for this operation and a list of exceptions that may be raised by this operation.

TypedefDef

Defines a base interface for named types that are not interfaces.

ConstantDef

Defines a named constant.

ExceptionDef

Defines an exception that may be raised by an operation.

The Repository Class.

class CORBA {

class Repository : public Container {
Contained_ptr lookup_id(const char * search_id);
PrimitiveDef_ptr get_primitive(PrimitiveKind kind);
StringDef_ptr create_string(ULong bound);
SequenceDef_ptr create_sequence(CORBA::ULong bound, IDLType_ptr element_type);
ArrayDef_ptr create_array(ULong length, IDLType_ptr element_type);
};
  ...
};

Obtaining an Object's Interface

The library client application could be enhanced to dynamically obtain the Library interface and obtain information about the add_book operation.

Dynamically obtaining the Library::add_book method.

#include <lib_client.hh>
int main(int argc, char *const *argv)
{
CORBA::Boolean ret;
  // Initialize the ORB
  CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

  // Declare the library object
  library_var library_object;

  // Declare an interface repository pointer
CORBA::Repository_var rep_object;

try {
// Attempt to bind to the interface repository
rep_object = CORBA::Repository::_bind();
}
// Check for errors
catch(const CORBA::Exception& excep) {
cout << "Error binding to interface repository" << endl;
return(0);
}

// Locate the add_book operation definition. Can the operation be
// located without first locating the interface?
CORBA::Contained_var add_req =
rep_object->lookup_id("Library::add_book");

try {
// Bind to the library object.
CORBA::Object_ptr p;
WAIReturnType_t rc;
char* host = "myServer";
int port = 80;
char* uri = "/NameService/libObj";
rc = resolveURI(host, port, uri, p);
// Narrow the reference.
library_object = library::_narrow(p);
}
// Check for errors
catch(const CORBA::Exception& excep) {
cout << "Error binding to library object" << endl;
return(0);
}

// Create a request, initializing the operation name.
Request_var req = library_object->_request(add_req->name());
...
}

The Request Class

When your client application invokes a method on an object, a Request must be created to represent the method invocation. This Request is written to a buffer and sent to the object implementation. When your client application uses client stubs, this processing occurs transparently. Client applications that use the DII must create and send the Request themselves. The following code listing shows the Request class.

NOTE: There is no constructor for this class. The Object::_request method or Object::_create_request methods are used to create a Request object, given an object reference.
The Request class.

class CORBA {
  class Request {
    public:
      CORBA::Object_ptr       target() const;
      const char*             operation() const;
      CORBA::NVList_ptr       arguments();
      CORBA::NamedValue_ptr   result();
      CORBA::Environment_ptr  env();

      void                    ctx(CORBA::Context_ptr ctx);
      CORBA::Context_ptr      ctx() const;

      CORBA::Status           invoke();
      CORBA::Status           send_oneway();
      CORBA::Status           send_deferred();
      CORBA::Status           get_response();
      CORBA::Status           poll_response();
      ...
  };
};
The target is set implicitly from the object reference used to create the Request. The name of the operation must be specified when the Request is created. The initialization of the remaining properties is covered in "Initializing a DII Request". A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

Creating a DII request

Once you have obtained the interface to an object, issued a bind to that object and obtained an object reference, you can use one of two methods for creating a Request object. The following code listing shows the methods offered by the Object class.

Two methods for creating a Request object.

class CORBA {
  class Object {
    ...
    Status       _create_request(Context_ptr     ctx,
                                 const char *    operation,
                                 NVList_ptr      arg_list,
                                 NamedValue_ptr  result,
                                 Request_ptr     request,
                                 Flags           req_flags);

    Request_ptr  _request(Identifier operation);
    ...
  };
};
You can use the _create_request method to create a Request object, initializing the Context, the operation name, the argument list to be passed and the result. The request parameter points to the Request object that was created for this operation. The req_flags must be set to OUT_LIST_MEMORY if one or more of the arguments in the arg_list are output parameters.

You can also use the _request method to create a Request object, specifying only the operation name. You must then perform the rest of the initialization manually.

Initializing a DII Request

Initializing a DII request is a two-step process.

Set the context.

  1. Set the arguments.

Setting the Context

The Context object contains a list of properties, stored as NamedValue objects, that are passed to the object implementation as part of the Request. These properties represent information that would otherwise be difficult to communicate to the object implementation. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

The Context class.

class CORBA {
  class Context {
    public:
      const char          *context_name() const;
      CORBA::Context_prt  parent();
      CORBA::Status       create_child(const char *name,
                                       CORBA::Context_ptr&);
      CORBA::Status       set_one_value(const char *name,
                                        const CORBA::Any&);
      CORBA::Status       set_values(CORBA::NVList_ptr);
      CORBA::Status       delete_values(const char *name);
      CORBA::Status       get_values(const char *start_scope,
                                     CORBA::Flags,
                                     const char *name,
                                     CORBA::NVList_ptr&) const;
};

Setting the Arguments

The arguments for a Request are represented with a NVList object, which stores name-value pairs as NamedValue objects. You can use the arguments method to obtain a pointer to the arguments. This pointer can then be used to set the names and values of each of the arguments.

The NVList

This class implements a list of NamedValue objects that represent the arguments for a method invocation. Methods are provided for adding, removing and querying the objects in the list. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

class NVList {
  public:
    Long            count() const;
    NamedValue_ptr  add(Flags);
    NamedValue_ptr  add_item(const char *name, Flags);
    NamedValue_ptr  add_value(const char *name, const Any&, Flags);
    NamedValue_ptr  item(Long);
    Status          remove(Long);
    Status          free_out_memory();
};

The NamedValue

This class implements a name-value pair that represents both input and output arguments for a method invocation request.The NamedValue class is also used to represent the result of a request that is returned to the client application. The name property is simply a character string and the value property is represented by an Any class. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

The NamedValue class.

class NamedValue {
  public:
    const char  *name() const;
    Any         *value() const;
    Flags       flags() const;
};

Table 8.2 The NamedValue class methods
Method Description
name

Returns a pointer to the name of the item that you can then use to initialize the name.

value

Returns a pointer to an Any object representing the item's value that you can then use to initialize the value. For more information, see "The Any Class"

flags

Indicates if this item in an input argument, an output argument or both an input and output argument. If the item is both an input and output argument, you can specify a flag indicating that the ORB should make a copy of the argument and leave the caller's memory intact. Flags are:

ARG_IN
ARG_OUT
ARG_INOUT
IN_COPY_VALUE

The Any Class

This class is used to represent any IDL type so that they may be passed in a type-safe manner. Objects of this class have a pointer to a TypeCode that defines the object's type and a pointer to the value associated with the object. Methods are provide to construct, copy and destroy an object as well as initialize and query the object's properties. In addition, streaming operators are provided to write the object to a stream. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

The Any class.

class Any
{
  public:
    Any();
    Any(const Any&);
    Any(TypeCode_ptr tc, void *value, Boolean release=0);
    ~Any();

    Any& operator=(const Any&);
    // Overloaded operators for all data types
    void operator<<=(Short);
    void operator<<=(UShort);
    void operator<<=(Long);
    void operator<<=(ULong);
    ...
    TypeCode_ptr    type();
    const void      *value() const;
    static Any_ptr  _nil();
    static Any_ptr  _duplicate(Any *ptr);
    static void     _release(Any *ptr);

    // Streaming operators to write Anys to stdout, etc.
    ostream& operator<<(ostream&, const Any&);
    istream& operator>>(istream& strm, Any& any);
    istream& operator>>(istream& strm, Any_ptr& any);
    ...
}

The TypeCode Class

This class is used by the Interface Repository and the IDL compiler to represent the type of arguments or attributes.TypeCode objects are also used in the DII to specify an argument's type in conjunction with the Any class. TypeCode objects have a kind property and parameter list property. A complete description of this class can be found in the Netscape Internet Service Broker for C++ Reference Guide.

Table 8.3 TypeCode kinds and their associated parameter lists 
TypeCode Const Kind Parameter list
TC_null

tk_null

None

TC_void

tk_void

None

TC_short

tk_short

None

TC_long

tk_long

None

TC_ushort

tk_ushort

None

TC_ulong

tk_ulong

None

TC_float

tk_float

None

TC_double

tk_double

None

TC_boolean

tk_boolean

None

TC_char

tk_char

None

TC_octet

tk_octet

None

TC_any

tk_any

None

TC_TypeCode

tk_TypeCode

None

TC_Principal

tk_Principal

None

TC_Object

tk_objref

interface_id

Structure

(const generated)

tk_struct

struct-name, {member, TypeCode}

Union

(const generated)

tk_union

union-name, switch TypeCode, {label-value, member-name, TypeCode}

Enum

(const generator)

tk_enum

{enum-name, enum-id}

TC_string

tk_string

maxlen

Sequence

(const generator)

tk_sequence

TypeCode, maxlen

Array

(const generator)

tk_array

TypeCode, length

Table 8.4 TypeCode constants for IDL data types.
Type Name
TypeCode_ptr

_tc_null

TypeCode_ptr

_tc_void

TypeCode_ptr

_tc_short

TypeCode_ptr

_tc_long

TypeCode_ptr

_tc_ushort

TypeCode_ptr

_tc_ulong

TypeCode_ptr

_tc_float

TypeCode_ptr

_tc_double

TypeCode_ptr

_tc_boolean

TypeCode_ptr

_tc_char

TypeCode_ptr

_tc_octet

TypeCode_ptr

_tc_Any

TypeCode_ptr

_tc_TypeCode

TypeCode_ptr

_tc_Principal

TypeCode_ptr

_tc_Object

TypeCode_ptr

_tc_string

TypeCode_ptr

_tc_NamedValue

Sending a DII Request

The Request class provides several methods for sending the request, once it has been properly initialized. The simplest of these is the invoke method which sends the request and blocks waiting for a response before returning to your client application. The non-blocking method send_deferred allows your client to send the request and then use the poll_response method to determine when the response is available. The get_response method blocks until a response is received.

The send_oneway method can be used to send a oneway request. Oneway requests do not involve a response being sent from the object implementation.

The result method returns a pointer to a NamedValue object that represents the return value.

Example

...
// Assumes that req has been set to Request
// Create TypeCode for structure
CORBA::StructMemberSeq members;
members.length(2);
members[0].name = (const char *)"author";
members[0].type = CORBA::TypeCode::_duplicate(CORBA::_tc_string);
members[1].name = (const char *)"title";
members[1].type = CORBA::TypeCode::_duplicate(CORBA::_tc_string);

bookTypeCode = orb->create_struct_tc("book", "book", members);

// Write out author and title to a MarshalOutBuffer
CORBA::MarshalOutBuffer buf;
buf << argv[1];                    // Author
buf << argv[2];                    // Title
bookValue.replace(bookTypeCode, buf);
// Get Argument list from request.
CORBA::NVList_var arguments = req->arguments();
arguments->add_value("book", bookValue, CORBA::ARG_IN);

// Set result
// NOTE: All parameters types (IN, OUT, INOUT and RETURN) need
//          to be set so that DII knows the data types of all
//          arguments.
CORBA::Boolean ret=0;
CORBA::NamedValue_var result(req->result());
CORBA::Any_var  resultAny(result->value());
resultAny->replace(CORBA::_tc_boolean, &result);

// Execute the function
req->invoke();
CORBA::Environment_var env = req->env();
if ( env->exception() )
cout << "Exception occured" << endl;
else {
// Get the return value;
ret = *(CORBA::Boolean *)resultAny->value();
}
cout << "Return value from invoke: " << (int)ret << endl;
return(1);
}

Sending and Receiving Multiple Requests

A sequence of DII Request objects can be created using RequestSeq, defined in the CORBA::ORB class and shown in the code listing below. A sequence of requests can be sent using the ORB methods send_multiple_requests_oneway or send_multiple_requests_deferred. If the sequence of requests is sent as oneway requests, no response is expected from the server to any of the requests.

If the requests in the sequence are sent using send_multiple_requests_deferred, the poll_next_response and get_next_response methods are used to receive the response the server sends for each request.

The ORB method poll_next_response can be used to determine if a response has been received from the server. This method returns one if one or more responses are available. This method returns zero if there are no responses available.

The ORB method get_next_response can be used to receive a response. If no response is available, this method will block until a response is received. If you do not wish your client application to block, use the poll_next_response method to determine when a response is available.

ORB methods for sending multiple requests and receiving the results.

class CORBA {
  class ORB {
    ...
    typedef  sequence<Request_ptr> RequestSeq;
    Status   send_multiple_requests_oneway(const RequestSeq &);
    Status   send_multiple_requests_deferred(const RequestSeq &);
    Boolean  poll_next_response();
    Status   get_next_response();
    ...
  };
};


[Contents] [Previous] [Next]

Last Updated: 02/03/98 15:33:56


Copyright © 1997 Netscape Communications Corporation