An Introduction To The CORBA And Java RMI-IIOP

An Introduction To The CORBA And Java RMI-IIOP

In this article, I’d like to introduce to you the Common Object Request Broker Architecture, which is abbreviated as CORBA1, and its implementation in Java, which is named Java RMI-IIOP. You may think the CORBA is an outdated technology, actually it is still acting as the the basic architecture of distributed object invocation field. Many of state-of-the-art projects are built on top of CORBA. For example, Narayana, the transaction processing project provided by JBoss community, use many idl files to describe its OTS implementation interfaces. Narayana is acting as the transaction processing subsystem in Wildfly2.

Java RMI3 is an distributed object invocation solution provided from Java community, and it was then enhenced by the RMI-IIOP standard to fully compliant with the CORBA standard. Because CORBA provides platform independency, so the Java RMI-IIOP allows the Java applications to communicate with the systems that conforms to the CORBA that is not written in Java.

The CORBA and Java RMI-IIOP technologies are usually hide from the application level to the end users, but they are broadly used in middleware level as implemenations of remoting and transaction processing subsystems. In this article, I’d like to give you an introduction on these technologies. First let’s learn how to write a CORBA application.

Writing an CORBA Application In Java

In this section, we need to know that CORBA uses a platform independent description language called IDL4 for the users to describe their application interfaces. The fullname of IDL is Interface description language, and its grammar looks like C++ language but it’s not the same. Java has an IDL compiler called idlj that can compile the IDL file into Java classes. In this section, we will write an Hello interface using the IDL format, and then we will use the idlj compiler to compile it to Java classes. The first step is to write the IDL file to descibe our application interface.

Writing IDL

Firstly let’s write an IDL file named Hello.idl as following:

module HelloApp
{
	interface Hello
	{
		string sayHello();
	};
};

The above interface is named Hello, and it contains a sayHello() method that will return string typed data. Now we can use the idlj compiler to compile it.

Generating The Client Side Classes

Here is the idlj command to generate the client side Java classes from the idl file:

$ idlj -fclient Hello.idl

As the above command shows, we used We have used the -fclient option to tell idlj to generate the client side classes for us, and it generated these classes into a HelloApp directory. Here is the output:

$ ls HelloApp/
Hello.java           HelloHelper.java     HelloHolder.java     HelloOperations.java _HelloStub.java

The above command output shows the classes that is related with the client side. Before explaining these classes, we need to have an understanding on the basic CORBA architecture. Generally speaking, CORBA divides the whole system into three parts: The naming service; The client side; The servant side.

Firstly, we need to know CORBA is a netword based architecture, which means the three sides can be located at different places. The servant side acts like a server that accepts the calls from clients, and the client side will make the calls to the servant side. The naming service is used for the servant side to register the service it provides, and the client side will fetch the servant information from the naming service. The naming service is usally a server that listens to network for the servants to register/unregister themselves, and for the clients to fetch the servants information that they need.

From the client side of the view, it will just call the interface methods like it is a local method, and all the underlying network communications and details are hidden from the perspective of users. The benefit to use CORBA is that we just need to define a common interface using the IDL file, and then we could use different languages to implement the interface. There are tools in each language to generate helper code from the IDL files. In this article, I will focus on Java implementation. Now let’s turn back to see the details of the client side classes generated by the idlj compiler. The relationship of above classes is shown as below:

From the above class diagram, we can see the HelloOperations interface defines the sayHello() method we wrote in Hello.idl, and then the Hello interface extends the HelloOperations interface. The _HelloStub class implements the Hello interface, and the client side will use it to call the Hello implementation on servant side. We haven’t written an implementation of Hello interface for servant side, and we’ll do it later. Now let’s check the sayHello() method generated in _HelloStub:

public String sayHello ()
{
        org.omg.CORBA.portable.InputStream $in = null;
        try {
            org.omg.CORBA.portable.OutputStream $out = _request ("sayHello", true);
            $in = _invoke ($out);
            String $result = $in.read_string ();
            return $result;
        } catch (org.omg.CORBA.portable.ApplicationException $ex) {
            $in = $ex.getInputStream ();
            String _id = $ex.getId ();
            throw new org.omg.CORBA.MARSHAL (_id);
        } catch (org.omg.CORBA.portable.RemarshalException $rm) {
            return sayHello (        );
        } finally {
            _releaseReply ($in);
        }
} // sayHello

From the above code we can see the method uses the InputStream and OutputStream to represent the request and the response of the call. In addition, the _request() method and _invoke() method are called to get the result we need. We’ll check the detail of this process later. The other class I’d like to explain is the HelloHelper class. This class contains helper functions for us to deal with the objects transferred via the network, such as the data marshalling and unmarshalling work. For example, here is the narrow(...) method in HelloHelper class:

public static HelloApp.Hello narrow (org.omg.CORBA.Object obj)
 {
   if (obj == null)
     return null;
   else if (obj instanceof HelloApp.Hello)
     return (HelloApp.Hello)obj;
   else if (!obj._is_a (id ()))
     throw new org.omg.CORBA.BAD_PARAM ();
   else
   {
     org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
     HelloApp._HelloStub stub = new HelloApp._HelloStub ();
     stub._set_delegate(delegate);
     return stub;
   }
}

From the above code we can see the org.omg.CORBA.Object typed parameter is cast to HelloApp.Hello typed return value. We will use this narrow(...) method later. That’s all I’d like to explain for client side. The next step is to generate the servant side classes.

Generating The Servant Side Classes

Now we need to generate the classes for the servant side. Most client side and servant side classes are the same, so we won’t get many more files by generating the servant side classes. Here is the command to generate the servant side classes:

$ idlj -fserver Hello.idl

The above command will generate the classes for servant side, and many classes are common with the client side, so there is just one additional class named HelloPOA added into the directory. We can check the files we have now:

$ ls
Hello.java           HelloHelper.java     HelloHolder.java     HelloOperations.java HelloPOA.java        _HelloStub.java

The POA is abbreviation of Portable Object Adapter5, and is it part of the CORBA specification. The HelloPOA class is a servant side skeleton class that can help us to register the defined methods into naming service. Let’s see the defintion of this class:

public abstract class HelloPOA extends org.omg.PortableServer.Servant
 implements HelloApp.HelloOperations, org.omg.CORBA.portable.InvokeHandler

From the above class definition, we can see the HelloPOA class extends the org.omg.PortableServer.Servant and implements HelloApp.HelloOperations and org.omg.CORBA.portable.InvokeHandler. This is an abstract class, and we need to extend this class and implement the sayHello() method in the HelloOperations interface. Firstly let’s check the detail code in HelloPOA class:

private static java.util.Hashtable _methods = new java.util.Hashtable ();
static
{
  _methods.put ("sayHello", new java.lang.Integer (0));
}

As the code shown above, it stores our defined methods into _methods hashtable. Then let’s check the _invoke() method:

public org.omg.CORBA.portable.OutputStream _invoke (String $method,
                              org.omg.CORBA.portable.InputStream in,
                              org.omg.CORBA.portable.ResponseHandler $rh)
{
  org.omg.CORBA.portable.OutputStream out = null;
  java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
  if (__method == null)
    throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);

  switch (__method.intValue ())
  {
     case 0:  // HelloApp/Hello/sayHello
     {
       String $result = null;
       $result = this.sayHello ();
       out = $rh.createReply();
       out.write_string ($result);
       break;
     }

     default:
       throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
  }

  return out;
} // _invoke

As the code shown above, the _invoke() method will firstly call the sayHello() method in servant side with the actual HelloOperations implementation. Here is the relative part of the code in the method:

$result = this.sayHello ();

After the above local invocation is done in servant side, it will create the reply and write to client side via network like this:

out = $rh.createReply();
out.write_string ($result);

The above code will transfer the the response of local invocation to client side via network. After looked at the code in HelloPOA, now let’s see this class diagram that includes all the classes we have generated:

From the above diagram, we can see the relationship between these classes, and we can see the shared classes between client side and servant side. Nevertheless, these classes can not form a complete client or servant yet. We need to implement some client side and servant side classes by oursevles. Now let’s implment the servant side implementation of the Hello interface.

Servant Implementation

For the servant side, we need to write a HelloImpl class that extends HelloPOA to implement the sayHello() method. Here is the code:

package HelloApp;

import org.omg.CORBA.ORB;

/**
 * Created by weli on 25/04/2017.
 */
public class HelloImpl extends HelloPOA {


    private ORB orb;

    public void setORB(ORB orbVal) {
        orb = orbVal;
    }



    @Override
    public String sayHello() {
        return "\nHello, world!\n";
    }

}

From the above code, we can see that the HelloImpl class I wrote implements the sayHello() method, and it will accept a org.omg.CORBA.ORB instance for network communication. We’ll check the detail of network communication layer later. The following diagram shows the servant side classes we have so far:

From the above diagram, we can see the two generated classes by idlj compiler, which are HelloOperations and HelloPOA, and a handwritten class HelloImpl. Their relationship is shown in above diagram. Until now, we have _HelloStub class for client side to call the method in HelloImpl from server side, and the network communication detail in between is handled by the POA class. Actually the ORB layer will deal with the network communication, and it’s handled by the generated classes. We don’t have to dig into the network layer so deep now. Let’s write a HelloServer class that accepts the call from client and invocate the HelloImpl.sayHello() method, and then return the result back to the client. Here is the code of the HelloServer class:

package HelloApp;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;

import java.util.Properties;

/**
 * Created by weli on 25/04/2017.
 */
public class HelloServer {
    public static void main(String[] args) throws Exception {
        ORB orb = ORB.init(args, null);

        POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

        rootpoa.the_POAManager().activate();

        HelloImpl helloImpl = new HelloImpl();
        helloImpl.setORB(orb);

        org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl);
        Hello href = HelloHelper.narrow(ref);

        org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
        NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

        String name = "Hello";
        NameComponent path[] = ncRef.to_name(name);
        ncRef.rebind(path, href);

        System.out.println("Server started. Accepting requests...");

        orb.run();

    }
}

From the above code, we can see the HelloServer class generally performs three steps: Firstly, it activates the servant manager:

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

As the above code shows, the servant manager is in rootpoa, and it’s retrieved by ORB from naming service. The name of the POA object in naming service is RootPOA. This is the architecture defined by CORBA specification. There is a naming service for the servants to be registered and fetched. For example, our HelloImpl can be registered into the naming service. The naming service is provided by naming server. In this article, I’ll use a tool provided by JDK named orbd for this purpose. It is a CORBA naming service daemon provided by JDK.

Secondly, the above code registers HelloImpl into naming service. The code is shown in below:

HelloImpl helloImpl = new HelloImpl();
helloImpl.setORB(orb);

org.omg.CORBA.Object ref = rootpoa.servant_to_reference(helloImpl);
Hello href = HelloHelper.narrow(ref);

org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

String name = "Hello";
NameComponent path[] = ncRef.to_name(name);
ncRef.rebind(path, href);

From the above code, we can see that we use orb to get NameService itself from naming service, and then we register our HelloImpl as Hello service.

Thirdly, we start our service to listen to the client requests:

System.out.println("Server started. Accepting requests...");

orb.run();

In above code, the last line will put the servant into listening status, and it will start to listen for requests from clients via network. As we have prepared the servant side, now we need to implement the client side.

Client Side Implementation

We should write a client side class named HelloClient that can help us to fetch the Hello servant from the naming service, and then issue a Hello.sayHello() call to the servant. Here is the call for HelloClient:

package HelloApp;


import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContext;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.CosNaming.NamingContextHelper;

import java.util.Properties;

/**
 * Created by weli on 25/04/2017.
 */
public class HelloClient {

    static Hello helloImpl;

    public static void main(String[] args) throws Exception {

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

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

        NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

        String name = "Hello";

        helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));

        System.out.println(helloImpl.sayHello());

    }
}

Same like server side, the above client side implementation also uses the orb object to fetch NameService, and here is thre relative code in above HelloClient class:

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

This happens via network, and we will see the detail later. And then the NamingContextExtHelper will help the client side to convert the objRef gotten from network to NamingContextExt:

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

The next step in client is to get the Hello interface via network, and here is the relative code:

helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));

The above code is simliar to the process of getting NameService in above. We use ncRef.resolve_str(...) to fetch the remote object from naming server. If we check the type of helloImpl, we will see the type is _HelloStub, and this class will handle the network communication with server side. When we call helloImpl.sayHello() method later, the stub will actually invoke the HelloImpl.sayHello() implementation at servant side. Now let’s check how does servant side receives the call from client side and processes the request.

Request Processing Of Servant Side

At the servant side, the HelloImpl class extends the generated HelloPOA class as we saw in above. We have checked that the _invoke() method in HelloPOA class, and knows it will receive the network requests, and then dispatch the requests to local implementations. Let’s review the core part in the HelloPOA._invoke(...) method:

private static java.util.Hashtable _methods = new java.util.Hashtable ();
static
{
_methods.put ("sayHello", new java.lang.Integer (0));
}

...

switch (__method.intValue ())
{
   case 0:  // HelloApp/Hello/sayHello
   {
	 String $result = null;
	 $result = this.sayHello ();
	 out = $rh.createReply();
	 out.write_string ($result);
	 break;
   }

   default:
	 throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
}

return out;
} // _invoke

As the code shown above, we can see how does servant side deals with the request from client side. Now let’s see the sayHello() call in _HelloStub from client side:

From the above sequence diagram, we can see how does _HelloStub.sayHello() method invokes a call to remote server side, and remarshal the reply message received from servant. Here is the sequence diagram of HelloPOA._invoke(...) method in server side:

From the above seqeuence diagram, we can see how does servant side deal with the requests from the client side. To sum up, here is the description of the whole process: Firstly, there is a naming service daemon started that allows multiple servants to be registered and client can fetch the servant from the naming service.

Secondly, we register our HelloImpl servant into the naming service, and the service name is set to Hello. The client side fetches the Hello servant from the naming service, and knows the network location of the Hello servant. Then the client side invokes a request by using the Hello.sayHello() method. This is done via the _HelloStub class. The _HelloStub class deals with the network communication with the servant side. The servant side used HelloPOA class to deal with the request from client side. It invokes the sayHello() method in HelloImpl, and then marshal the return message and send back the message to client side.

Thirdly, the client side receives the the network data reply from the servant side, and remarhal the data as the return value of the sayHello() method of Hello interface..

From the user’s perspective, the client side invokes Hello.sayHello() method as if it’s a local method call, but actually it is a remote call to the HelloImpl.sayHello() in a servant. The network communication and server implementation details are hidden to users. This is one of the design purposes of the CORBA architecture. In addition, the client side, servant side and naming service side are independent from each other. Now let’s deploy our whole project into running state.

Deployment Of The Project

Now we can start the project deployment phase. We will use the orbd command to start the naming service, and then register our Hello servant into the naming service. After this, the client side will fetech the servant from naming service, and then make a remote call via stub interface. Here is the deployment diagram of naming service, servant side and client side:

From the above diagram, we can see the three sides will be deployed into different machines and they are connected by the network. That is what I’d like to show you in this section. I will start the orbd on machine A. The servant that has HelloImpl will be deployed on machine B, and it will register itself into the naming service on machine A. At last I will deploy the client on machine C. The client side will fetch the Hello servant from naming service. The naming service will tell the client side that the Hello servant is on machine B, and then the client will make a remote call to the servant on machine B. Now let’s take the first step: starting the orbd on machine A.

Start the ORBD

The first step is to start the orbd on machine A. Here is the command and the output:

$ orbd -ORBInitialPort 1050 -ORBInitialHost 10.0.1.155  &
[1] 20458

As the command shown above, we have started the naming service daemon on machine A. The ORBInitialHost option defines the hostname or IP address that orbd will listen to. Because the public IP address of machine A is 10.0.1.155, so the option is set to it. the ORBInitialPort option is used to set the port that orbd will allow servant or client to fetch the NameService. NameService is the default service provided by orbd, and servanta need to register itself into it. Meanwhile the client also need to fetch servants from NameService. Please note orbd has another port called activation port which accepts multiple commands to operate on servants. We will check the details later. The default port for activation is 1049. Please make sure you have configured your firewall properly so the communication between three machines are not blocked. I recommend you to turn off the firewall on machine A, B, C for testing purpose. Next we should register our HelloImpl servant on machine B to the naming service on machine A.

Servant Registration

Now we should go to machine B and start the HelloServer. It will register the HelloImpl into the naming service on machine A. I added some options into HelloServer for it to connect to the naming service. Here is the detail of the code:

 public static void main(String[] args) throws Exception {

        Properties prop = new Properties();

        prop.put("org.omg.CORBA.ORBInitialHost", "10.0.1.155");
        prop.put("org.omg.CORBA.ORBInitialPort", "1050");

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

        POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

        rootpoa.the_POAManager().activate();

        HelloImpl helloImpl = new HelloImpl();
...

As the code shown above, I have set the org.omg.CORBA.ORBInitialHost and org.omg.CORBA.ORBInitialPort properties to point to machine A. Thre properties are injected into orb instance, which deals with the concrete network communication. Then the HelloServer will start the servant registration process. I set four breakpoints in IntelliJ IDEA, because these four steps involve network communication. The breakpoints are shown in the following screenshot:

From the above diagram, we can see there are four lines of code that involves network comunication. Now I start to debug running the code and it stops at the first line of breakpoint:

The above line of code is like this:

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

The above line of code will fetch the NameService from the naming service running on machine A. I started the Wireshark network data analyzer on machine A to check how the above command looks like at network level, and here is the screenshot of the network data I got on machine A:

From the above diagram, you can see I have applied a filter that will only show GIOP packets. GIOP is the protocol used by CORBA to transfer data. We can see the GIOP data is transferred via TCP protocol, and this is called IIOP. In a word, the GIOP data trasferred via TCP protocol is called IIOP.

We can see the benefit to support a standard communication protocol in network level: Because Java RMI-IIOP standard supports the IIOP protocol, so it can interact with other systems with different language platoforms. Different language platforms just need to generate the interface from IDL file and make the invocation, and don’t need to worry about the detail implementation on the other side.

In addition, we can see there is a request GIOP data from the source IP address 10.0.1.75 to the destination IP address 10.0.1.155. The source IP address 10.0.1.75 is machine B, which wants to register HelloImpl servant, and the 10.0.1.155 is the naming service itself. Meanwhile, we can see the TCP port used by naming service is 1050. Because the code is to fetch the NameService, from the Wireshark data analyzer we can see the request operation is get, and the service we want to get is NameService, here is the relative screenshot:

From the above screenshot we can see GIOP data contains operations and data necessary for communication. Now we can check the reply data, and here is the screenshot of the reply data:

We can see from above that the naming service will send the data requested by servant, which is the NamingService. Now we should let the code go to the next line, and here is the code:

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

The corresponding data of above request from HelloServer on machine B captured in machine A, and here is the screenshot of the captured data:

We can see the NamingContextExtHelper.narrow(...) will issue an _is_a operation on naming service side. This time the _is_a operation uses TCP port 1049 for communication:

As the screenshot shown above, we can see the naming service uses two different ports for different purposes. The port 1050 is used for intially fetching the NameService, and all the following actions will use port 1049. The code will then run to this line on in HelloServer:

NameComponent path[] = ncRef.to_name(name);

From the above ncRef.to_name(...) call, the relative GIOP message received at machine A is like this:

The GIOP message shows that the operation name at network level is also to_name. Next we come to this line of HelloServer:

ncRef.rebind(path, href);

This is the command that will register the HelloImpl into naming service, and here is the corresponding GIOP message:

From the above screenshot, we see the rebind operation has a protocol name called COSNAMING. This is actually part of the CORBA intefaces transferred via GIOP, and Wireshark has the ability to decode them, such as CosEvents and CosNaming. Here is the screenshot of the decoded CosNaming data:

From the above screenshot, we can see the information of the Hello servant has been registered into the naming service. The IP address 10.72.12.42 is another public IP of machine B, and later the client from machine C will fetch this info of Hello servant and use it to communicate with the servant on machine B.

The whole message is encapulated in a data structure called IOR. The fullname of IOR is called Interoperable Object Reference, this data structure is part of the CORBA standard, and Java RMI-IIOP standard fully supports it and trafer it via the network. After the above registration is done, our HelloServer will be put into server mode by running orb.run() at last line, and finally the output is like this:

As the screenshot shown above, the servant is ready for accepting requests. We have analyzed the registration process of the servant to naming service, next we should start a client side call from machine C to see the network communication process.

Client Call

The client on machine C needs to communicate with the naming server on machine A to fetch the NameService, and then it will fetch the info of Hello servant from the NameService. After getting the info of Hello servant, it knows the servant server is located at machine B, and then the _HelloStub will communicate with machine B and remotely call the HelloImpl.

In summary, client on machine C will firstly contact with the naming service on machine A, and get the info of Hello servant, and then it wil call the Hello servant server on machine B.

Now let’s check the detail process and analyze the network data. This time we need to start Wireshark on both machine A and machine B, because the client on machine C will contact with both of them. Firstly, I added two lines into HelloClient on machine C:

public static void main(String[] args) throws Exception {

        Properties prop = new Properties();
        prop.put("org.omg.CORBA.ORBInitialHost", "10.0.1.155");
        prop.put("org.omg.CORBA.ORBInitialPort", "1050");

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

As the code shown above, I have configured the ORB to point to the naming service on machine A. Same like the analyze method on HelloServer, I have put four breakpoints on HelloClient code, it’s shown in following screenshot:

As the screenshot shown above, there are four lines of code related with network communication. The first three lines is to contact with the naming service on machine A to fetch the Hello servant info from NameService, the code is like this:

org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
helloImpl = HelloHelper.narrow(ncRef.resolve_str(name));

The first two lines is to fetch NameService, and the third line is to get the Hello servant. After these steps are done, now the client side knows where to called the Hello servant, because it gets the IP address of machine B from the naming server. Here is the corresponding GIOP messages fetched from machine A:

As the screenshot shown above, we see the three requests corresponding to the three lines of code in above. Now as we have the servant info in client side, the last call will communicate to the servant. Here is the last line of code in HelloClient on machine C:

System.out.println(helloImpl.sayHello());

The helloImpl has the type of _HelloStub, and it will remotely call the servant on machine B. Here is the GIOP message captured from machine B:

As the screenshot shown above, we can see the sayHello operation is requested from client machine C to servant machine B. Please note the IP address 10.72.12.42 is the public address of client machine C, and the address 10.72.12.42 is the second public IP address of servant machine B, which is registered into naming service on machine A. Here is the final output from client machine C:

From the above screenshot, we can see client on machine A gets the result from HelloImpl.sayHello() method on machine C. From user’s perspective, it seems like the helloImpl.sayHello() is a local method call, but actually it is a remote call to another machine. What’s amazing is that we fetch the servant from an independent naming service on machine B, and we could easily change the servant in future, because some other servant can replace the Hello service by registering itself into naming server by using the same service name. This gives us great flexibility during our distributed service deployment.

Conclusion

In this article, we have written a Java application with CORBA standard and Java RMI-IIOP implementation. We have seen the achitecture defined by the CORBA, and we see how does Java platform provides us multiple tools to write the application that conforms to the standard. The CORBA and Java RMI-IIOP standards are the basis of many modern application servers. I wish this article is a good start for you to dig deeper in this area in future.

References

My Github Page: https://github.com/liweinan

Powered by Jekyll and Theme by solid

If you have any question want to ask or find bugs regarding with my blog posts, please report it here:
https://github.com/liweinan/liweinan.github.io/issues