Java IDL and CORBA


Unlike RMI, CORBA lets you make calls between Java objects and objects written in other languages. CORBA depends on having an Object Request Broker (ORB) available on both client and server. You can think of an ORB as a kind of universal translator for interobject CORBA communication. The CORBA 2 specification defines more than a dozen "services" that the ORB can use for various kinds of housekeeping tasks. These range from a "startup service" to get the process going, to a "life cycle service" that you use to create, copy, move, or destroy objects, to a "naming service" that allows you to search for objects if you know their name.

JDK 1.2 introduced an implementation of a CORBA 2-compliant ORB, giving Java applications and applets the ability to connect to remote CORBA objects.

NOTE

Sun refers to the CORBA support as "Java IDL." That term is really a misnomer. IDL refers to the interface definition language, a language for describing class interfaces. The important aspect of the technology is connectivity with CORBA, not just support for IDL.


Here are the steps for implementing CORBA objects:

1.

Write the interface that specifies how the object works, using IDL, the interface definition language for defining CORBA interfaces. IDL is a special language to specify interfaces in a language-neutral form.

2.

Using the IDL compiler(s) for the target language(s), generate the needed stub and helper classes.

3.

Add the implementation code for the server objects, using the language of your choice. (The skeleton created by the IDL compiler is only glue code. You still need to provide the actual implementation code for the server methods.) Compile the implementation code.

4.

Write a server program that creates and registers the server objects. The most convenient method for registration is to use the CORBA naming service, a service that is similar to the rmiregistry.

5.

Write a client program that locates the server objects and invokes services on them.

6.

Start the naming service and the server program on the server, and start the client program on the client.

These steps are quite similar to the steps that you use to build distributed applications with RMI, but with two important differences:

  • You can use any language with a CORBA binding to implement clients and servers.

  • You use IDL to specify interfaces.

In the following sections, you will see how to use IDL to define CORBA interfaces, and how to connect clients implemented in Java with C++ servers and C++ clients with servers implemented in Java.

However, CORBA is a complex subject, and we give you only a couple of basic examples to show you how to get started. For more information, we recommend Client/Server Programming with Java and CORBA by Robert Orfali and Dan Harkey [John Wiley & Sons 1998]. More advanced, and definitely not for the faint of heart, is Advanced CORBA Programming with C++ by Michi Henning and Steve Vinoski [Addison-Wesley 1999].

The Interface Definition Language

To introduce the IDL syntax, we quickly run through the same example that we used for RMI. In RMI, you started out with an interface in the Java programming language. With CORBA, the starting point is an interface in IDL syntax:

 interface Product {    string getDescription(); }; 

There are a few subtle differences between IDL and Java. In IDL, the interface definition ends with a semicolon. Note that string is written in lower case. In fact, the string class refers to the CORBA notion of a string, which is different from a Java string. In the Java programming language, strings contain 16-bit Unicode characters. In CORBA, strings contain only 8-bit characters. If you send the 16-bit string through the ORB and the string has characters with nonzero high byte, an exception is thrown. This kind of type mismatch problem is the price you pay for interoperability between programming languages.

NOTE

CORBA also has wchar and wstring types for "wide" characters. However, there is no guarantee that wide character strings use the Unicode encoding.


The "IDL to Java" compiler (Java IDL compiler) translates IDL definitions to definitions for Java interfaces. For example, suppose you place the IDL Product definition into a file Product.idl and run

 idlj Product.idl 

The result is a file ProductOperations.java with the following contents

 interface ProductOperations {    String getDescription(); } 

and a file Product.java that defines an interface

 public interface Product extends    ProductOperations,    org.omg.CORBA.Object,    org.omg.CORBA.portable.IDLEntity { } 

NOTE

In JDK1.2, the idltojava program is used to translate IDL files.


The IDL compiler also generates a number of other source filesthe stub class for communicating with the ORB and three helper classes that you will encounter later in this section and the next.

NOTE

You cannot do any programming in IDL. IDL can only express interfaces. The CORBA objects that IDL describes must still be implemented, for example, in C++ or Java.


The rules that govern the translation from IDL to the Java programming language are collectively called the Java programming language binding. Language bindings are standardized by the OMG; all CORBA vendors are required to use the same rules for mapping IDL constructs to a particular programming language.

We do not discuss all aspects of IDL or the Java programming language bindingsee the CORBA documentation at the web site of the Object Management Group (http://www.omg.org) for a full description. However, there are a number of important concepts that every IDL user needs to know.

When defining a method, you have more choices for parameter passing than the Java programming language offers. Every parameter can be declared as in, out, or inout. An in parameter is simply passed to the methodthis is the same parameter-passing mechanism as in Java. However, Java has no analog to an out parameter. A method stores a value in each out parameter before it returns. The caller can retrieve the values stored in out parameters.

For example, a find method might store the product object that it has found:

 interface Warehouse {    boolean locate(in String descr, out Product p);    . . . }; 

If the parameter is declared as out only, then the method should not expect the parameter to be initialized. However, if it is declared as inout, then the caller needs to supply a value for the method, and the method can change that value so that the caller can retrieve the changed value. In Java, these parameters are simulated with special holder classes that are generated by the Java IDL compiler.

The IDL compiler generates a class with suffix Holder for every interface. For example, when the Product interface is compiled, it automatically generates a ProductHolder class. Every holder class has a public instance variable called value.

When a method has an out parameter, the IDL compiler changes the method signature to use a holder, for example,

 interface Warehouse {    boolean locate(String descr, ProductHolder p);    . . . }; 

When calling the method, you must pass in a holder object. After the method returns, you retrieve the value of the out parameter from the holder object. Here is how you call the locate method.

 Warehouse w = . . .; String descr = . . .; Product p; ProductHolder pHolder = new ProductHolder(); if (w.locate(descr, pHolder))    p = pHolder.value; 

Holder classes are predefined for fundamental types (such as IntHolder, DoubleHolder, and so on).

NOTE

IDL does not support overloaded methods, so you must come up with a different name for each method.


In IDL, you use the sequence construct to define arrays of variable size. You must first define a type before you can declare sequence parameters or return values. For example, here is the definition of a "sequence of products" type.

 typedef sequence<Product> ProductSeq; 

You then use that type in method declarations:

 interface Warehouse {    ProductSeq find(in Customer c);    . . . }; 

In the Java programming language, sequences correspond to arrays. For example, the find method is mapped to

 Product[] find(Customer c) 

If a method can throw an exception, you first define the exception type and then use a raises declaration. In the following example, the find method can raise a BadCustomer exception.

 interface Warehouse {    exception BadCustomer { string reason; };    ProductSeq find(in Customer c) raises BadCustomer;    . . . }; 

The IDL compiler translates the exception type into a class.

 final public class BadCustomer    extends org.omg.CORBA.UserException {    public BadCustomer() {}    public BadCustomer(String __reason) { reason = __reason; }    public String reason; } 

If you catch such an exception, you can look into its public instance variables.

The raises specifier becomes a throws specifier of the Java method

 ProductSeq find(Customer c) throws BadCustomer 

Interfaces can contain constants, for example,

 interface Warehouse {    const int SOLD_OUT = 404;    . . . }; 

Interfaces can also contain attributes. Attributes look like instance variables, but they are actually shorthand for a pair of accessor and mutator methods. For example, here is a Book interface with an isbn attribute:

 interface Book {    attribute string isbn;    . . . }; 

The Java equivalent is a pair of methods, both with the name isbn:

 String isbn() // accessor void isbn(String __isbn) // mutator 

If the attribute is declared as readonly, then no mutator method is generated.

You cannot specify variables in CORBA interfacesthe data representation for objects is part of the implementation strategy, and IDL does not address implementation at all.

CORBA supports interface inheritance, for example,

 interface Book : Product { /* . . . */ }; 

You use the colon (:) to denote inheritance. An interface can inherit multiple interfaces.

In IDL, you can group definitions of interfaces, types, constants, and exceptions into modules.

 module corejava {    interface Product    {       . . .    };    interface Warehouse    {       . . .    }; }; 

In Java, modules are translated to packages.

Once you have the IDL file, you run the IDL compiler that your ORB vendor supplies to get stubs and helper classes for your target programming language (such as Java or C++).

For example, to convert IDL files to Java, you run the idlj program. Supply the name of the IDL file on the command line:

 idlj Product.idl 

The program creates five source files:

  • Product.java, the interface definition;

  • ProductOperations.java, the interface that contains the actual operations (Product extends ProductOperations as well as a couple of CORBA-specific interfaces);

  • ProductHolder.java, the holder class for out parameters;

  • ProductHelper.java, a helper class, which we use in the next section;

  • _ProductStub.java, the stub class for communicating with the ORB.

The same IDL file can be compiled to C++. We use a freely available ORB called omniORB for our examples. The omniORB package contains an IDL-to-C++ compiler called omniidl. To generate C++ stubs, invoke it as

 omniidl -bcxx Product.idl 

You get two C++ files:

  • Product.hh, a header file that defines classes Product, Product_Helper, and POA_Product (the superclass for the server implementation class);

  • ProductSK.cc, a C++ file that contains the source code for these classes.

NOTE

Although the language binding is standardized, it is up to each vendor to decide how to generate and package the code that realizes the binding. IDL-to-C++ compilers of other vendors will generate a different set of files.


A CORBA Example

In our first example, we show you how to call a C++ server object from a Java client, using the CORBA support that is built into the JDK. On the server side, we use omniORB, an open-source ORB that is available from http://omniorb.sourceforge.net.

NOTE

You can use any CORBA 2-compliant ORB on the server. However, you need to make changes to the C++ code if you use a different ORB.


Our example C++ server object simply reports the value of an environment variable on the server. The interface is

 interface Env {    string getenv(in string name); }; 

For example, the following Java program fragment obtains the value of the PATH environment variable of the process in which the server object runs.

 Env env = . . .; String value = env.getenv("PATH") 

The C++ implementation code for this interface is straightforward. We simply call the getenv method in the standard C library.

 class EnvImpl    : public POA_Env, public PortableServer::RefCountServantBase { public:    virtual char* getenv(const char *name)    {       char* value = std::getenv(name);       return CORBA::string_dup(value);    } }; 

You don't need to understand the C++ code to follow this sectionjust treat it as a bit of legacy code that you want to encapsulate in a CORBA object so that you can call it from Java programs.

On the server side, you write a C++ program that does the following:

1.

Starts the ORB;

2.

Creates an object of the EnvImpl class and registers it with the ORB;

3.

Uses the name server to bind the object to a name;

4.

Waits for invocations from a client.

You can find that program in Example 5-19 at the end of this section. We do not discuss the C++ code in detail. If you are interested, consult the omniORB documentation for more information. The documentation contains a good tutorial that explains each step in detail.

Let us now turn to the client code. You already saw how to invoke a method on the server object once you have a reference to the remote object. However, to get to that reference, you have to go through a different set of mumbo-jumbo than in RMI.

First, you initialize the ORB. The ORB is simply a code library that knows how to talk to other ORBs and how to marshal and unmarshal parameters.

 ORB orb = ORB.init(args, null); 

Next, you locate the naming service that helps you locate other objects. However, in CORBA, the naming service is just another CORBA object. To call the naming service, you first need to locate it. In the days of CORBA 1, this was a major problem because there was no standard way of getting a reference to it. However, a CORBA 2 ORB lets you locate certain standard services by name. The call

 String[] services = orb.list_initial_services(); 

lists the names of the standard services to which the ORB can connect. The naming service has the standard name NameService. Most ORBs have additional initial services, such as the RootPOA service that accesses the root Portable Object Adaptor.

To obtain an object reference to the service, you use the resolve_initial_references method. It returns a generic CORBA object, an instance of the class org.omg.corba.Object. Use the full package prefix; if you just use Object, then the compiler assumes that you mean java.lang.Object.

 org.omg.CORBA.Object object = orb.resolve_initial_references("NameService"); 

Next, convert this reference to a NamingContext reference so that you can invoke the methods of the NamingContext interface. In RMI, you would simply cast the reference to a different type. However, in CORBA, you cannot simply cast references.

 NamingContext namingContext = (NamingContext) object; // ERROR 

Instead, you have to use the narrow method of the helper class of the target interface.

 NamingContext namingContext = NamingContextHelper.narrow(object); 

CAUTION

Casting a CORBA object reference to a subtype will sometimes succeed. Many org.omg.CORBA.Object references already point to objects that implement the appropriate interface. However, an object reference can also hold a delegate to another object that actually implements the interface. Because you don't have any way of knowing how the stub objects were generated, you should always use the narrow method to convert a CORBA object reference to a subtype.


Now that you have the naming context, you can use it to locate the object that the server placed into it. The naming context associates names with server objects. Names are nested sequences of name components. You can use the nesting levels to organize hierarchies of names, much like you use directories in a file system.

A name component consists of an ID and a kind. The ID is a name for the component that is unique among all names with the same parent component. The kind is some indication of the type of the component. These kinds are not standardized; we use "Context" for name components that have nested names, and "Object" for object names.

In our example, the server program has placed the EnvImpl object into the name expressed by the sequence

 (, kind="Context"), (, kind="Object") 

We retrieve a remote reference to it by building an array of name components and passing it to the resolve method of the NamingContext interface.

 NameComponent[] path =    {       new NameComponent("corejava", "Context"),       new NameComponent("Env", "Object")    }; org.omg.CORBA.Object envObj = namingContext.resolve(path); 

Once again, we must narrow the resulting object reference:

 Env env = EnvHelper.narrow(envObj); 

Now we are ready to call the remote method:

 String value = env.getenv("PATH"); 

You will find the complete code in Example 5-18.

This example shows the steps to follow in a typical client program:

1.

Start the ORB.

2.

Locate the naming service by retrieving an initial reference to "NameService" and narrowing it to a NamingContext reference.

3.

Locate the object whose methods you want to call by assembling its name and calling the resolve method of the NamingContext.

4.

Narrow the returned object to the correct type and invoke your methods.

To actually test this program, do the following. The C++ instructions depend on the ORB. We give instructions for omniORB; modify them if you use another ORB.

1.

Compile the IDL file, using both the C++ and Java IDL compilers:

 omniidl -bcxx Env.idl idlj Env.idl 

2.

Compile the C++ server program. The compilation instructions depend on the ORB. For example, with OmniORB on Linux, you use

 g++    -o EnvServer    -D__x86__ -D__linux__ -D__OSVERSION__=2    -I/usr/local/include/omniORB4    EnvServer.cpp EnvSK.cc    -lomniORB4 -lomnithread -lpthread 

To find out what you need with your particular ORB, compile one of the example programs that are supplied with your installation and make the appropriate modifications to compile your own programs.

3.

Compile the Java client program.

4.

Start the naming service on the server. You can either use the orbd program that comes with the JDK, or the naming service of your ORB (for example, omniNames if you use omniORB). The naming service runs until you kill it.

To start orbd, run

 orbd -ORBInitialPort 2809 & 

Alternatively, to start omniNames, run

 omniNames -ORBsupportBootstrapAgent 1 & 

5.

Start the server:

 ./EnvServer -ORBInitRef NameService=corbaname::localhost:2809 & 

The server also runs until you kill it.

6.

Run the client:

 java EnvClient -ORBInitialPort 2809 

The client program should report the PATH of the server process.

If the server is on a remote machine or if the initial port of the server ORB is not the same as the Java IDL default of 900, then set the ORBInitialHost and ORBInitialPort properties. For example, OmniORB uses port 2809. When using orbd, we also used port 2809 because we need to have root privileges to start a service on a port below 1024 on UNIX/Linux.

There are two methods for setting these properties. You can set the system properties

 org.omg.CORBA.ORBInitialHost org.omg.CORBA.ORBInitialPort 

for example, by starting the java interpreter with the -D option. Or, you can specify the values on the command line:

 java EnvClient -ORBInitialHost warthog -ORBInitialPort 2809 

The command-line parameters are passed to the ORB by the call

 ORB orb = ORB.init(args, null); 

In principle, your ORB vendor should tell you with great clarity how its bootstrap process works. In practice, we have found that vendors blithely assume that you would never dream of mixing their precious ORB with another, and they tend to be less than forthcoming with this information. If your client won't find the naming service, try forcing the initial ports for both the server and the client to the same value.

TIP

If you have trouble connecting to the naming service, print a list of initial services that your ORB can locate.

 public class ListServices {    public static void main(String args[]) throws Exception    {       ORB orb = ORB.init(args, null);       String[] services = orb.list_initial_services();       for (int i = 0; i < services.length; i++)          System.out.println(services[i]);    } } 

With some ORBs, NameService isn't among the listed services, no matter how much you tweak the configuration. In that case, switch to Plan B and locate the server object by its Interoperable Object Reference, or IOR. See the sidebar for more information.


In this section, you saw how to connect to a server that was implemented in C++. We believe that is a particularly useful scenario. You can wrap legacy services into CORBA objects and access them from your Java programs, without having to deploy additional system software on the client. In the next section, you will see the opposite scenario, where the server is implemented in Java and the client in C++.

Example 5-18. EnvClient.java

[View full width]

  1. import org.omg.CosNaming.*;  2. import org.omg.CORBA.*;  3.  4. public class EnvClient  5. {  6.    public static void main(String args[])  7.    {  8.       try  9.       { 10.          ORB orb = ORB.init(args, null); 11.          org.omg.CORBA.Object namingContextObj = orb.resolve_initial_references ("NameService"); 12.          NamingContext namingContext = NamingContextHelper.narrow(namingContextObj); 13. 14.          NameComponent[] path = 15.             { 16.                new NameComponent("corejava", "Context"), 17.                new NameComponent("Env", "Object") 18.             }; 19.          org.omg.CORBA.Object envObj = namingContext.resolve(path); 20.          Env env = EnvHelper.narrow(envObj); 21.          System.out.println(env.getenv("PATH")); 22.       } 23.       catch (Exception e) 24.       { 25.          e.printStackTrace(); 26.       } 27.    } 28. } 

Example 5-19. EnvServer.cpp

[View full width]

   1. #include <iostream>   2. #include <cstdlib>   3.   4. #include "Env.hh"   5.   6. using namespace std;   7.   8. class EnvImpl :   9.    public POA_Env,  10.    public PortableServer::RefCountServantBase  11. {  12. public:  13.    virtual char* getenv(const char *name);  14. };  15.  16. char* EnvImpl::getenv(const char *name)  17. {  18.    char* value = std::getenv(name);  19.    return CORBA::string_dup(value);  20. }  21.  22. static void bindObjectToName(CORBA::ORB_ptr orb, const char name[], CORBA::Object_ptr  objref)  23. {  24.    CosNaming::NamingContext_var rootContext;  25.  26.    try  27.    {  28.       // Obtain a reference to the root context of the name service:  29.       CORBA::Object_var obj;  30.       obj = orb->resolve_initial_references("NameService");  31.  32.       // Narrow the reference returned.  33.       rootContext = CosNaming::NamingContext::_narrow(obj);  34.       if(CORBA::is_nil(rootContext))  35.       {  36.          cerr << "Failed to narrow the root naming context." << endl;  37.          return;  38.       }  39.    }  40.    catch (CORBA::ORB::InvalidName& ex)  41.    {  42.       // This should not happen!  43.       cerr << "Service required is invalid [does not exist]." << endl;  44.       return;  45.    }  46.  47.    try  48.    {  49.       CosNaming::Name contextName;  50.       contextName.length(1);  51.       contextName[0].id   = (const char*) "corejava";  52.       contextName[0].kind = (const char*) "Context";  53.  54.       CosNaming::NamingContext_var corejavaContext;  55.       try  56.       {  57.          // Bind the context to root.  58.          corejavaContext = rootContext->bind_new_context(contextName);  59.       }  60.       catch (CosNaming::NamingContext::AlreadyBound& ex)  61.       {  62.          // If the context already exists, this exception will be raised. In this  case, just  63.          // resolve the name and assign the context to the object returned:  64.          CORBA::Object_var obj;  65.          obj = rootContext->resolve(contextName);  66.          corejavaContext = CosNaming::NamingContext::_narrow(obj);  67.          if( CORBA::is_nil(corejavaContext) )  68.          {  69.             cerr << "Failed to narrow naming context." << endl;  70.             return;  71.          }  72.       }  73.  74.       // Bind objref with given name to the context:  75.       CosNaming::Name objectName;  76.       objectName.length(1);  77.       objectName[0].id = name;  78.       objectName[0].kind = (const char*) "Object";  79.  80.       try  81.       {  82.          corejavaContext->bind(objectName, objref);  83.       }  84.       catch (CosNaming::NamingContext::AlreadyBound& ex)  85.       {  86.          corejavaContext->rebind(objectName, objref);  87.       }  88.    }  89.    catch (CORBA::COMM_FAILURE& ex)  90.    {  91.       cerr << "Caught system exception COMM_FAILURE--unable to contact the naming  service."  92.          << endl;  93.    }  94.    catch (CORBA::SystemException&)  95.    {  96.       cerr << "Caught a CORBA::SystemException while using the naming service." << endl;  97.    }  98. }  99. 100. int main(int argc, char *argv[]) 101. { 102.    cout << "Creating and initializing the ORB..." << endl; 103. 104.    CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "omniORB4"); 105. 106.    CORBA::Object_var obj = orb->resolve_initial_references("RootPOA"); 107.    PortableServer::POA_var poa = PortableServer::POA::_narrow(obj); 108.    poa->the_POAManager()->activate(); 109. 110.    EnvImpl* envImpl = new EnvImpl(); 111.    poa->activate_object(envImpl); 112. 113.    // Obtain a reference to the object, and register it in the naming service. 114.    obj = envImpl->_this(); 115. 116.    cout << orb->object_to_string(obj) << endl; 117.    cout << "Binding server implementations to registry..." << endl; 118.    bindObjectToName(orb, "Env", obj); 119.    envImpl->_remove_ref(); 120. 121.    cout << "Waiting for invocations from clients..." << endl; 122.    orb->run(); 123. 124.    return 0; 125. } 

Locating Objects Through IORs

If you can't configure your server ORB and name service so that your client can invoke it, you can still locate CORBA objects by using an Interoperable Object Reference, or IOR. An IOR is a long string starting with IOR: and followed by many hexadecimal digits, for example:

[View full width]

IOR :012020201000000049444c3a4163636f756e743a312e300001000000000000004e000000010100200f0000003231362e31352e3131322e3137390020350420202e00000001504d43000000001000000049444c3a4163636f756e743a312e30000e0000004a61636b20422e20517569636b00

An IOR describes an object uniquely. By convention, many server classes print out the IORs of all objects they register, to enable clients to locate them. You can then paste the server IOR into the client program. Specifically, use the following code:

 String ref = "IOR:012020201000000049444c3a4163636f...";    // paste IOR from server org.omg.CORBA.Object object = orb.string_to_object(ref); 

Then, narrow the returned object to the appropriate type, for example:

 Env env = EnvHelper.narrow(object); 

or

 NamingContext context = NamingContextHelper.narrow(object); 

When testing the code for this book, we successfully used this method to connect clients with Visibroker and OmniORB.



 org.omg.CORBA.ORB 1.2 

  • static ORB init(String[] commandLineArgs, Properties orbConfigurationprops)

    creates a new ORB and initializes it.

  • String[] list_initial_services()

    returns a list of the initially available services such as "NameService".

  • org.omg.CORBA.Object resolve_initial_references(String initialServiceName)

    returns an object that carries out one of the initial services.

  • org.omg.CORBA.Object string_to_object(String ior)

    locates the object with a given IOR.


 org.omg.CosNaming.NamingContext 1.2 

  • org.omg.CORBA.Object resolve(NameComponent[] name)

    returns the object that is bound to the given name.


 org.omg.CosNaming.NameComponent  1.2 

  • NameComponent(String componentId, String componentType)

    constructs a new name component.

Implementing CORBA Servers

If you are deploying a CORBA infrastructure, you will find that Java is a good implementation language for CORBA server objects. The language binding is natural, and robust server software is more easily built with Java than with C++. This section describes how to implement a CORBA server in the Java programming language.

The example program in this section is similar to that of the preceding section. We supply a service to look up a system property of a Java virtual machine. Here is the IDL description:

 interface SysProp {    string getProperty(in string name); }; 

For example, our client test program calls the server as follows:

 CORBA::String_var key = "java.vendor"; CORBA::String_var value = sysProp->getProperty(key); 

The result is a string describing the vendor of the Java virtual machine that is executing the server program. We don't look into the details of the C++ client program. Example 5-21 lists the code.

To implement the server, you run the idlj compiler with the -fall option. (By default, idlj only creates client-side stubs.)

 idlj -fall SysProp.idl 

Then you extend the SysPropPOA class that the idlj compiler generated from the IDL file. Here is the implementation:

 class SysPropImpl extends SysPropPOA {    public String getProperty(String key)    {       return System.getProperty(key);    } } 

NOTE

You can choose any name you like for the implementation class. In this book, we follow the RMI convention and use the suffix Impl for the implementation class name. Other programmers use a suffix Servant or _i.


NOTE

If your implementation class already extends another class, you cannot simultaneously extend the implementation base class. In that case, you can instruct the idlj compiler to create a tie class. Your server class then implements the operations interface instead of extending the implementation base class. However, any server objects must be created by means of the tie class. For details, check out the idlj documentation at http://java.sun.com/j2se/5.0/docs/guide/rmi-iiop/toJavaPortableUG.html.


Next, write a server program that carries out the following tasks:

1.

Start the ORB.

2.

Locate and activate the root Portable Object Adaptor (POA).

3.

Create the server implementation.

4.

Use the POA to convert the servant reference to a CORBA object reference. (The server implementation class extends SysPropPOA, which itself extends org.omg.PortableServer.Servant.)

5.

Print its IOR (for name-service-challenged clientssee the sidebar on page 309).

6.

Bind the server implementation to the naming service.

7.

Wait for invocations from clients.

You will find the complete code in Example 5-20. Here are the highlights.

Start the ORB as you would for a client program:

 ORB orb = ORB.init(args, null); 

Next, activate the root POA:

 POA rootpoa = (POA) orb.resolve_initial_references("RootPOA"); rootpoa.the_POAManager().activate(); 

Construct the server object and convert it to a CORBA object:

 SysPropImpl impl = new SysPropImpl(); org.omg.CORBA.Object ref = rootpoa.servant_to_reference(impl); 

Next, obtain the IOR with the object_to_string method and print it:

 System.out.println(orb.object_to_string(impl)); 

You obtain a reference to the naming service in exactly the same way as with a client program:

 org.omg.CORBA.Object namingContextObj = orb.resolve_initial_references("NameService"); NamingContext namingContext = NamingContextHelper.narrow(namingContextObj); 

You then build the desired name for the object. Here, we call the object SysProp:

 NameComponent[] path =    {       new NameComponent("SysProp", "Object")    }; 

You use the rebind method to bind the object to the name:

 namingContext.rebind(path, impl); 

Finally, you wait for client invocations:

 orb.run(); 

To test this program, do the following.

1.

Compile the IDL file, using both the C++ and Java IDL compilers.

 omniidl -bcxx SysProp.idl idlj -fall SysProp.idl 

2.

Compile the server program.

 javac SysPropServer.java 

3.

Compile the C++ client program. On Linux, use the command

 g++ -o SysPropClient -D__x86__ -D__linux__ -D__OSVERSION__=2 -I /usr/local/include/omniORB4/    SysPropClient.cpp SysPropSK.cc -lomniORB4 -lomnithread -lpthread 

4.

Start the orbd naming service on the server. This program is a part of the JDK.

 orbd -ORBInitialPort 2809 & 

5.

Start the server.

 java SysPropServer -ORBInitialPort 2809 & 

The server also runs until you kill it.

6.

Run the client.

 ./SysPropClient -ORBInitRef NameService=corbaname::localhost 

It should print the JVM vendor of the server.

You have now seen how to use CORBA to connect clients and servers that were written in different programming languages.

This concludes our discussion of CORBA. CORBA has other interesting features, such as dynamic method invocation and a number of standard services such as transaction handling and persistence. We refer you to Client/Server Programming with Java and CORBA by Robert Orfali and Dan Harkey [John Wiley & Sons 1998] for an in-depth discussion of advanced CORBA issues.

Example 5-20. SysPropServer.java

[View full width]

  1. import org.omg.CosNaming.*;  2. import org.omg.CORBA.*;  3. import org.omg.PortableServer.*;  4.  5. class SysPropImpl extends SysPropPOA  6. {  7.    public String getProperty(String key)  8.    {  9.       return System.getProperty(key); 10.    } 11. } 12. 13. public class SysPropServer 14. { 15.    public static void main(String args[]) 16.    { 17.       try 18.       { 19.          System.out.println("Creating and initializing the ORB..."); 20. 21.          ORB orb = ORB.init(args, null); 22. 23.          System.out.println("Registering server implementation with the ORB..."); 24. 25.          POA rootpoa = (POA) orb.resolve_initial_references("RootPOA"); 26.          rootpoa.the_POAManager().activate(); 27. 28.          SysPropImpl impl = new SysPropImpl(); 29.          org.omg.CORBA.Object ref = rootpoa.servant_to_reference(impl); 30. 31.          System.out.println(orb.object_to_string(ref)); 32. 33.          org.omg.CORBA.Object namingContextObj = orb.resolve_initial_references ("NameService"); 34.          NamingContext namingContext = NamingContextHelper.narrow(namingContextObj); 35.          NameComponent[] path = 36.             { 37.                new NameComponent("SysProp", "Object") 38.             }; 39. 40.          System.out.println("Binding server implemenation to name service..."); 41.          namingContext.rebind(path, ref); 42. 43.          System.out.println("Waiting for invocations from clients..."); 44.          orb.run(); 45.       } 46.       catch (Exception e) 47.       { 48.          e.printStackTrace(System.out); 49.       } 50.    } 51. } 

Example 5-21. SysPropClient.cpp

[View full width]

  1. #include <iostream>  2.  3. #include "SysProp.hh"  4.  5. using namespace std;  6.  7. CORBA::Object_ptr getObjectReference(CORBA::ORB_ptr orb, const char serviceName[])  8. {  9.    CosNaming::NamingContext_var rootContext; 10. 11.    try 12.    { 13.       // Obtain a reference to the root context of the name service: 14.       CORBA::Object_var initServ; 15.       initServ = orb->resolve_initial_references("NameService"); 16. 17.       // Narrow the object returned by resolve_initial_references() to a CosNaming: :NamingContext 18.       // object 19.       rootContext = CosNaming::NamingContext::_narrow(initServ); 20.       if (CORBA::is_nil(rootContext)) 21.       { 22.          cerr << "Failed to narrow naming context." << endl; 23.          return CORBA::Object::_nil(); 24.       } 25.    } 26.    catch (CORBA::ORB::InvalidName&) 27.    { 28.       cerr << "Name service does not exist." << endl; 29.       return CORBA::Object::_nil(); 30.    } 31. 32.    // Create a name object, containing the name corejava/SysProp: 33.    CosNaming::Name name; 34.    name.length(1); 35. 36.    name[0].id   = serviceName; 37.    name[0].kind = "Object"; 38. 39.    CORBA::Object_ptr obj; 40.    try 41.    { 42.       // Resolve the name to an object reference, and assign the returned reference to a 43.       // CORBA::Object: 44.       obj = rootContext->resolve(name); 45.    } 46.    catch (CosNaming::NamingContext::NotFound&) 47.    { 48.       // This exception is thrown if any of the components of the path [contexts or  the object] 49.       // aren't found: 50.       cerr << "Context not found." << endl; 51.       return CORBA::Object::_nil(); 52.    } 53.    return obj; 54. } 55. 56. int main (int argc, char *argv[]) 57. { 58.    CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv, "omniORB4"); 59. 60.    CORBA::Object_var obj = getObjectReference(orb, "SysProp"); 61.    SysProp_var sysProp = SysProp::_narrow(obj); 62. 63.    if (CORBA::is_nil(sysProp)) 64.    { 65.       cerr << "Cannot invoke on a nil object reference."  << endl; 66.       return 1; 67.    } 68. 69.    CORBA::String_var key = "java.vendor"; 70.    CORBA::String_var value = sysProp->getProperty(key); 71. 72.    cerr << key << "=" << value << endl; 73. 74.    return 0; 75. } 


 org.omg.CORBA.ORB 1.2 

  • void connect(org.omg.CORBA.Object obj)

    connects the given implementation object to this ORB, enabling the ORB to forward calls to the object's methods.

  • String object_to_string(org.omg.CORBA.Object obj)

    returns the IOR string of the given object.


 org.omg.CosNaming.NamingContext 1.2 

  • void bind(NameComponent[] name, org.omg.CORBA.Object obj)

  • void rebind(NameComponent[] name, org.omg.CORBA.Object obj)

    bind an object to a name. The bind method throws an AlreadyBound exception if the object has previously been bound. The rebind method replaces any previously bound objects.



    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net