Chapter 9: 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 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. Even recently registered object can be accessed by a client application using the DII. Your 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 newly added object implementations.

Steps for Dynamic Invocation

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

  1. Retrieve an object's interface definition from the interface repository.
  2. Identify and retrieve the desired operation definition from the object's interface definition.
  3. Bind to the object and obtain an object reference.
  4. Create the dynamic invocation request.
  5. 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. Table 9-1 shows the various types of objects that can be contained in the IR. A complete description of this class can be found in the VisiBroker for C++ Reference Guide.

The following section lists the objects that can be stored in the Interface Repository. The object type is given first followed by the 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 following example shows 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, introduced in Chapter 2, could be enhanced to dynamically obtain the Library interface and obtain information about the add_book operation.


#include <lib_client.hh>

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.

library_object = library::bind();

}

// 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. Figure 9-1 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.


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" and "Initializing Requests from an OperationDef" later in this document. A complete description of this class can be found in the VisiBroker 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 example shows the methods offered by the Object class.


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

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 VisiBroker for C++ Reference Guide.


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 VisiBroker 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 VisiBroker for C++ Reference Guide.


class NamedValue {

public:

const char *name() const;

Any *value() const;

Flags flags() const;

};

The following section lists the NamedValue class methods. The method is given first followed by the 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" on page 9-11.

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:

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 VisiBroker for C++ Reference Guide.


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 VisiBroker for C++ Reference Guide.

TypeCodes

The following section lists the kinds of TypeCodes and their associated parameter lists. The TypeCode Const if given first followed by the kind and parameter list.

TC_null

tk_null

No parameters.

TC_void

tk_void

No parameters.

TC_short

tk_short

No parameters.

TC_long

tk_long

No parameters.

TC_ushort

tk_ushort

No parameters.

TC_ulong

tk_ulong

No parameters.

TC_float

tk_float

No parameters.

TC_double

tk_double

No parameters.

TC_boolean

tk_boolean

No parameters.

TC_char

tk_char

No parameters.

TC_octet

tk_octet

No parameters.

TC_any

tk_any

No parameters.

TC_TypeCode

tk_TypeCode

No parameters.

TC_Principal

tk_Principal

No parameters.

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

TypeCode Constants

The following section lists TypeCode constants for IDL data types. The type is given first followed by the 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.

The following example shows how to send a DII request.

...

// 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 Figure 9-12. 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.

The following example shows 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();

...

};

};


[Preface] [Chapter 1] [Chapter 2] [Chapter 3] [Chapter 4] [Chapter 5] [Chapter 6]
[Chapter 7] [Chapter 8] [Chapter 9] [Chapter 10] [Chapter 11] [Chapter 12] [Appendix A]