Dynamic CORBA, Part 4: The Interface Repository

The Dynamic CORBA Interface Repository provides type information about CORBA interfaces and other entities defined in IDL. It helps ensure the type-safety and correctness of the DII and DSI mechanisms that defer an application's binding onto specific interface types until run time.


Object Interconnections: Dynamic CORBA, Part 4: The Interface Repository

Douglas C. Schmidt and Steve Vinoski


Introduction

Welcome to the next installment of our series covering Dynamic CORBA. In Part 1 [1], we discussed the DII (CORBA Dynamic Invocation Interface), which allows applications to invoke operations on target objects without having compile-time knowledge of target interfaces. In Part 2 [2], we explained the basics of the Dynamic Any, which enables applications to handle any value of any IDL type (whether simple or complex) without having compile-time information about that type. In Part 3 [3], we covered the DSI (Dynamic Skeleton Interface), which is the server-side counterpart of the DII that enables CORBA developers to portably construct applications that cannot know a priori the types or identities of the objects they must serve.

In this column, we explain the IFR (Interface Repository), which is the next piece of the Dynamic CORBA puzzle. Our previous columns in this series have intentionally ignored the fact that Dynamic CORBA applications require a means of obtaining type information dynamically. To avoid cluttering our examples and explanations, we instead relied on hard-coded type information. Now that we've completed our coverage of the DII, DSI, and Dynamic Any, we'll show how CORBA developers can use the IFR to construct truly dynamic applications that discover all necessary type information at run time.

IFR Basics

CORBA developers are typically familiar with IDL compilers, which parse IDL definitions and generate stubs and skeletons to represent them. Such compilers are typically composed of:

·         A front end, which performs lexical, syntactic, and semantic analysis of the IDL input while building a parse tree representing that input.

·         One or more back ends, which walk the resulting parse tree and generate code based on its contents. For example, one back-end implementation might generate C++ stubs and skeletons, while another might generate Java stubs and skeletons.

The IFR is a distributed service that shares similarities with the inner workings of an IDL compiler. It allows applications to store and retrieve IDL definitions to and from an instance of the service, much like allowing them to modify and access an IDL parse tree at run time. For this column, we focus on the IFR operations that provide read access to IDL definitions, since the write operations are typically used only by tools used to populate and administer an IFR. ORBs that provide IFR implementations normally support tools that make use of these write interfaces to allow you to add definitions to the IFR (though most require you to remember to do so explicitly). Often, the tool that writes the IFR is a special back end to the IDL compiler that walks the internal parse tree and writes the definitions to the IFR.

There are several different ways to access the IFR's contents. We describe the two most useful approaches below:

1.      Initial reference. If you pass the string "InterfaceRepository" to the ORB::resolve_initial_references method, it will return an object reference that you can narrow to the CORBA::Repository interface type. You can then use that interface to look up and navigate through all type definitions stored in the repository.

2.      CORBA::Object method. The get_interface method of the CORBA::Object interface returns a CORBA::InterfaceDef object that provides access to the IDL definitions specific to the target object.

Dynamic CORBA applications typically use the CORBA::Object::get_interface method because it provides more direct access to the type information needed for such applications. Since applications must be able to invoke get_interface without knowing the specific type of the target object, this method appears on the CORBA::Object interface, meaning that it's available on all CORBA objects. The InterfaceDef returned from get_interface describes the most derived interface supported by the target object. For example, if interface B derives from interface A, and a servant implements interface B, then B is the most derived interface supported by an object incarnated by the servant. By virtue of inheritance, the object also supports interface A, but A is not its most derived interface. If someone later adds an interface C derived from B, the most derived interface of the existing object is still B, because it does not implement C.

Since it inherits from several base interfaces, InterfaceDef is somewhat complicated. First we look at its describe_interface method, which returns a struct that contains all the information about the target interface. To make our example concrete, this column will continue to use the Stock Quoter IDL shown below as our target interface:

// IDL

module Stock {

  exception Invalid_Stock {};

 

  struct Info {

    long high;

    long low;

    long last;

  };

 

  interface Quoter {

    Info get_quote (in string stock_name) raises (Invalid_Stock);

  };

};

Assuming we had an object reference to an instance of Stock::Quoter, FullInterfaceDescription struct returned from describe_interface would contain the following information:

·         Its simple name, e.g., "Quoter".

·         Its repository ID, e.g., "IDL:Stock/Quoter:1.0". (We discuss repository IDs in more detail near the end of this column.)

·         Its TypeCode, which for static programs is also available as the constant Stock::_tc_Quoter.

·         Its version string.

·         Its enclosing scope's repository ID.

·         The repository IDs of its base interfaces.

·         Sequences of full descriptions of its operations and attributes.

For example, let's assume we've retrieved a stringified object reference for a Stock::Quoter object from a file, so we can invoke its get_interface method, as shown below:

CORBA::String_var str = // read string from file

CORBA::Object_var target =

  orb->string_to_object (str);

CORBA::InterfaceDef_var intf_def =

  target->_get_interface ();

Note that like other CORBA::Object operations, the get_interface operations acquires a leading underscore when mapped to a C++ method to keep it from clashing with user-defined operations in derived classes. After we invoke get_interface, we can access the full description of the Stock::Quoter interface by invoking describe_interface on the returned InterfaceDef to obtain FullInterfaceDescription, as follows:

CORBA::InterfaceDef::FullInterfaceDescription_var fid =

 intf_def->describe_interface ();

Continuing with the example above, we could print the names of all operations and attributes in the Stock::Quoter interface (or any interface for that matter) like this:

cout << "interface name: " << fid.name.in () << endl;

CORBA::ULong i;

for (i = 0; i < fid->operations.length (); ++i)

  cout << " operation " << i+1 << ": "

<< fid->operations[i].name.in () << endl;

for (i = 0; i < fid->attributes.length (); ++i)

  cout << " attribute " << i+1 << ": "

 << fid->attributes[i].name.in () << endl;

The operations member is a sequence of OperationDescription, and the attributes member is a sequence of AttributeDescription. The output of this program for Stock::Quoter is:

interface name: Quoter

  operation 1: get_quote

Invoking a DII Request

The full descriptions of the operations and attributes that InterfaceDef contains are the key to obtaining the type information necessary to write true dynamic clients. In Parts 1 and 2 of this series, we used static type information in our examples since we hadn't discussed the IFR yet. We'll now revisit those examples to show how to get the necessary type information from the IFR. Our DII/DynAny example is based on the Stock::Quoter IDL definitions shown above. Our original DII/DynAny client looked like this:

CORBA::Object_var obj = // ...obtain object reference...

CORBA::Request_var req = obj->_request ("get_quote");

req->add_in_arg () <<= "IONA"; // Note static string type.

// Note static TypeCode.

req->set_return_type (Stock::_tc_Info);

// Another static TypeCode.

req->exceptions ()->add (Stock::_tc_Invalid_Stock);

req->invoke ();

CORBA::Environment_ptr env = req->env ();

if (CORBA::is_nil (env) || env->exception () == 0) {

  DynamicAny::DynAny_var dynany =

 dynfactory->create_dyn_any (req->return_value ());

 ...

}

For a truly dynamic client, we must replace the static type information we use to set up the request arguments, return type, and exception types with type information dynamically retrieved from the IFR. One way to do this is to navigate explicitly through the full OperationDescription for the get_quote operation. The following example builds on the FullInterfaceDescription retrieval code from the previous example.

 1  CORBA::Object_var obj =

 2   // ...obtain object reference...

 3  CORBA::Request_var req =

 4   obj->_request ("get_quote");

 5  for (CORBA::ULong i =

 6    0; i < fid->operations.length (); ++i) {

 7    if (strcmp (fid->operations[i].name,

 8      "get_quote") == 0) {

 9      for (CORBA::ULong j = 0;

10        j <fid->operations[i].parameters.length ();

11        ++j) {

12        CORBA::Flags direction;

13        switch

14 (fid->operations[i].parameters[j].mode) {

15        case CORBA::PARAM_IN:

16 direction = CORBA::ARG_IN;

17 break;

18        case CORBA::PARAM_OUT:

19 direction = CORBA::ARG_OUT;

20 break;

21        case CORBA::PARAM_INOUT:

22 direction = CORBA::ARG_INOUT;

23 break;

24        }

25        CORBA::NamedValue_ptr nv =

26 req->arguments ()->add (direction);

27        nv->value ()->replace

28 (fid->operations[i].parameters[j].type. in (),

29 0);

30 }

31 req->set_return_type

32 (fid->operations[i].result.in ());

33 for (CORBA::ULong j = 0;

34 j < fid->operations[i].exceptions.length ();

35 ++j)

36     req->exceptions ()->add

37   (fid->operations[i].exceptions[j].type.in ());

38}

39 }

Lines 5-8 look through the sequence of operation descriptors to find the element that describes the get_quote operation. Using that element, lines 9-30 set the type and direction for each Request argument. Line 231 then sets the Request return type, and lines 33-38 set all the user-defined exceptions that the Request can return. After this code completes, all that's left to do is set the string value for the single argument to get_quote using DynAny, which we'll show a little later.

Although the code above eliminates the dependency on static type information, it's complicated due to the deep nesting of the description structs. Another approach is to use the create_operation_list method that the ORB provides to fill in the target operation's argument details:

 1  CORBA::Object_var target =

 2    // ...obtain object reference...

 3  CORBA::InterfaceDef_var intf_def =

 4    target->_get_interface ();

 5  CORBA::Contained_var ctd =

 6    intf_def-> lookup ("get_quote");

 7  CORBA::OperationDef_var op_def =

 8    CORBA::OperationDef::_narrow (ctd.in ());

 9  CORBA::NVList_var arglist;

10  orb->create_operation_list

11 (op_def.in (), arglist.out ());

The create_operation_list method shown on lines 10-11 uses its OperationDef argument to fill in its NVList argument, which is an out parameter. (An actual implementation of create_operation_list inside the ORB might look a lot like the FullInterfaceDescription example, in fact). Lines 5-6 obtain OperationDef by looking up the entity named "get_quote" in InterfaceDef. Note, however, that create_operation_list fills in only the types and directions of the arguments in the target operation's argument list and does not take care of the Request return type, context list, or exception list. A complete example using create_operation_list follows:

 1  CORBA::Object_var target =

 2   // ...obtain object reference...

 3  CORBA::InterfaceDef_var intf_def =

 4   target->_get_interface ();

 5  CORBA::Contained_var ctd =

 6   intf_def-> lookup ("get_quote");

 7  CORBA::OperationDef_var op_def =

 8   CORBA::OperationDef::_narrow (ctd.in ());

 9  CORBA::NVList_var arglist;

10  orb->create_operation_list (op_def.in (),

11  arglist.out ());

12  CORBA::NamedValue_var ret_nv;

13  orb->create_named_value (ret_nv.out ());

14  CORBA::TypeCode_var ret_type = op_def->result ();

15  ret_nv->value ()->replace (ret_type.in (), 0);

16  CORBA::ExceptionList_var exc_list;

17  CORBA::ExceptionDefSeq_var exc_seq =

18 op_def->exceptions ();

19  if (exc_seq->length () > 0) {

20 orb->create_exception_list (exc_list.out ());

21 for (CORBA::ULong i = 0; i < exc_seq->length ();

22 ++i) {

23 CORBA::TypeCode_var exc_tc = exc_seq[i]->type ();

24 exc_list->add (exc_tc.in ());

25 }

26  }

27  CORBA::Request_var request;

28  obj->_create_request (CORBA::Context::_nil (),

29 "get_quote", arglist.in (), ret_nv.in (),

30 CORBA:ContextList::_nil (),exc_list.in (),

31 request.out (), 0);

This code is really no simpler than the previous example, but it reveals an important fact about the IFR: it supports multiple views of the information it contains. Specifically, the IFR allows navigation based on description data (as the previous example shows) or based on object references (as this example shows). Specifically, in this example, we first retrieve an InterfaceDef object reference on lines 3-4. From that we retrieve an OperationDef object reference via named-based lookup on lines 5-8. Lines 10-26 then invoke separate methods on OperationDef to retrieve each data item we need, which differs from the previous example that invokes one OperationDef method to return all the necessary data in one big data structure. Finally, lines 28-31 invoke the long form of the create_request operation. We use the long form because we already have all the information necessary to create the request.

Note how lines 5-6 in this example use the lookup method to obtain Contained, which we then narrow to OperationDef in lines 7-8. The Contained interface and its counterpart Container interface serve as fundamental base interfaces in the IFR, allowing for navigation through the hierarchies of IDL definitions. In IDL, every entity is either a container or is something that can be put into a container. Some IDL entities are both. For example, InterfaceDef is a Container that holds definitions for operations, attributes, and types. It's also a Contained that can be stored within a module definition. Similarly, ModuleDef is also both a Container and a Contained, whereas EnumDef (used to describe an IDL enum type) is only a Contained. At the top level of the IFR hierarchy, the Repository interface (which is the type of interface returned by ORB::resolve_initial_references("InterfaceRepository")) does not correspond to an IDL construct but still derives from Container because it contains everything within the IFR.

Regardless of whether we use create_operation_list or explicitly construct the argument list ourselves using a full description struct, we must set the argument values. The C++ code below will work with either approach:

 1  CORBA::Object_var obj =

 2    orb->resolve_initial_references ("DynAnyFactory");

 3  DynamicAny::DynAnyFactory_var dynfactory =

 4    DynamicAny::DynAnyFactory::_narrow (obj.in ());

 5  CORBA::ULong len = req->arguments ()->count ();

 6  for (CORBA::ULong i = 0; i < len; ++i) {

 7   CORBA::NamedValue_ptr nv =

 8   req->arguments ()->item (i);

 9   if (nv->flags () == CORBA::ARG_OUT)

10      continue;

11    CORBA::Any *arg = nv->value ();

12    CORBA::TypeCode_var tc = arg->type ();

13    DynamicAny::DynAny_var dynany =

14      dynfactory->create_dyn_any_from_type_code

15      (tc.in ());

16    switch (tc->kind ()) {

17    case CORBA::tk_string:

18  dynany->insert_string ("IONA");

19  break;

20

21  // handle other types

22    }

23    CORBA::Any_var newany = dynany->to_any ();

24    *arg = newany.in ();

25  }

26  req->invoke ();

To set our arguments, lines 6-22 walk over the list of arguments created in the previous example, ignoring all out arguments. For each argument, lines 12-15 use TypeCode to create DynAny. Lines 16-24 then set the value of DynAny according to its type. Our example sets a hard-coded string value in DynAny for simplicity, though a real dynamic application would obtain its values from another application, a database or file, or a GUI. After setting all the arguments, line 26 finally invokes the request. For clarity, we separated the creation of the argument list from the setting of the argument values. In real application code, it would be more efficient and easier to set the argument values as you construct the argument list. Note also that our example shows only the handling of the string type because showing a complete solution would require much more code.

The IFR and the DSI

Now that we've illustrated how a dynamic client application can use the IFR to obtain and use type information, let's move the discussion to the server. As we explained in Part 3 of this column series [3], the DSI allows certain types of server applications, such as gateways or debugging intermediaries, to serve objects for which they have no compile-time type information. Such DSI applications thus require access to the IFR to handle requests properly.

When it is up-called by the POA, a DSI servant must determine the type and identity of the target object for which it's being invoked. Based on the target object's type, it can query the IFR to obtain type information about the arguments of the operation being invoked. It then passes this argument type information back to the ORB runtime through the ServerRequest interface to allow the runtime to demarshal the input arguments properly. The following example illustrates these steps within the DSI servant's invoke method:

 1  CORBA::Object_var obj =

 2 orb->resolve_initial_references ("POACurrent");

 3  PortableServer::Current_var current =

 4 PortableServer::Current::_narrow (obj.in ());

 5  CORBA::Object_var target = current->get_reference ();

 6  CORBA::InterfaceDef_var intf_def =

 7 target->_get_interface ();

 8  CORBA::Contained_var ctd =

 9 intf_def-> lookup (server_request->operation ());

10  CORBA::OperationDef_var op_def =

11CORBA::OperationDef::_narrow (ctd.in ());

12  CORBA::NVList_var arglist;

13  orb->create_operation_list (op_def.in (),

14    arglist.out ());

15  server_request->arguments (arglist.inout ());

Lines 1-5 obtain an object reference for the target object via the POA Current. Lines 6-7 then use the object reference and the name of the invoked operation (obtained from ServerRequest) to obtain an OperationDef for the target operation. Lines 13-24 pass OperationDef to create_operation_list to create an argument list for the target operation, which we pass into the ServerRequest::arguments method to obtain the arguments passed from the caller on line 15. After our arguments are available, we process them using DynAny as shown in [3]. As you can see, create_operation_list and OperationDef make it fairly straightforward to implement truly dynamic DSI servants.

Repository IDs

Ever wonder about those strange repository ID strings that appear in various parts of CORBA? The IFR is the main reason those repository IDs exist. As its name suggests, a repository ID identifies an entity in the IFR. There are multiple formats allowed for repository IDs, such as Java RMI hashed identifiers, DCE UUID identifiers, and support for custom application-specific formats, but the default is the familiar "IDL:identifier:version" format. For example, the default repository ID for our Stock::Quoter interface is:

IDL:Stock/Quoter:1.0

The entity identifier is formed by replacing all instances of :: in the fully-scoped IDL name with the / separator, and setting the version defaults to 1.0. You can change the entity identifier and the version portions of a repository ID in your IDL definition using #pragma prefix or typeprefix directives. For example, all definitions in OMG specifications have the "omg.org" prefix:

#pragma prefix "omg.org"

module CORBA {

  interface InterfaceDef { ... };

};

Without the prefix, the repository ID for the InterfaceDef interface would be:

IDL:CORBA/InterfaceDef:1.0

With the #pragma prefix directive in effect, the repository ID becomes:

IDL:omg.org/CORBA/InterfaceDef:1.0

Using such prefixes helps reduce the chances of name clashes among repository IDs for types defined by separate organizations. To modify the version number in a repository ID, we use the #pragma version directive:

module CORBA {

  typeprefix CORBA "omg.org"

  interface InterfaceDef { ... };

#pragma version InterfaceDef 3.0

};

The parameters to the #pragma version directive are the scoped name of the entity to be versioned, and the version number in <major>.<minor> format. (Caveats regarding the versioning of IDL definitions are explained elsewhere [4]). Note also that in this example we use the new typeprefix IDL keyword to attach the "omg.org" prefix to the CORBA module definition, rather than using #pragma prefix. This new keyword promotes the importance of prefixes and helps eliminate ambiguities regarding the scope and application of #pragma prefix, especially when one IDL file includes another. With the #pragma version directive and typeprefix keyword in effect, the repository ID for our example becomes:

IDL:omg.org/CORBA/InterfaceDef:3.0

Finally, you can use the #pragma ID directive or the new typeid keyword to set the whole repository ID, as follows:

module A {

 interface B { ... };

#pragma ID B "LOCAL:module(A)/interface(B)"

};

Both the #pragma ID directive and the typeid keyword must be followed by a scoped name and an identifier string. This example shows the use of a LOCAL repository ID format for the identifier. The CORBA specification includes the LOCAL format to allow applications to support their own custom repository ID strings. For this format, the identifier can be any string you want it to be as long as it follows the "LOCAL:" format identifier. Despite official CORBA support for it, the LOCAL format is rarely used in practice.

If we know the repository ID of an entity, we can easily look it up in an IFR. The Repository interface we mentioned above (i.e., the one that represents the outermost container in the IFR) provides the lookup_id operation for searching the IFR based on repository ID. The following example illustrates how to use the repository ID for this purpose:

 1  CORBA::Object_var obj =

 2 orb->resolve_initial_references

 3 ("InterfaceRepository");

 4  CORBA::Repository_var repos =

 5 CORBA::Repository::_narrow (obj.in ());

 6  CORBA::Contained_var contained =

 7 repos->lookup_id ("IDL:Stock/Quoter:1.0");

 8  CORBA::InterfaceDef_var intf_def =

 9 CORBA::InterfaceDef::_narrow (contained.in ());

10  // intf_def now refers to the interface definition

11  // for Stock::Quoter

Lines 1-5 obtain a Repository reference by passing "InterfaceRepository" to resolve_initial_references and narrowing the return value appropriately. Lines 5-6 then invoke lookup_id, passing in the default repository ID for the Stock::Quoter interface. The lookup_id operation returns Contained, which we narrow to InterfaceDef in lines 8-9. Note that this narrowing is based on static knowledge that Stock::Quoter is an interface, but we could avoid that by asking Contained what it is, as follows:

 // Lines 1-7 same as above.

 8  if (contained->def_kind () == CORBA::dk_Interface) {

 9 CORBA::InterfaceDef_var intf_def =

10      CORBA::InterfaceDef::_narrow (contained.in ());

11    // intf_def now refers to the interface

12    // definition for Stock::Quoter

13  }

The def_kind operation call on line 8 returns DefinitionKind, which is an enum that indicates what type of definition Contained refers to. Having verified that Contained actually does refer to an interface type, on lines 9-10 we safely narrow Contained to InterfaceDef.

Evaluating the IFR

When used in conjunction with other Dynamic CORBA features, such as the DII and DSI, the CORBA IFR supports powerful and type-safe metaprogramming capabilities [5]. CORBA applications can query an IFR to obtain metadata that describes IDL interface types, operation signatures, operation arguments and return types, and the definition of user-defined data types. DII clients and DSI servers can use the metadata provided in an IFR to construct "generic" applications whose behavior can be determined at run time (e.g., via an interpretive language like CorbaScript [6]). These generic applications can also be used to reduce the effort required to develop bridges that translate requests from an ORB in one domain to other ORBs in a different domain or other systems running different middleware technologies, such as COM or Java RMI.

For example, a generic CORBA bridge can use the DII, DSI, and IFR to process a two-way operation as follows:

·         Upon receiving a request, use the DSI API and IFR to decompose the request into a name/value list (NVList), which stores each argument in a separate Any data structure.

·         Use the resulting NVList to construct a DII request and use the DII API to send this request to the target object.

·         After receiving the DII reply, NVList holds the out arguments, so transfer it and the return value, or any raised exception, back into the DSI ServerRequest and reply to the original DSI request.

Such a bridge could serve, for example, as a generic dynamic load balancer, forwarding requests to the least loaded server among a set of replicas [7]. Achieving this sort of flexibility using statically compiled CORBA stubs and skeletons would be impractical.

Although the IFR specification is a powerful service that occupies a large portion of the CORBA standard, surprisingly few CORBA developers actually use the IFR in practice. The following are some of the key reasons:

·         Complexity. It should be clear from the discussion above that a generic ORB bridge can incur significant overhead, both in terms of time/space utilization and programming effort. For example, Dynamic CORBA operations and the IFR may require multiple data copies (e.g., copying the data from the request buffer into each one of the Anys of the DSI argument list and then later copying the reply from the DSI argument list into the DII request buffer). Copies are also required from the DII reply buffer into the Anys used to extract each argument and finally into the DSI reply buffer. There's also the overhead of having to make one or more expensive remote calls to the IFR to obtain the type information needed just to invoke the original desired request. Likewise, the programming APIs for the IFR are large and complicated, which makes them hard to use correctly. Our examples in this column provide only a sample of the complexity involved in navigating the IFR data structures and interfaces.

·         Consistency management. The IFR holds metadata about objects separately from the objects themselves. Although this design saves human and computer resources for applications that don't use an IFR, it also allows the IFR to easily get out of sync with the objects it is supposed to describe. It also requires you to remember to populate the IFR with your IDL definitions if you intend to use your objects in Dynamic CORBA applications. An alternative way of providing an IFR-like service would be to have the objects themselves provide their own metadata in a reflective manner. For example, rather than having Object::get_interface retrieve information from a separate IFR, the object's skeletons could return the information directly based on their own compile-time type information. This would ensure that the metadata would never be inaccurate or missing — if you can get to the object, then you are assured that you can also get to its metadata. Naturally, this approach would have increased the space utilization for applications that don't use the IFR, which helps explain why the OMG didn't mandate the tighter coupling between objects and their metadata.

Concluding Remarks

CORBA applications based on static stubs and skeletons have been used successfully in domains such as telecommunications, aerospace, process automation, and e-commerce. Static stubs and skeletons are not well suited, however, to shielding developers from the effects of changes to requirements or environmental conditions that occur late in an application's lifecycle (i.e., during deployment and/or at run time). For example, application developers may need to interact with objects whose interfaces did not exist when a distributed application was deployed initially. With Static CORBA, applying these types of changes requires tedious and error-prone redesign and reimplementation of existing application software. For applications such as interactive test programs or intermediary bridges that must be independent of the objects they interact with, Static CORBA simply does not suffice.

To address the limitations of Static CORBA outlined above, this column described the Dynamic CORBA IFR, which is a service that provides type information about CORBA interfaces and other entities defined in IDL. As described in our previous columns on Dynamic CORBA, one purpose of the DII and DSI mechanisms is to defer an application's binding onto specific interface types until run time. The IFR helps ensure the type-safety and correctness of these run-time bindings.

Our next column will discuss Portable Interceptors, which are yet another kind of Dynamic CORBA feature that an ORB invokes in the path of an operation invocation to monitor or modify the behavior of the invocation without changing the client or server application. If you have comments, questions, or suggestions regarding Dynamic CORBA or our column in general, please let us know at object_connect@cs.wustl.edu.

We'd like to thank Jeff Parsons for his helpful comments that improved the form and content of this column.

References

[1] Steve Vinoski and Douglas C. Schmidt. "Object Interconnections: Dynamic CORBA, Part 1: The Dynamic Invocation Interface," C/C++ Users Journal C++ Experts Forum, July 2002, <www.cuj.com/experts/2007/vinoski.htm>.

[2] Steve Vinoski and Douglas C. Schmidt. "Object Interconnections: Dynamic CORBA, Part 2: Dynamic Any," C/C++ Users Journal C++ Experts Forum, September 2002, <www.cuj.com/experts/2009/vinoski.htm>.

[3] Steve Vinoski and Douglas C. Schmidt. "Object Interconnections: Dynamic CORBA, Part 3: The Dynamic Skeleton Interface," C/C++ Users Journal C++ Experts Forum, November 2002, <www.cuj.com/experts/2011/vinoski.htm>.

[4] Michi Henning and Steve Vinoski. Advanced CORBA Programming with C++ (Addison-Wesley, 1999).

[5] Nanbor Wang, Douglas C. Schmidt, Ossama Othman, and Kirthika Parameswaran. "Evaluating Meta-Programming Mechanisms for ORB Middleware," IEEE Communication Magazine, October 2001.

[6] Object Management Group. "CORBA Scripting Language, v1.0," OMG Document formal/01-06-05, June 2001.

[7] Ossama Othman, Carlos O'Ryan, and Douglas C. Schmidt. "An Efficient Adaptive Load Balancing Service for CORBA," IEEE Distributed Systems Online, March 2001.

About the Author

Steve Vinoski is vice president of Platform Technologies and chief architect for IONA Technologies and is also an IONA Fellow. A frequent speaker at technical conferences, he has been giving CORBA tutorials around the globe since 1993. Steve helped put together several important OMG specifications, including CORBA 1.2, 2.0, 2.2, and 2.3; the OMG IDL C++ Language Mapping; the ORB Portability Specification; and the Objects By Value Specification. In 1996, he was a charter member of the OMG Architecture Board. He is currently the chair of the OMG IDL C++ Mapping Revision Task Force. He and Michi Henning are the authors of Advanced CORBA Programming with C++, published in January 1999 by Addison Wesley Longman. Steve also represents IONA in the W3C (World Wide Web Consortium) Web Services Architecture Working Group.

Doug Schmidt is a full professor at Vanderbilt University. His research focuses on patterns, optimization principles, model-based software development, and empirical analyses of object-oriented techniques that facilitate the development of high-performance, real-time distributed object computing middleware on parallel processing platforms running over high-speed networks and embedded-system interconnects. He is the lead author of the books Pattern-Oriented Software Architecture: Patterns for Concurrent and Networked Objects, published by Wiley and Sons, and C++ Network Programming: Mastering Complexity with ACE and Patterns and C++ Network Programming: Systematic Reuse with ACE and Frameworks both published by Addison-Wesley. He can be contacted at schmidt@isis-server.isis.vanderbilt.edu.