Bootstrapping Techniques

Middleware News Brief (MNB) features news and technical information about Open Source middleware technologies.

INTRODUCTION

When developing a CORBA application, you must eventually tackle the issue of how to locate your CORBA objects. This issue is sometimes referred to as bootstrapping. In this Middleware News Brief, we discuss the issues raised by – and choices available for – bootstrapping a CORBA application so that each of the processes may access the services of its peer processes. We describe the options for developers and consider the performance and reliability/fault tolerance tradeoffs of each of the techniques.

In deciding how to bootstrap an application, there are several issues that must be dealt with:

  • Should you connect to all of the components at process startup or spread out the connection process to the latest possible moment?
  • What should your application do if a desired CORBA object is not when you attempt to access it?
  • What run-time or startup constraints can you handle in your application?

These issues and a few others will drive the decision as to which approach is best suited for you.

CORBA provides several standardized building blocks for us to use as a solution to the bootstrap problem. These mechanisms provide the benefits of being adopted CORBA standards available in every CORBA 2.5 or higher ORB. The approaches can be separated into three categories.

  • Object location services. Two object location services have been specified by the OMG: the Naming Service and the Trading Service.
  • Initialization API. CORBA provides a standard way to provide object references to an application through the use of an operation on the ORB object: resolve_initial_references. This operation is used to map a simple string (e.g., NameService) to a CORBA object reference.
  • Object reference encoding. Object references may be transformed into strings and transferred somehow to the client application. The first string representation of object references representation is encoded as an opaque sequence of strings that can be decoded by the ORB. The other is encoded in the form of a Universal Resource Locator (URL).

All of these approaches are discussed in the following paragraphs.

Object Location Services

NAMING SERVICE

The Naming Service is by far the most common solution to the application-object bootstrap issue. It is a standard CORBA service that can be used to store application objects via a user-provided, hierarchical name. The OMG envisioned the Naming Service as a standard mechanism by which application objects can be registered by the server and accessed by distributed client applications.

Since the Naming Service is a CORBA object, it also must be bootstrapped into the client and server processes. This detail, however, can be hidden from the application software. The OMG specifies an operation on the ORB for querying for certain critical components, including the Naming Service: resolve_initial_references. You may also register your own application object references using this mechanism, and this approach is described in its own section below. We will ignore this issue for now.

In the server process, you associate the object reference with a name. This is called binding. The server must bind the object reference to a specified name in the namespace.

When populating the namespace you must create the initial bindings, and subsequently determine if the bindings are still in place. This code can be somewhat clumsy due to the use of exceptions to detect the existence of a binding that may have been inadvertently left from a previous run, or the absence of a binding that should have been created previously.

An example of binding an object reference is shown below:

  1. //Create and bind the hotels naming context. If it already exists, consume
  2. //the AlreadyBound exception. The naming context will be created exactly once.
  3. CosNaming::Name name;
  4. name.length(1);
  5. name[0].id = CORBA::string_dup(hotels);
  6.  
  7. CosNaming::NamingContext_var hotelsNC;
  8.  
  9. //Place the branch hotels in the namespace
  10. // rootContext is a variable of type CosNaming::NamingContext
  11. try {
  12. hotelsNC = rootContext->bind_new_context(name);
  13. }
  14. //We must have left the branch in the namespace from the last run
  15. catch (CosNaming::NamingContext::AlreadyBound&) {
  16. // consume the exception
  17. }
  18.  
  19. // Create the Hotel California name.
  20. name.length(2);
  21. name[1].id = CORBA::string_dup("Hotel California");
  22.  
  23. // Create a Hotel implementation object (servant), and place its
  24. // object reference in the variable hotelObj
  25. // Rebind the object ref to the name Hotel California in the
  26. // hotels naming context, overwriting any previous binding.
  27. // Rebind throws away any previous binding.
  28. rootContext->rebind(name, hotelObj);

The client obtains the object reference by resolving the specified name to a CORBA object reference.

An example of the code required to perform this resolution is shown below:

  1. // Build the compound name.
  2. CosNaming::Name hotelName;
  3. hotelName.length(2); // the compound name will consist of two elements
  4. hotelName[0].id = CORBA::string_dup("hotels");
  5. hotelName[1].id = CORBA::string_dup("Hotel California");
  6.  
  7. // Get the object reference from the Naming Service
  8. // rootContext is a variable of type CosNaming::NamingContext
  9. CORBA::Object_var hotelObj = rootContext->resolve(hotelName);
  10.  
  11. //Must narrow the returned CORBA::Object to its interface type -- a Hotel
  12. Hotel_var hotelCalifornia = Hotel::_narrow(hotelObj.in());
  13. ...
  14. //May use the Hotel to access other objects (e.g., Guest Rooms)

The interface for using the Naming Service was simplified as part of the Interoperable Naming Service [ptc/99-09-01]. A new interface, NamingContextExt, which inherits from NamingContext, allows a client to pass in a string representation of the compound name. This makes the creation of complex naming schemes easier and easier to understand and configure.

Example usage of the new interface is shown below.

  1. CORBA::Object_var obj = orb->resolve_initial_references("NameService");
  2. CosNaming::NamingContextExt_var rootNC =
  3. CosNaming::NamingContextExt::_narrow(obj.in());
  4.  
  5. //Simplified interface does not require creation of compound name structure
  6. //Just use a string to specify the name with a '/' separator.
  7. CORBA::String_var nameStr = CORBA::String_dup ("hotel/HotelCalifornia");
  8. obj = rootNC->resolve_str(nameStr);
  9.  
  10. //Must narrow the CORBA::Object to a Hotel
  11. Hotel_var hotel = Hotel::_narrow(obj.in());

The Naming Service provides a standard, straightforward mechanism for locating application objects. However, its use does impact the reliability and performance of the application.

Using the Naming Service as a separate process adds an additional failure point in a distributed application. Although distributed applications must deal with the potential failure of each of its processes, if the Naming Service is used to locate crucial application objects, it takes on the role of a critical component. Failure to reach the Naming Service in these circumstances can prevent further progress by the application.

The performance impact of the Naming Service is usually incurred during the startup of the client and the server processes. The server process must bind the object reference with the Naming Service process and the client process must resolve it. This results in additional overhead since it requires multiple remote CORBA invocations for the server to build the name structure and bind the object reference and for the client to acquire the object reference. With TAO it is possible to collocate the Naming Service with one of the application processes to alleviate some of the overhead; however, this topic is not explored in this discussion.

Some of the issues inherent in the use of the Naming Service are:

  • What to advertise. Not all CORBA objects need to be stored in the Naming Service. Typically only a few objects from the application are placed in the Naming Service, some of which will provide an interface for creating or accessing the other CORBA objects. Many of the issues of what to advertise are discussed in [H&V, p. 811].
  • How to partition the namespace. The namespace consists of a tree structure much like the filesystem on a computer. The developer must decide the names of the branches and decide where in this tree structure to place the application object references.
  • Avoiding dangling object references. If the application that bound the object reference is shutdown or crashes, the object reference must be unbound; otherwise users might access the object reference and attempt to use it, thereby receiving an exception.
  • Preventing unexpected or undesired modifications to the namespace. By default, the Naming Service allows anyone to make changes to the namespace. I have heard stories of developers accidentally accessing the production name service with test code and inadvertently accessing live service objects. This could result in costly or dangerous situations in certain domains and needs to be guarded against.
  • Dealing with race conditions between client and server. This is actually an issue for all of approaches. If the client tries to locate the service prior to its binding in the name service, it will obviously be unsuccessful. The developer must implement some mechanism for waiting for the binding to appear in the Naming Service before continuing. This issue must also be handled for applications that wish to handle server shutdown either due to failure or maintenance. In these cases the consumer must have a strategy for what to do if the object requested is not registered yet.
  • Maintaining the namespace. Periodically, old branches in the namespace will need to be removed manually. This happens as a result of changes in the naming scheme and retiring of applications that have outlived their usefulness. Eventually the namespace will need to be cleaned up.

TRADING SERVICE

The Trading Object Service [formal/00-06-27] is similar in purpose to the Naming Service but is tailored more toward allowing a client to select one of several objects based on the interface it supports, as well as other service description parameters.

Some of the primary differences are that the object references are advertised with a set of properties that describe characteristics of the object that may be orthogonal to the functional services of the object. The server is also not necessarily the only publisher of this information and the data can be managed through an external interface including a GUI or command line interface.

This service is particularly useful for applications where bootstrap objects are selected based on dynamic criteria. A complex interface makes the use of this service more difficult. Certain domains where there are multiple objects that can satisfy the bootstrap needs of the application might benefit from the use of the Trader. Applications where there is only one instance of the server will not benefit due to the complexity of the trader interface.

This specification is much more complex but provides a great deal more flexibility than the Naming Service. Its usefulness also extends beyond the pure brokering of client and server connection establishment. The service has been defined to support business oriented concepts such as locating a service that provides capabilities and services that best support the applications developer according to orthogonal properties (e.g., quality of service, or price). This service allows a set of criteria to be defined to allow the selection of a service that most closely matches the needs of the application.

The added complexity of the Trading Service interface over the Naming Service makes it an unlikely candidate for use to bootstrap an application. Its primary benefit is in providing flexibility to a client to select an implementation that best suits their needs dynamically.

Initialization API

USING resolve_initial_references

The need for bootstrapping was anticipated by the OMG, and their solution was to provide a standard API for accessing certain services (initial services) which are critical to the functioning of the application. To access initial services, the developer requests an object reference from the ORB using a simple string to identify the initial reference service. For the Naming Service the OMG reserved the string NameService (see the example code below).

        CORBA::Object_var init_ref = orb->resolve_initial_references("NameService");
	CosNaming::NamingContext_var nc = CosNaming::NamingContext::_narrow(init_ref);

The simplicity of this API belies the complexity of the actual specification of these initial services to the application upon startup. The resolve_initial_references API does not solve the problem alone, but must be combined with one of the other approaches: stringified object references and object URLs.

A standard startup option is defined to allow the user to identify the object that will serve the initial object responsibility (-ORBInitRef). This command line option allows you to provide a stringified object reference or an object URL (discussed later) and assign it to a string that may be used in the resolve_initial_references call. You can use this to define the location for the Naming Service as well as your own initial service with which you wish to bootstrap your application.

To define an application object as an initial service, the developer provides a command line option similar to the one shown below to start the client application:

        >  "MyClient -ORBInitRef MyInitialService="file://service.ior"

After starting the application, the developer may access the object reference for the initial service using the string MyInitialService as shown in the example using the NameService string. In this example, we have supplied the object reference in a file called service.ior, thereby hiding the complexity of how the object reference is transferred to the client from the specified service. The client may then obtain the object reference using the ORB method resolve_initial_references.

        CORBA::Object_var init_ref = orb->resolve_initial_references("MyInitialService");
	MyServiceType_var serv = MyServiceType::_narrow(init_ref);

The performance overhead of this approach is not much greater than the mechanism by which the initial service is accessed. See the following sections for a discussion of the performance impacts of accessing a service via a stringified object reference.

Some of the issues with the use of resolve_initial_references are:

  • Incomplete solution. You must still determine how you will identify the CORBA object provided back to the client. One of the following approaches must be used in combination with this approach.
  • Consistency. Each client application must specify the same object reference through the -ORBInitRef option. A change in the required option value will need to be made to each of the client applications.

Object Reference Encoding

STRINGIFIED OBJECT REFERENCES

Another standard approach for bootstrapping distributed applications is for the server to encode an object reference as a string (stringify) that may be communicated to clients. The clients decode the object reference from the string (destringify).

There are several means by which the object reference may be communicated to the client:

  • Write it to a file or another persistent store
  • Encode it in the client executable
  • Pass it in as a command line argument

An example of using a file to access a stringified object reference is shown below:

        CORBA::Object_var obj = orb->string_to_object("file://bank.ior");
	Bank_var my_bank = Bank::_narrow(obj.in());

In this example:

  1. The string identifies the file containing the actual object reference details
  2. The ORB loads the file, "bank.ior," located in the directory in which the application was started and decodes the stringified object reference in order to generate the CORBA object reference

Stringified object references are standardized and are interoperable between ORBs provided by different vendors. For this reason, a stringified object reference is also known as an Interoperable Object Reference or IOR. Since they can be stored in a persistent store, they may be rapidly accessed.

If the servant object is a persistent object, the object reference and the server will not change location, and the IOR may be encoded into the application for use in embedded systems that may not have access to a persistent store. This approach is primarily useful only for persistent objects and not transient objects.

When developing services, you can identify addressing information that can be used to locate the service by other applications. This is called an endpoint. To specify the endpoint for CORBA objects in your application, you use the -ORBListenEndpoints option when starting the application. The endpoint specifies the protocol, the address, and the port on which the service's ORB will listen for connections. See section 16.8.9 in the TAO Developers guide for additional information and options.

        > MyApplication -ORBListenEndpoints iiop://myhost:9999

This option specifies that each of the objects implemented in this application will be accessed through the IIOP protocol on the hostname "myhost" with port number 9999. This information is encoded in the object references for each CORBA object activated in this process.

This approach does not suffer from the performance impacts imposed by using a separate server process like the Naming and Trading Service. The location of the server is encoded directly into the IOR string and may be used to establish a connection with a minimum amount of overhead.

If the IOR is stored in a common file system between the client and server, there may be fault tolerance issues arising from a loss of access to the shared storage by one or more of the applications.

Some of the issues with the use of stringified object references are:

  • Method of transfer. Stringified object references must be shared between the client and server via some form of media (e.g., a shared filesystem, email, etc.). Requiring the client and server to share a filesystem reduces the deployment options for the application. This limits its applicability for widely distributed applications (i.e., where the deployed nodes are separated by large distances). Email or other manual transmission techniques will result in a large latency between the startup of the server application and the access by the client.
  • Unreliable comparison. IORs cannot be reliably compared since the same string may not be generated in subsequent invocations for the same CORBA object. However, it is always guaranteed to denote the same object for as long as that CORBA object exists.
  • Race conditions. The server must write the IOR to the filesystem prior to the access of this IOR by the client. It is also the responsibility of the server to remove or replace the file when its characteristics (addressing, protocol, etc.) are changed.

OBJECT URLS

corbaloc

As part of the Interoperable Naming Service specification [ptc/99-09-01], the corbaloc style object reference was added to the CORBA standard. Detailed documentation on corbaloc URL scheme may be found in section 13.6.10.1 of the CORBA 2.6 specification [formal/01-12-01].

In earlier versions of TAO (prior to version 1.2), support was provided for the iioploc format – the predecessor to the corbaloc specification. References in the corbaloc format may be used to contact servers built with any version of TAO as long as the client ORB supports them.

IORs can be complex and difficult to exchange, so the corbaloc specification allows you to specify object references using a URL scheme. It is quite similar to the way ftp and http URLs are specified.

To address the problem of bootstrapping and allow for more convenient exchange of human-readable object references, ORB::string_to_object allows URLs in the corbaloc format to be converted into CORBA object references. To specify an object reference, you use this URL format to identify the location and key of the object that you wish to communicate with in the following way:

corbaloc:iiop:xyz.ociweb.com:2809/server

The iiop protocol is the default and can be omitted if IIOP is being used. Also, the default port number is 2809.

So, the above format can be simplified to:

corbaloc::xyz.ociweb.com/server

This format is independent of the Naming Service.

To allow access to the object via a corbaloc URL, the server must register the object reference that it implements. To accomplish this, you will need to register the CORBA object in the server ORB's IOR table using a simple key.

To bind an object reference in current versions of TAO (1.1.10 and later):

  1. // Turn your object reference into an IOR string
  2. CORBA::String_var ior_string = orb->object_to_string(obj.in());
  3.  
  4. // Get a reference to the IOR Table
  5. CORBA::Object_var tobj = orb->resolve_initial_references("IORTable");
  6. IORTable::Table_var table = IORTable::Table::_narrow(tobj.in());
  7.  
  8. // Bind your stringified IOR in the IOR Table
  9. table->bind("CORBABank", ior_string.in());

In order to access this service, you will need to #include tao/IORTable/IORTable.h and link with the TAO_IORTable library.

In older versions of TAO (1.1.9 and prior) this binding is accomplished via the following code:

        // Add our Bank object to the ORB's IOR table (TAO specific).
	orb->_tao_add_to_IOR_table( "CORBABank", obj.in() );

This makes it possible for corbaloc-style object references to locate the specified CORBA object using:

  • A hostname
  • A port
  • A simple object key (CORBABank)

When we run the TAO server, we must ensure that it listens on a host and port that are known to the client, as shown in the discussion on stringified object references above. Again, we use TAO's -ORBListenEndpoints command-line option to do this.

The format for the -ORBListenEndpoints option is:

-ORBListenEndpoints iiop://host:port

For example, if I'm running my server on host myhost, listening on port 11019:

> server -ORBListenEndpoints iiop://myhost:11019

When running the client, we'll use a corbaloc object reference.

The format of the corbaloc object reference is:

corbaloc:protocol:host:port/ObjectKey

For example:

corbaloc:iiop:myhost:11019/CORBABank

connects to the server on host myhost at port 11019 and finds the object reference in that server corresponding to the CORBABank key. This object reference can be passed to ORB::string_to_object(), and the result is used like any other object reference as shown below:

        CORBA::Object_var obj = orb->string_to_object("corbaloc:iiop:myhost:11019/CORBABank");
	Bank_var my_bank = Bank::_narrow(obj.in());

The URL may also be passed to the application using the -ORBInitRef option and used as described in the earlier section.

For example:

> MyApplication -ORBInitRef MyCORBABank=corbaloc:iiop:myhost:11019/CORBABank

Some issues with the use of corbaloc are:

  • Exposes deployment details. The OMG originally attempted to keep the IOR opaque to avoid this issue, but the necessity for bootstrapping has made the capability to specify these characteristics using the corbaloc URL object reference valuable. If the servers addressing or protocol characteristics change, the URL will need to be modified.
  • Key mapping not standardized. The mechanism for registering an object reference with a Simple Key in the server is not standardized yet. The approach shown above is only valid for TAO.

corbaname

The corbaname URL object reference style [ptc/99-09-01] extends the corbaloc approach for use in combination with the Naming Service.

In a corbaname URL, a compound name string can be added after the corbaloc portion of the URL. The corbaloc portion of the URL must specify an object reference for a Naming Context object. The compound name string is then passed on to the Naming Context object specified by the corbaloc portion of the URL.

For example, to request the CORBABank object from a Naming Service on "myhost" at port 9999, you would use the following:

corbaname::myhost:9999#hotel/HotelCalifornia

This corbaname URL specifies that a NamingContext at the specified corbaloc URL will return the result of the resolve operation with the (stringified) name "hotel/HotelCalifornia". This object URL may be used as an argument to string_to_object or as a value for the -ORBInitRef option for an application.

Some issues with the corbaname approach:

  • Shares issues with Naming Service and corbaloc. Since it uses the Naming Service combined with the corbaloc approach, it shares the same issues with both.
  • Reduced control. The simplicity of the corbaname approach also has drawbacks. It makes it more difficult to deal with low-level issues that are identified by exceptions returned individual invocations using the Naming Service.

Summary

Bootstrapping a CORBA application is an unavoidable consideration. Several standard mechanisms have been provided to support you in the development of a bootstrapping approach. Although many factors influence this decision, a careful examination of the issues and the requirements of an individual system will aid you in developing the best approach.

References