Previous Up Next

Chapter 2  Importing and Exporting Object References

2.1  Introduction

Although a CORBA server may contain many objects, it is typical for just one or two of these objects to be the initial point(s) of contact for client applications. For example, consider a server that contains one FooFactory object and many Foo objects. When the server starts up, it might export (advertise) an object references for its FooFactory object. When a client application starts, it imports the reference to the FooFactory object and then invokes, say, lookup() or create() operations on it to get access to some Foo objects. The pseudo-code below illustrates the main() function of a typical CORBA server that exports one object reference:

 1 main(int argc, char ** argv)
 2 {
 3     orb = CORBA::ORB_init(argc, argv);
 4     obj = ...;
 5     exportObjRefToNamingService(obj, ...);
 6     orb->run();
 7 }

The above code initializes CORBA (line 3). It then creates one or more objects (line 4) and exports one of these (line 5) to, say, the Naming Service, before going into the event loop (line 6) to receive incoming requests. For the purposes of brevity, some details have been omitted, such as creation of a POA hierarchy and activation of POA managers. The above code contains two common flaws:

  1. The server unconditionally (re-)exports the object reference every time the server is run. Doing this might appear to be a harmless practice, but it can cause problems in some kinds of deployment.
  2. The server is hard-coded to export the object reference to the Naming Service (line 5). Although the Naming Service is a popular place for exporting object references, the decision on where to export object references should be left to deployment time rather than being hard-coded at compilation time. For example, some users may prefer to export object references with another technology, such as, say, a Trading Service, a file, a database or FTP. A server that is hard-coded to export object references with one technology can prove to be inconvenient.

The rest of this paper discusses these two problems and explains how they can be overcome in simple, yet very effective ways.

2.2  Servers should Conditionally Export Object References

The pseudo-code below illustrates a good, high-level structure for a server that exports one object reference:

 1 main(int argc, char ** argv)
 2 {
 3     orb = CORBA::ORB_init(argc, argv);
 4     parseCmdLineArgs(argc, argv, exportMode, runMode);
 5     obj = new ...;
 6     if (exportMmode) {
 7         exportObjRef(orb, obj, "...");
 8     }
 9     if (runMode) {
10         orb->run();
11     }
12 }

The above code initializes CORBA (line 3) and then parses any remaining command-line arguments (line 4). Very importantly, the code checks for the presence of command-line options called, say, -export and -run and sets boolean variables exportMode and runMode to true if these options are used (line 4). The server exports its main object reference (line 7) only if the -export option was used. Likewise, it goes into the event loop (line 10) only if the -run option was used.

Designing a server to support the -export and -run options provides useful flexibility. For example:

  1. Some people may wish to re-export object references every time a server is restarted. Developers typically find this mode of operation to be convenient. This can be achieved by always specifying both -export and -run command-line options when running the server.
  2. Other people may prefer to export an object reference just once, during the initial installation of a server. This can be achieved by running the server with just the -export option, which causes the server to export its object reference and immediately die (because the -run option was not given). Once the server has been installed, thereafter the server can be launched with just the -run option, which will cause the server to go into its event loop without re-exporting its object reference.

    This mode of launching a server without re-exporting its object reference is vitally important if the deployment site is running, say, the Orbix Naming Service in replicated mode. In this mode, one replica of the Naming Service is the master and it can both read and update its database. All the other replicas of the Naming Service are slaves and they have read-only access to the database. If a slave receives a request that involves updating the database then it forwards that request to the master. If the master replica is not running then the Naming Service becomes read only until the master is restarted. The main purpose of having a replicated Naming Service is to prevent it from becoming it a single point of failure. This scheme works well if the only time a server exports an object reference is when the server is being installed and thereafter the server does not attempt to re-export an object reference whenever it is (re-)started. However, if a server insists on always exporting an object reference whenever it is re-started then the server may fail to start up if the master replica of the Naming Service is currently not running, thereby defeating the purpose of having a replicated Naming Service.

The point is that developers should provide a mechanism that allows exporting of object references and running of the server to be performed independently of each other. This then allows others to choose the policy of how the server should be used in practice. This flexibility is important because developers do not always know how others (such as system administrators) might like to deploy applications. Failing to provide this flexibility can actually cause significant hindrance, as discussed above in the case where a deployment site has a replicated Naming Service.

It may be useful to provide applications with a third option called, say, -install. When a server is run with this option, it performs all the steps required to install the server, such as set up configuration files and databases, register the server with the implementation repository and, of course, export an object reference. Alternatively, some people prefer to write a separate install utility to perform these steps, rather than embed this functionality into the executable of a server application. If you do provide an -install command-line option for a server then it is still useful to provide the -export option because a user may want to do a normal install initially and then later re-export the server’s object reference to a different location.

2.3  Flexibility in Importing/Exporting Object References

Some CORBA clients and servers are hard-coded to import and export object references through text files. This is especially common in demonstration programs in magazine articles or supplied with CORBA vendor products. This approach is simple but it suffers from a lack of geographic scalability because it requires that a client and server have access to a shared file system. Obviously, this will be the case if the client and server are running on the same computer, and it may be the case if they are running on different computers in the same local area network. However, it is rarer for a shared file system to span a wider area network. For this reason, many programmers are told that it is a bad idea to import and export object references through text files, and that use of the Naming Service is better. The result is that many programmers hard-code their clients and servers to import and export object references through the Naming Service.

In reality, it is a bad idea for a programmer to hard-code use of any particular import/export technology (such as text files or the Naming Service) in clients or servers. This is because applications have a habit of being used in ways that their developers did not foresee. For example, a developer might think that importing/exporting object references with the Naming Service is a good idea, but an end user might prefer to import/export with a database, or FTP, or even email. Sometimes the developer works in the same organization where the application is going to be deployed and hence s/he knows what is the preferred technology for importing/exporting object references. However, even in these situations, there might be a change in the preferred import/export technology in a few months time for any variety of reasons. For example, as the organization’s use of CORBA changes, they might prefer to import/export with a Trading Service rather than with a Naming Service. Alternatively, a system administrator might not want to allow a Naming Service to be accessed across a firewall and it might become more convenient to import/export object references through FTP instead. It is time consuming and expensive to have to modify an existing application’s hard-coded import/export logic whenever a change occurs in the organization’s preference for how object references should be imported or exported.

A better approach is for an application to offer some flexibility in how it imports/exports object references. Ideally, an application should be able to import/export object references with any technology (text files, Naming Service, Trading Service, fax, email, FTP, databases or whatever), and the choice of which technology to use should be left to deployment time. Achieving this goal turns out to be surprisingly easy, as we now discuss. Consider the following two utility functions (shown first in C++ and then in Java):

// C++ version (defined in "import_export.h")
namespace corbautil {
    CORBA::Object_ptr
    importObjRef(
        CORBA::ORB_ptr          orb,
        const char *            instructions)
            throw(ImportExportException);
    void
    exportObjRef(
        CORBA::ORB_ptr          orb,
        CORBA::Object_ptr       obj,
        const char *          instructions)
            throw(ImportExportException);
};

// Java version
package com.iona.corbautil;
import org.omg.CORBA.*;
public class ImportExport {
    static public org.omg.CORBA.Object
    importObjRef(
        ORB                      orb
        String                   instructions)
            throws ImportExportException;
    static public void
    exportObjRef(
        ORB                      orb
        org.omg.CORBA.Object     obj,
        String                   instructions)
            throws ImportExportException;
}

As their names suggest, importObjRef() and exportObjRef() are used to import and export object references. Both take a reference to an ORB as a parameter, so that they can invoke operations upon it—for example, string_to_object(), object_to_string() or resolve_initial_references()—as an aid to importing or exporting an object reference. Both functions also take an instructions parameter that specifies how the object reference should be imported or exported. This parameter will be discussed in detail shortly. Finally, the exportObjRef() function takes another parameter, which is the object reference to be exported, while importObjRef() returns the imported object reference.

2.3.1  Instructions Passed to exportObjRef()

The instructions parameter passed to exportObjRef() is a string that can be in any of the following formats:

The "name_service#..." and "file#..." variants are provided because users commonly want to export with the Naming Service or a file. The "exec#..." variant is provided as a fallback mechanism in case users want to export an object reference with a different technology. Specifically, many technologies (such as FTP, databases, email and so on) can be manipulated by command-line utilities. Exporting an object by such a technology is made possible by an "exec#..." instruction.

The "exec#..." instruction subsumes the power of both "name_service#..." and "file#..." because it can execute command-line utilities that will bind (advertise) an object reference into the Naming Service or write a stringified object reference to a file. However, the "name_service#..." and "file#..." variants are provided both for convenience and for efficiency.

The "java_class#..." variant is supported only in the Java implementation. This variant allows developers to implement alternative algorithms for exportObjRef() in Java. Details on how to write such algorithms are given in Section 2.4.

2.3.2  Instructions Passed to importObjRef()

The instructions parameter passed to importObjRef() is a string that can be in any of the following formats:

The "name_service#...", "java_class#..." and "file#..." instruction variants have a similar format for both exportObjRef() and importObjRef(). However, the "exec#..." variant is different, depending on whether it is used in exportObjRef() or importObjRef(). In exportObjRef(), "exec#..." takes an IOR place-holder, but in importObjRef() it does not take an IOR place-holder; instead, the executed command should write the stringified object reference to its standard output.

In addition, importObjRef() can accept instructions in any of the URL formats that are specified by the CORBA specification. These include "IOR:..", "corbaloc:..." and "corbaname:...". Since the CORBA specification may define new URL formats in the future (or a CORBA vendor may support proprietary URL formats), importObjRef() assumes that any string starting with letters and a colon is a URL and passes it as a parameter to string_to_object().

2.3.3  C++ Usage

The bold code in the pseudo-code below shows how a C++ server can export an object reference with the exportObjRef() function:

 1 #include "import_export.h"
 2 main(int argc, char ** argv)
 3 {
 4     orb = CORBA::ORB_init(argc, argv);
 5     parseCmdLineArgs(argc,argv, exportMode, runMode, instructions);
 6     obj = ...;
 7     if (exportMode) {
 8         try {
 9             corbautil::exportObjRef(orb, obj, instructions);
10         }
11         catch (const corbautil::ImportExportException & ex) {
12             cerr << ex << endl;
13             orb->destroy();
14             exit(1);
15         }
16     }
17     if (runMode) {
18         orb->run();
19     }
20     orb->destroy();
21 }

The instructions that specify where the object reference is exported to should not be hard-coded into the application, but rather should be obtained from, say, a command-line argument (line 5) or a runtime configuration file. If exportObjRef() fails for any reason then it throws an exception that contains a descriptive error message. For this reason, the call to exportObjRef() (line 9) is enclosed in a try-catch clause. If an exception is thrown then the exception message is printed out and the application gracefully terminates.

The use of importObjRef() is similar, and is shown in bold in the pseudo-code below:

 1 #include "import_export.h"
 2 main(int argc, char ** argv)
 3 {
 4     orb = CORBA::ORB_init(argc, argv);
 5     parse_cmd_line_args(instructions);
 6     try {
 7         obj = corbautil::importObjRef(orb, instructions);
 8     } catch (const corbautil::ImportExportException & ex) {
 9         cerr << ex << endl;
10         orb->destroy();
11         exit(1);
12     }
13     ... // narrow obj and invoke upon it
14 }

2.3.4  Java Usage

The bold code in the pseudo-code below shows how a Java server can export an object reference with the exportObjRef() function:

 1 import com.iona.corbautil.*;
 2 import org.omg.CORBA.*;
 3 ...
 4 public static void main(String[] args)
 5 {
 6     BooleanHolder exportMode = new BooleanHolder();
 7     BooleanHolder runMode = new BooleanHolder();
 8     StringHolder instructions = new StringHolder();
 9     orb = ORB.init(args);
10     parseCmdLineArgs(args, exportMode, runMode, instructions);
11     obj = ...;
12     if (exportMode.value) {
13         try {
14             ImportExport.exportObjRef(orb, obj,
15                     instructions.value);
16         } catch (ImportExportException ex) {
17             System.out.println(ex.getMessage());
18             orb.destroy();
19             System.exit(1);
20         }
21     }
22     if (runMode.value) {
23         orb.run();
24     }
25 }

The instructions that specify where the object reference is exported to should not be hard-coded into the application, but rather should be obtained from, say, a command-line argument (line 10) or a runtime configuration file. If exportObjRef() fails for any reason then it throws an exception that contains a descriptive error message. For this reason, the call to exportObjRef() (line 14) is enclosed in a try-catch clause. If an exception is thrown then the exception message is printed out and the application gracefully terminates.

The use of importObjRef() is similar, and is shown in bold in the pseudo-code below:

 1  import com.iona.corbautil.*;
 2 import org.omg.CORBA.*;
 3 ...
 4 public static void main(String[] args)
 5 {
 6     StringHolder instructions = new StringHolder();
 7     orb = ORB.init(args);
 8     parseCmdLineArgs(args, instructions);
 9     try {
10         obj = ImportExport.importObjRef(orb,
11                                         instructions.value);
12     } catch (ImportExportException ex) {
13         System.out.println(ex.getMessage());
14         orb.destroy();
15         System.exit(1);
16     }
17     ... // narrow obj and invoke upon it
18 }

2.4  Implementing Import/Export Algorithms as Java Classes

The ImportExportAlgorithm interface (defined in the com.iona.corbautil package) defines the signatures of the importObjRef() and exportObjRef() methods. If you write a Java class that implements this interface then you can use that class to import/export object references by using the "java_class#full.package.name.of.class" variant of instructions.

The code below is from the ImportExportExampleAlgorithm class that is provided in the com.iona.corbautil package. This code imports/exports object references through standard input/output and provides an example of how to write your own Java classes to import/export object references.

package com.iona.corbautil;
import org.omg.CORBA.*;
import java.io.*;
public class ImportExportExampleAlgorithm
implements ImportExportAlgorithm
{
    public void exportObjRef(
        ORB                   orb,
        org.omg.CORBA.Object  obj,
        String                instructions)
                                 throws ImportExportException
    {
        String strIOR = null;
        try {
            strIOR = orb.object_to_string(obj);
        } catch(Exception ex) {
            throw new ImportExportException(
                  "export failed for instructions ‘"
                + instructions
                + "’: object_to_string() failed: " + ex);
        }
        System.out.println("instructions = ‘"
                           + instructions + "’");
        System.out.println("IOR = " + strIOR);
    }
    public org.omg.CORBA.Object importObjRef(
        ORB     orb,
        String  instructions) throws ImportExportException
    {
        System.out.println("instructions: " + instructions);
        System.out.println("Enter a stringified obj ref: ");
        try {
            BufferedReader stdin = new BufferedReader(
            new InputStreamReader(System.in));
            String strIOR = stdin.readLine();
            return orb.string_to_object(strIOR);
        } catch (Exception ex) {
            throw new ImportExportException(
                  "import failed for "
                + "instructions ‘" + instructions
                + "’: error importing a stringified "
                + "object reference from the console: "
                + ex);
        }
    }
}

2.5  Benefits of importObjRef() and exportObjRef()

The importObjRef() and exportObjRef() functions offer several benefits:


Previous Up Next