Corba
The aim of this section is not to describe CORBA, but just to explain a little bit about how it works, so later I can explain what is and how the Naming Service works - which is part of the CORBA architecture.
So CORBA stands for Common Object Request Broker Architecture. It is a standard which enables programmers to write distributed applications, we can simplify it a lot and say it enables us to write apps working over the network.
CORBA is just an implementation of a standard Server - Client architecture. In CORBA's world, we create objects on the server side - and these objects can be used by clients which are running on other computers on the network.
The client has to know the structure of the object (the methods and the attributes). The objects which are created on server side and used by the clients are before described by the IDL - interface definition language. The very big advantage of Corba is that if we describe the object by IDL, we can later generate Java code as well as C++ code.
When the server creates the object, it will put its reference to ORB - Object Request Broker. This is the container that takes care of serialization of object over the network. This is the abstraction layer which permits us to run distributed apps. When the Server submits the object to ORB, it will obtain IOR - string identification of the object reference in ORB.
When the client wants to access the object, it will need the IOR. If the server and client meet before, they can somehow exchange this info - if not we need to transfer the IOR a different way. Here we will use Naming Service.
Naming Service
Naming Service is a tree structure which allows to store object references of objects which are being managed by the Object Request Broker. Basically that means that the client can now access the object not only by its IOR - but also by a string
like name of the object.
Naming Service is a tree structure. There are generally 2 types of nodes - contexts and object references. When a node is a context, it can contain several children - this can be again of type context or object. When the client wants an object reference, he needs to now the exact place of the object reference in the tree structure and its name.
There is some valuable information about CORBA Naming Service on this site.
NSAdmin
The tool which I called NSAdmin allows creating or deleting references to objects as well as creating and destroying contexts. The tool can visualize the tree structure using the Java JTree component. Here is how it looks like:
How It Works
I wrote NSAdmin in Java and I have used SWING to create the GUI. The program is composed of two classes NSAdminFrame
(the GUI) and NSAdmin
- a static
class which actually contains the implementations of the methods. To test the tool, you can use the Java implementation of ORB which is a generic part of JDK. To start ORB, run:
orbd.exe -ORBInitialPort 1234
The Initialisation
In order to work with the Naming Service, we have to initialize the Object Request Broker. From the ORB, we can obtain the reference to root naming context. This context is always called "NameService
". The method for initialization looks like this, if you have started the ORB on your local machine, then the parameters that you will submit to this method will be: 1234,localhost. Note that the variable rootContext
and orb
are defined on top of the static
class.
public static void init(String port, String initialHost)
throws Exception{ String[] args = new String[4]; args[0] = "-ORBInitialPort";
args[1] = port; args[2] = "-ORBInitialHost"; args[3] = initialHost;
orb = ORB.init(args,null); rootContext = NamingContextExtHelper.narrow
(orb.resolve_initial_references("NameService")); }
Exploring the Naming Service Tree
To list the content of the naming context, we have a method which recursively calls itself. This method will create a tree structure of classes which I called Entry
.
Entry
is a simple class which has a name and can have multiple children in its ArrayList
entries.
public class Entry {
public ArrayList<entry> entries;
public String name;
public Entry(String n){
name = n;
entries = new ArrayList<entry>();
}
}
The structure of Entry
classes will be later used to visualize the content in JTree
. OK now the method explore which recursively traverses the naming context.
public static Entry explore(String contextName,NamingContext context,int treeDepth)
throws InvalidName, NotFound, CannotProceed,
org.omg.CosNaming.NamingContextPackage.InvalidName{
BindingListHolder blh = new BindingListHolder();
BindingIteratorHolder bih = new BindingIteratorHolder();
context.list(0,blh, bih);
Entry entry = new Entry(contextName);
System.out.println(contextName);
BindingIterator bit = bih.value;
boolean remains = true;
while(remains){
BindingHolder biholder = new BindingHolder();
remains = bit.next_one(biholder);
Binding binding = biholder.value;
NameComponent[] name = binding.binding_name;
if(binding.binding_type == BindingType.nobject){
if(name.length == 1){
System.out.println("ID: " + name[0].id + " KIND: " +
name[0].kind + " Depth: " + treeDepth);
entry.entries.add(new Entry(name[0].id));
}
}else if(binding.binding_type == BindingType.ncontext){
NamingContext tmpContext = NamingContextHelper.narrow
(context.resolve(binding.binding_name));
NameComponent component = name[0];
entry.entries.add(explore(component.id,tmpContext,treeDepth+1));
}
}
return entry;
}
The method takes NamingContext
as parameter and will return a structure of Entry
classes which will correspond to the naming service tree. There are couple things to notice:
The method list(int,BindingListHolder, BindingIteratorHolder)
has two ways of usage. The first possibility is to specify how many binding we want to load. If we specify the value, than the bindings will be loaded into the array Binding[]
which will be returned in the BindingListHolder
class.
The second possibility is to call list(0,blh,bih)
. This way, the BindingListHolder
will contain no results and we will obtain an iterator which we can use to iterate through the array. That is the possibility which I chose.
You can see that I iterate through all the bindings, and for each binding I determine the type. If it is a context - than I will recursively explore the context, if the type is object (reference is binded), then I will just add a new Entry
which will represent this reference.
Destroying and Creating a subcontext
Creating the context is quite straight forward.
public static void createContext(String name)
throws org.omg.CosNaming.NamingContextPackage.InvalidName,
NotFound, CannotProceed, AlreadyBound{
NameComponent[] contextName = rootContext.to_name(name);
NamingContext newContext = rootContext.new_context();
rootContext.bind_context(contextName,newContext);
}
Destroying context can be a bit tricker because in order to destroy a context, the context has to be empty. So I implemented a recursive method, which will destroy all the children of context and then the context can be destroyed.
public static void deleteContext(String name)
throws org.omg.CosNaming.NamingContextPackage.InvalidName,
NotFound, CannotProceed, NotEmpty{
NameComponent[] contextName = rootContext.to_name(name);
NamingContext context = NamingContextHelper.narrow(rootContext.resolve(contextName));
BindingListHolder blh = new BindingListHolder();
BindingIteratorHolder bih = new BindingIteratorHolder();
context.list(Integer.MAX_VALUE, blh, bih);
for(Binding binding:blh.value){
NameComponent[] componentName = binding.binding_name;
if(binding.binding_type == BindingType.ncontext){
NSAdmin.deleteContext(name + "/" + componentName[0].id);
}
else{
NSAdmin.deleteObjectReference(name + "/" + componentName[0].id);
}
}
context.destroy();
rootContext.unbind(contextName);
}
Creating Object Reference
This last method is a method to manually add object reference to a context. Let's say that an object was created on the server and we have the IOR - the Interoperable Object Reference - which is a identificator of the object. Well, then we can bind the reference of the object to the tree and a client application can use just the name to obtain the reference.
public static void createObjectReference(String ior,String context,String objectName)
throws org.omg.CosNaming.NamingContextPackage.InvalidName,
NotFound, CannotProceed, AlreadyBound{
org.omg.CORBA.Object reference=orb.string_to_object(ior);
NameComponent[] componentName = rootContext.to_name(context + objectName);
rootContext.bind(componentName,reference);
}
To obtain the reference of the object, we just call the method string_to_object
of Object Request Broker. The actual binding is then performed as for any other object we want to bind in the tree.
Summary
This is just a light overview of the functions which are the core of the program. Of course, there is a bit of glue code to put it together and let it work with the GUI. I think you can easily understand the rest from the code itself.
You can download the source code here.
CodeProject