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 IDL
4 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 Adapter
5, 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.