Bootstrapping Techniques

by Kevin Stanley
Principal Software Engineer and Partner
Object Computing, Inc. (OCI)

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 CORBA News Brief we will 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 will describe the options for developers and consider the performance, and reliability/fault tolerance tradeoffs of each of the techniques.

In deciding how to bootstrap the 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.

All of these approaches are discussed in the following paragraphs.

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:

	//Create and bind the  hotels naming context.  If it already exists, consume
	//the AlreadyBound exception.  The naming context will be created exactly once.
	CosNaming::Name name;
	name.length(1);
	name[0].id = CORBA::string_dup(hotels);

	CosNaming::NamingContext_var hotelsNC;

	//Place the branch hotels in the namespace
	// rootContext is a variable of type CosNaming::NamingContext
	try {
		hotelsNC = rootContext->bind_new_context(name);
	}
	//We must have left the branch in the namespace from the last run
	catch (CosNaming::NamingContext::AlreadyBound&) {
	// consume the exception
	}

	// Create the Hotel California name.
	name.length(2);
	name[1].id = CORBA::string_dup("Hotel California");

	// Create a Hotel implementation object (servant), and place its 
	// object reference in the variable hotelObj
	// Rebind the object ref to the name Hotel California in the 
	// hotels naming context, overwriting any previous binding.  
	// Rebind throws away any previous binding.
	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:

	// Build the compound name.
	CosNaming::Name hotelName;
	hotelName.length(2);       // the compound name will consist of two elements
	hotelName[0].id = CORBA::string_dup("hotels");
	hotelName[1].id = CORBA::string_dup("Hotel California");

	// Get the object reference from the Naming Service
	// rootContext is a variable of type CosNaming::NamingContext
	CORBA::Object_var hotelObj = rootContext->resolve(hotelName);

	//Must narrow the returned CORBA::Object to its interface type -- a Hotel
	Hotel_var hotelCalifornia = Hotel::_narrow(hotelObj.in());
	...
	//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.

	CORBA::Object_var obj = orb->resolve_initial_references("NameService");
	CosNaming::NamingContextExt_var rootNC = 
	CosNaming::NamingContextExt::_narrow(obj.in());
	
	//Simplified interface does not require creation of compound name structure
	//Just use a string to specify the name with a '/' separator.
	CORBA::String_var nameStr = CORBA::String_dup ("hotel/HotelCalifornia");
	obj = rootNC->resolve_str(nameStr);

	//Must narrow the CORBA::Object to a Hotel
	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, then 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:

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.

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:

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, or 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, the string identifies the file containing the actual object reference details. In this example, the ORB will load 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, 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:

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 how 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):

	// Turn your object reference into an IOR string 
	CORBA::String_var ior_string = orb->object_to_string(obj.in());

	// Get a reference to the IOR Table 
	CORBA::Object_var tobj = orb->resolve_initial_references("IORTable");
	IORTable::Table_var table = IORTable::Table::_narrow(tobj.in());

	// Bind your stringified IOR in the IOR Table 
	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, and 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 of the issues with the use of corbaloc are:

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 of the issues of the corbaname approach:

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 there are many factors that 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

[H&V] M. Henning and S. Vinoski. Advanced CORBA Programming with C++. Addison Wesley Longman, Inc., 1999.

[formal/01-12-01] CORBA 2.6 specification

[formal/00-06-27] Trading Object Service

[ptc/99-09-01] Interoperable Naming Service Specification


OCI Educational Services

Object Computing, Inc (OCI) has been providing educational services to clients, industries and universities since 1993. We offer one of the most comprehensive distributed Object Oriented training curricula in the country. These curricula focus on the fundamentals of OO technology; with close to 40 workshops in OOAD, Java, XML, C++/CORBA and Unix/Linux.

Java C/C++ .NET/C# Real-Time Systems Object Oriented Software Engineering Distributed Computing Wireless Enterprise Unix/Linux XML

For further information regarding OCI's Educational Services programs, please visit our Educational Services section on the web or contact us at training.


The OCI CORBA News Brief is intended to promote CORBA and object technology in the development of distributed computing applications. Each issue of the CORBA News Brief will feature news and technical information about OCI's supported open-source ORBs (TAO and JacORB), case studies, and examples using CORBA, as well as information about OCI's educational offerings.

The OCI CORBA News Brief is published on a monthly basis. Send ideas for articles of interest to corba.

To subscribe or unsubscribe from the CNB mailing list, send mail to majordomo with the line "subscribe cnb" or "unsubscribe cnb" in the body of the message.