An Overview of
Distributed Computing using
CORBA
brief summaries of all OMG's specifications:
Object Management Architecture - OMA:
Common Object Request Broker Architecture
IDL - Interface Defination Language:
Dynamic Invocationa and Dispatch:
IIOP - Internet Inter-ORB Protocols:
Part II: CORBA Client and Server
TimeOperation.JAVA
- SOURCE FILE.
TimeHelper.JAVA
- SOURCE FILE.
TimeHolder.JAVA
- SOURCE FILE.
_TimeImplBase.JAVA
- SOURCE FILE.
Part III: A Distributed
Client-Server Example Using JDBC
IDL generated -
DB.DBAccessHelper.java
IDL generated -
DB.DBAccessHolder.java
IDL generated - DB.DBAccessOperations.java
IDL generated -
DB._DBAccessStub.java
IDL generated -
DB._DBAccessImplBase.java
Part IV – IONA’s Orbix Client
Server Example
To begin a discussion about CORBA we need to have some
background on OMG and OMA. CORBA is a complex specification and I have tried to
put it in an easy to understand format.
what is omg?
Object Management Group, OMG, is a global
organization which was founded in April 1989 by eleven companies, including
3Com Corporation, American Airlines, Canon, Inc., Data General,
Hewlett-Packard, Philips Telecommunications N.V., Sun Microsystems and Unisys
Corporation. Now it has over 800 members who are working towards developing and
refining the standards.
In October 1989, the OMG began independent operations
as a not-for-profit corporation.
what does OMG do?
The
OMG manages an open, vendor-neutral process which proposes technologies and
invites proposals and invites feedback from any member company before coming to
consensus on a final specification, which becomes an adopted standard. Some of the standards the OMG has developed
include
·
CORBA,
·
UML and
·
IIOP.
The
OMG is moving forward in establishing CORBA as the "Middleware that's
Everywhere" through its worldwide standard specifications: CORBA/IIOP,
Object Services, Internet Facilities and Domain Interface specifications, UML
and other specifications supporting Analysis and Design.
Analysis and Design Specifications:
UML, the Unified Modeling Language, is a graphical
language that expresses program design in a standard way, allowing design tools
to interchange models (using XMI, discussed below). It's an object-oriented
language that standardizes an impressive number of diagram types including
Class and Object diagrams, Structure diagrams, Use Case diagrams, and more.
XMI, for XML Metadata Interchange, is a stream format
for interchange of metadata including the UML models that you create during
your analysis and design activities. It's useful for transferring the model
from one step to the next as your design and coding progress, or for
transferring from one design tool to another.
OMG's Object Management Architecture (OMA):
The OMA - Object Management Architecture - categorizes
objects into four categories: the CORBAservices, CORBAfacilities, CORBAdomain
objects, and Application Objects.
CORBA: ( More details below )
CORBA is the
acronym for Common Object Request Broker Architecture. CORBA is OMG's open,
vendor-independent specification for an architecture and infrastructure that
computer applications use to work together over networks. Interoperability
results from two key parts of the specification: OMG Interface Definition
Language (OMG IDL), and the standardized protocols GIOP and IIOP. These allow a
CORBA-based program from any vendor, on almost any computer, operating system,
programming language, and network, to interoperate with a CORBA-based program
from the same or another vendor, on almost any other computer, operating
system, programming language, and network.
Historically we are moving from a monolithic sytem to
a client - server model.
Mainframes:
In the beginning we had mainframe computers, which had
some hierarchical database and dumb terminals. Data and the business logic and
all data access functionality where contained in one large application.
PCs:
With the advent of PCs, clients got processing power
and we saw the computing model change to the client - server model. Unix
servers made smaller companies and departments of bigger corporations more
control over their computing environment. This two tier model gave way to a
multi - tier client - server model. The application was divided into three
parts, the client, the business logic and the database. To enhance this model
the next logical step was to introduce a distributed model. A big enterprise
would have different systems in the various departments and or counrties it
operates in. A need arose to link all these systems via an common
interface.
Some Features and Benefits:
CORBA is a complete Distributed Object platfrom. A
CORBA ORB connects a client application to the server object it wishes to use.
Transparent to the client , the ORB will locate and connect to the Object on
the same machine or on a remote machine. The ORB takes care of the details of
locating the object, routing the request, and returning the results.
Corba may not be the only way to program distributed
systems. These are some of the alternatives to CORBA:
Socket programming allow us to communicate between machines or processes on the same
machine. The socket API is rather low - level and can to difficult to code for
a complex situation.
Remote Procedure Call ( RPC ) provides a function oriented interface to socket
level programming. This more high level than scokets, but RPC protocol has
incompatible implementations by differrent vendors.
Distributed Component Object Model ( DCOM ) which offers facilities similar to CORBA but it runs
most efficiently on the Windows platfrom. It is not a true Object Oriented
system. DCOM objects do not maintain state between connections.
Remote Method Invocation ( RMI ) is also similar to CORBA but is a Java only solution.
RMI does not support dynamic invocations. It also lacks a inter ORB protocol
like IIOP. Though some vendors are working on the DCOM and RMI bridges to ORB.
HTTP/CGI is
being used today to deliver web based applications. This protocol is stateless
and is very slow. It is not very robust to provide an enterprise solution.
can I join OMG?
OMG is mainly for companies who are developing
software and want to contribute to the standards being developed by OMG. The
fee is based on the sales of the company. The good news is that Universities
can become voting members by paying a nominal annual fee.
Before diving into the CORBA standard, there are some
myths about its founding organization, which I plan to point out and help clear
out any mysteries about OMG, OMA or CORBA.
some myths about OMG - demystified.
myth : The OMG
is only about CORBA and other infrastructure standards.....
reality :
Though CORBA is an important part of OMG, other areas it does work in,
are, analysis and design work with the Unified Modeling Language™ (UML™)
specification which has been an OMG specification since 1997, to the Domain
work focused on building consensus in industries such as Telecommunications,
Manufacturing, Health Care, Finance, Life Sciences and many more.
myth : The OMG
is not a worldwide consortium...
reality: OMG is truely a global organization. It has
over 800 members spanning the five continents. The OMG is headquartered in
Needham, MA, USA and has international marketing offices in Bahrain, Brazil,
Germany, India, Italy, Japan and the UK, along with a government representative
in Washington, D.C.
( This forms the basis for CORBA )
The OMA is composed of two basic models on which CORBA
and all its standard interfaces are based. They are Object Model and Reference
Model.
Object Model:
The is defines how object distributed over a
hetrogeneous can be described. This is an abstract specification, without going
into syntatical details of the oject interfaces. This provides the basis for
CORBA, but is more relevant to ORB designers than to application programmers.
Reference Model:
This defines the development model for CORBA and its
standard interfaces which the developers can use to create frameworks,
compoments and objects. The figure below shows the reference model. The Object
Request Broker ( ORB ) is at the center and is reponsible for facilitating
communication between clients and ojects. There are four object interface
categories which use the ORB. They are

·
Object Services - These are Domain-independent interfaces and are
used a number of distributed programs.
Naming Service is an example of the Object Services. This allows clients to
locate objects by names. There are many Object Services specifications for
lifecycle management, security, transactions and event notifications.
·
Domain Interfaces - These interfaces provide support for application
from specific industry domains.
·
Common Facilities - These interfaces provide application level services
across domains.
·
Application
Interfaces - These are interfaces
which are developed for a specific application.
( CORBA )
These days we have very large computer systems which
need to connect over the network to other systems to exchange data or in some
cases use the processing power of better and fast systems. This has made
networks very complex and network programming very difficult as, programs have
been written in many different languages and are running on different operating
systems. A network programmer needs to deal with these issues while designing a
distributed application. CORBA defines a framework for developing
object-oriented distributed applications. This framework makes network
programing easier by making remote objects appear local and the objects can
interact as though the have being written in the same language and are on the
same machine.
CORBA was one of the first specifications adopted by
OMG. It details the interfaces and the characteristics of the ORB component of
the OMA. The main features of CORBA are:
·
ORB
·
IDL - Interface
Defination Language
·
Interface Repository
·
Language Mapping
·
Stubs and Skeletons
·
Dynamic Invocationa and
Dispatch
·
Object Adapters
·
IIOP - Inter-ORB
Protocols

The ORB delivers request to objects ( the server
object )and returns any response to the client object.
As evident by
the figure above the client invokes the member method of a remote CORBA object.
The ORB collects this method call and redirects the method call to the target
object over the network. Then the ORB will collect the response and return it
to the client.
The main feature of the ORB is that it hides the
details of the communication between the client and server objects. The ORB
allows the programmer to concentrate more on their application, rather than the
details of the low level distributed system programming issues. The ORB hides
the following from the client:
·
Object Location: The client does not know where the target object
resides.
·
Object Implementation: The client does not know what language the target
object is written in or the operating system / hardware it runs on.
·
Object Execution
State: The client at the time it
makes the request does not know if the object is activated. The ORB can
transparently start the object if required.
·
Object Communication
mechanisms: The client does not know
the low level mechanisms, such as TCP/IP, shared memory, etc ,used by the ORB.
The job of the ORB is to provide communication and
activation infrastructure for distributed object applications.
An object's interface specifies the operations and the
types supported by the object. This interface is defined using the Interface
Defination Language. The IDL is language independent, it is a declarative
language and not a programming language. This allows objects written in
different programming languages to communicate. For example the client could be
a java applet and the server could be written in C++.

In CORBA the objects are not directly coded in the
IDL, but as explained the IDL is a declarative language, and not a programming
language. Instead language mapping exists to map the IDL features to those of
an programming language. For example check the table which shows the IDL type
to C++ mapping.

There are standardized mapping for Java, C, C++,
SmallTalk
and Ada 95. More and more languages are being added.
Once you have created an IDL file, you will compile it using a special IDL
compiler which will create output source files for the client and the server
(explained below). The type of IDL compiler will determine the language of the
output files. Each supported language there will be an IDL compiler.For example
if you use an IDL to C++ compiler, then
the output would be in C++.
The IDL compiler translates the interfaces and outputs
client-side stubs and server-side skeletons. The stubs enable the client to
issue requests to the remote objects. While the skeletons on the server deliver
the requests to the target objects, and also help to deliver the results back
to the client. All the low level network handshaking is hidden in these two
files. The programmer can now make a remote method call, without knowing the
details of the network protocols.
The process of converting the remote method call into
a format to transmit over the network is called marshalling. This has to be
undone or unmarshalled at the server-side and convert it back to a method call.
The figure below shows this process of making a remote method call.
Using the stubs and skeletons is called static
invocation. We need to know the inetrfaces during compile time. This maybe a
disadvantage at times, when we are dealing with a very dynamic environment,
like a Gateway, which allows foriegn objects to access CORBA objects. We would
need to recompile this Gateway everytime someone added an interface to the
system. It would be better if this Gateway could dynamically determine the
interface information. For this we would need an online Interface database or repository.

Interface Repository:
The Interface Repository is an online database of the
ORB object types. The object information stored includes module, interfaces, operations, attributes
and exceptions.
Clients could query this database to find out method
in a remote object.
Aclient at runtime will query the database and get
information about the interfaces. Then it would use a component of the ORB
called the Dynamic Call Interface or DII to make a method call. Similar to the
DII, on the server we have the Dynamic Skeleton Interface or DSI. The DSI
allows the server to be written without the compile time skeletons for the
object being invoked.

The final subcomponent of CORBA, the Object Adapters
(OA). An OA is an object which that adapts the interface on another object to
the inetrface expected by a caller.
The figure below shows graphically the role of the OA.
The caller expects interface A, but the target object has an interface called
X. To enable communications we need an OA to translate the interface X to A,
fro the caller object.

OAs have other roles and help keep the ORB
implementation simple. Some responsibilities include,
·
Object Registration.
·
Object Reference
Generation.
·
Server Process
activation.
·
Oblect Activation.
·
Request demultiplexing.
·
Object upcalls.
In the earlier releases of CORBA the ORBs from
different vendors did not inter-operate. This was due to the fact that there was no communication protocol defined
by the OMG. In CORBA 2.0 a general ORB
interoperability architecture was provided to help ORB to ORB interoperability.
This was based on the General Inter-ORB Protocol ( GIOP). GIOP is designed to
be simple and easy to implement while allowing reasonable scalability and
performance. The IIOP specifies how GIOP is built over TCP/IP transport.
Recap:
There is lots more to CORBA and distributed
application. In my opinion I have covered most for the fundaments of the OMG's
CORBA specification. In a nutshell
CORBA is to objects what HTTP is to web pages. Different web pages can be liked
from one page. If we write our text in HTML any web server can read and display
it. Similarly if we make our application CORBA ready, by using the IDL when any
other CORBA objects can interact with this object. The best part is that the
low level network programming details are hidden from the programmer.
The Timeserver Example:
I will describe the basic
steps needed to create a distributed application using CORBA and the JAVA IDL.
This application is called the Time Server, and the only task of the server is
to return the time, when requested by a client. There are three basic steps in
building this application. They are
·
Creating the IDL
·
The Client and
·
The Server Program.
The server will have a method
called "gettime" which will return the time in a string format. The
CORBA client will request the ORB for the obejct reference to this server
object, also called the "Servant" object. After receiving this object
reference it will call this method as if it resides on the local machine. The
IDL generated support files and the ORB will marshal the parameters over the
network and the server - side ORB will unmarshal the request and pass it to the
skelEton files who in turn will invoke the server method. The process is
reversed to pass the result back to the client. All the network activity is
handled transparent ot the client programmer.
// Time Server IDL file
module TimeServer {
interface
Time {
string
getTime();
};
};
// end of file:
Some points about this file:
·
Module - it acts like a
container for realted interfaces and declarations. It maps to the
"package" statement in Java.
·
Interface - maps to the
"Interface" statement in Java. The client and server may have
different implmentations for the same interface.
·
Operations - are fucntions
that a server will provide to other CORBA clients. This maps to the methods in
Java. the method here is called "getTime".
Next we compile this file to
generate the necessary helper files.
c:\>idlj -fall TimeServer.idl
Note: we use the -fall switch
to generate the server skeleton files.
The compiler will create a
folder called TimeServer and will generate six files in it. It is important to
understand what each file does.
·
TimeHolder.java - this file
is needed to support the out and inout parameter passing modes. In java all
parameters are passed by value, so these holder classes are used to provide a
level of indirection and are passed instead of the actual type.
·
TimeHelper.java - This class provides methods for reading and writing
the objects to a stream and casting the object to/from a CORBA "Any".
·
_TimeStub.java - This files helps our client to pass the marshalling
information to the ORB.
·
Time.java - This maps to the IDL file.
·
TimeOperations.java
- This contains all the
operations listed in the IDL file.
·
_TimeImplBase.java - This is the server skeleton file.
Check appendix A: for the
source for these files and some more explanation.
We need to make a call to the
getTime() method on the server. The following are the steps needed.
We need to import the
necessary packages. These are
import TimeServer.*;
import org.omg.CosNaming.*;
import org.omg.CORBA.*;
CosNaming contains the Naming
Service and all Objects need to use the CORBA package.
Next step is to create an ORB
object, which will help in marshalling the parameters. Every client needs to
instantiate an org.omg.CORBA.ORB object.
ORB orb =
ORB.init (args, null);
Note:
ORB class has a method called init().
java.lang.Object
|
+--org.omg.CORBA.ORB
Next we need to find the
initail naming context. We use the resolve_initial_references() method of the
ORB call.
org.omg.CORBA.Object
objRef = orb.resolve_initial_references ("NameService");
The string
"NamingServer" is defined for all CORBA ORBs.
The reference returned is of
an generic type, and we need to narrow it down before using it. We narrow it by
using the Helper Classes of the naming service.
NamingContext
ncRef = NamingContextHelper.narrow (objRef);
Now that we have a reference
to the initial naming context, we need to find the path to our server object.
The naming service stores the names in a namecomponent object. these objects
are stored in a tree structure, similar to the files and directory structure.
So first we create a
namecomponent object for our timeserver,
NameComponent nc
= new NameComponent ("TimeServer", "");
We need to convert the
NameComponent into an array as the resolve() method of the naming service
expects an array.
NameComponent path[] = {nc};
Time timeRef = TimeHelper.narrow (ncRef.resolve(path));
We wrap the
resolve() method in out TimeHelper.Narrow method to narrow the returning
reference.
Now we have the
object reference of the server object and can use it as if the server object is
local. CORBA invocations look like a method call on a local object. The
complications of marshaling parameters over the wire, routing them to the
server-side ORB, unmarshaling, and placing the upcall to the server method are
completely transparent to the client programmer. Because so much is done for
you by generated code, invocation is the easiest part of CORBA programming.
String time =
"Time on the Server is " + timeRef.getTime ();
Finally, we add
code to print the results of the invocation to standard output
System.out.println
(time);
A server is a
process which instantiates one or more servant objects. The servants implement the
interface generated by the IDL and it handles the operations listed in the IDL.
The Servant
Class.
We define the
class for the servant object as follows.
class TimeServer
extends _TimeImplBase {
public String getTime () {
SimpleDateFormat formatter =
new SimpleDateFormat ("MMMMM dd, yyyyy
GGG, hh:mm:ss:SSS aaa");
Date date = new Date ();
return formatter.format ( date );
}
}
This class
extends the skeleton class generated by the IDL compiler. It also has the getTime
operation.
The Server
Class:
We first create
an obejct of the timeserver class.
TimeServer
timeRef = new TimeServer ();
Next, we connect
the servant to the ORB, so the ORB can recognize invocations on it and pass
them along to the correct servant:
orb.connect
(timeRef);
Like the client
we need to get the object reference to the initial naming context and narrow
the reference to the namingcontext type.
org.omg.CORBA.Object objRef =
orb.resolve_initial_references ("NameService");
NamingContext ncRef =
NamingContextHelper.narrow (objRef);
Registering
the servant with the Name Service.
Now we create a
NameComponent called "TimeServer".
NameComponent nc
= new NameComponent ("TimeServer", "");
NameComponent
path[] = {nc};
Next we have to
bind the servant object reference to the namecomponent and add it to the naming
context.
ncRef.rebind
(path, timeRef);
Now, when the
client calls resolve("TimeServer") on the initial naming context, the
Naming Service returns an object reference to the Time servant.
Now the server
just waits for a client invocation.
1> Start the
name service by typing the following
C:\>tnameserv
This will start
the Naming Service on the default port of 900.
2> Start the
Server.
C:\>java
Server
3>Run the
Client.
C:\>java
Client
This files
maps to the IDL file.
package TimeServer;
/**
* TimeServer/Time.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:12 PM EST
*/
public interface Time extends
TimeOperations, org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity
{
} // interface Time
package TimeServer;
/**
*
TimeServer/TimeOperations.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:12 PM EST
*/
public interface
TimeOperations
{
String getTime ();
} // interface TimeOperations
package TimeServer;
/**
* TimeServer/TimeHelper.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:11 PM EST
*/
abstract public class
TimeHelper
{
private static String _id
= "IDL:TimeServer/Time:1.0";
public static void insert (org.omg.CORBA.Any a, TimeServer.Time
that)
{
org.omg.CORBA.portable.OutputStream out =
a.create_output_stream ();
a.type (type ());
write (out, that);
a.read_value (out.create_input_stream (), type ());
}
public static TimeServer.Time extract (org.omg.CORBA.Any a)
{
return read (a.create_input_stream ());
}
private static org.omg.CORBA.TypeCode __typeCode = null;
synchronized public static org.omg.CORBA.TypeCode type ()
{
if (__typeCode == null)
{
__typeCode = org.omg.CORBA.ORB.init ().create_interface_tc
(TimeServer.TimeHelper.id (), "Time");
}
return __typeCode;
}
public static String id ()
{
return _id;
}
public static TimeServer.Time read (org.omg.CORBA.portable.InputStream
istream)
{
return narrow (istream.read_Object (_TimeStub.class));
}
public static void write (org.omg.CORBA.portable.OutputStream
ostream, TimeServer.Time value)
{
ostream.write_Object ((org.omg.CORBA.Object) value);
}
public static TimeServer.Time narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof TimeServer.Time)
return (TimeServer.Time)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 ();
return new TimeServer._TimeStub (delegate);
}
}
}
package TimeServer;
/**
* TimeServer/TimeHolder.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:11 PM EST
*/
public final class TimeHolder
implements org.omg.CORBA.portable.Streamable
{
public TimeServer.Time value = null;
public TimeHolder ()
{
}
public TimeHolder (TimeServer.Time initialValue)
{
value = initialValue;
}
public void _read (org.omg.CORBA.portable.InputStream i)
{
value = TimeServer.TimeHelper.read (i);
}
public void _write (org.omg.CORBA.portable.OutputStream o)
{
TimeServer.TimeHelper.write (o, value);
}
public org.omg.CORBA.TypeCode _type ()
{
return TimeServer.TimeHelper.type ();
}
}
package TimeServer;
/**
* TimeServer/_TimeStub.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:11 PM EST
*/
public class _TimeStub
extends org.omg.CORBA.portable.ObjectImpl implements TimeServer.Time
{
// Constructors
// NOTE: If the default
constructor is used, the
// object is
useless until _set_delegate (...)
// is called.
public _TimeStub ()
{
super ();
}
public _TimeStub (org.omg.CORBA.portable.Delegate delegate)
{
super ();
_set_delegate (delegate);
}
public String getTime ()
{
org.omg.CORBA.portable.InputStream _in = null;
try {
org.omg.CORBA.portable.OutputStream _out = _request
("getTime", 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 getTime ();
} finally {
_releaseReply (_in);
}
} // getTime
// Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:TimeServer/Time:1.0"};
public String[] _ids ()
{
return (String[])__ids.clone ();
}
private void readObject (java.io.ObjectInputStream s)
{
try
{
String str = s.readUTF ();
org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init
().string_to_object (str);
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)
obj)._get_delegate ();
_set_delegate (delegate);
} catch (java.io.IOException e) {}
}
private void writeObject (java.io.ObjectOutputStream s)
{
try
{
String str = org.omg.CORBA.ORB.init ().object_to_string
(this);
s.writeUTF (str);
} catch (java.io.IOException e) {}
}
} // class _TimeStub
package TimeServer;
/**
*
TimeServer/_TimeImplBase.java
* Generated by the
IDL-to-Java compiler (portable), version "3.0"
* from TimeServer.idl
* Tuesday, November 28, 2000
8:57:11 PM EST
*/
public abstract class
_TimeImplBase extends org.omg.CORBA.portable.ObjectImpl
implements TimeServer.Time,
org.omg.CORBA.portable.InvokeHandler
{
// Constructors
public _TimeImplBase ()
{
}
private static java.util.Hashtable _methods = new
java.util.Hashtable ();
static
{
_methods.put ("getTime", new java.lang.Integer (0));
}
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: // TimeServer/Time/getTime
{
String __result = null;
__result = this.getTime ();
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
// Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:TimeServer/Time:1.0"};
public String[] _ids ()
{
return __ids;
}
} // class _TimeImplBase
import TimeServer.*; // The package containing
our stubs.
import org.omg.CosNaming.*; //
Client will use the naming service.
import org.omg.CORBA.*; // All
CORBA applications need these classes.
public class Client {
public
static void main (String args[]) {
try
{
//
Create and initialize the ORB
ORB orb = ORB.init (args, null);
//
Get the root naming context
org.omg.CORBA.Object objRef =
orb.resolve_initial_references
("NameService");
NamingContext ncRef = NamingContextHelper.narrow (objRef);
//
Resolve the object reference in naming
NameComponent nc = new NameComponent ("TimeServer",
"");
NameComponent path[] = {nc};
Time timeRef = TimeHelper.narrow (ncRef.resolve (path));
//
Call the time server object and print results
String time = "Time on the Server is " + timeRef.getTime
();
System.out.println (time);
}
catch (Exception e) {
e.printStackTrace ();
}
}
}
// The package containing our stubs.
import Tracker.*;
// Server will use the naming service.
import org.omg.CosNaming.*;
// The package containing special
exceptions thrown by the name
// service.
import org.omg.CosNaming.NamingContextPackage.*;
// All CORBA applications need these
classes.
import org.omg.CORBA.*;
import java.util.*;
import java.text.*;
class TimeServer extends _TimeImplBase {
public
String getTime () {
SimpleDateFormat formatter =
new SimpleDateFormat ("MMMMM dd,
yyyyy GGG, hh:mm:ss:SSS aaa");
Date date = new Date ();
return
formatter.format (date);
}
}
public class Server {
public
static void main (String args[]) {
try
{
//
Create and initialize the ORB
ORB orb = ORB.init (args, null);
//
Create the servant and register it with the ORB
TimeServer timeRef = new TimeServer ();
orb.connect (timeRef);
//
Get the root naming context
org.omg.CORBA.Object objRef = orb.resolve_initial_references
("NameService");
NamingContext ncRef = NamingContextHelper.narrow (objRef);
//
Bind the object reference in naming
NameComponent nc = new NameComponent ("TimeServer",
"");
NameComponent path[] = {nc};
ncRef.rebind (path, timeRef);
//
Wait forever for current thread to die
Thread.currentThread ().join ();
}
catch (Exception e) {
e.printStackTrace ();
} } }
______<<Source Listing and
Explanations>>_____
The
first step in making a distributed CORBA application is to determine the
interfaces that will be exposed through this process. This is an example of a
client-server application. The client calls a method of the remote server
object. This method contains some JDBC code needed to query the database on the
server. The results are then returned to the client. In this example we query
an Access database, using the JDBC-ODBC Bridge built into Java 2. I have
included the source and relevant explanation below.
This IDL file contains
one interface, called DBAccess. This contains one operation called runQuery.
After creating this file, we compile it using the idl compiler in Java 2. This
will create the stub and the skeleton files. Some helper files will also be
created. Those files are listed below.
// module maps to
the Java Package statement
module DB {
// interface maps
to the Java Interface statement
interface DBAccess {
/* we have
defined one operation called runQuery
this maps to a method*/
string runQuery(in string query);
};
};
_____<Completes the list of
DBAccess.idl>______
The client firsts
initializes an ORB. Then it tries to find the Initial Naming Context of the
Naming Service. Then it looks for the reference to the remote object in the
Naming Service. After receiving this, it narrows the reference and then calls
the remote method.
import DB.*;
/* this folder
holds all the files generated by the IDL compiler */
import org.omg.CosNaming.*;
/* Client will
use the naming service. */
import org.omg.CORBA.*;
/* All CORBA
applications need these classes. */
/*Client Class starts*/
public class Client {
public static void main (String args[]) {
String str;
/* checks if the
user has send any SQL string*/
if( args.length == 0) {
str = "SELECT * FROM test";
System.out.println("Using default sql query -> " +
str);
}
else
{
str = args[0];
}
/* Wrap the CORBA
ORB communication in a try-catch loop*/
try {
// Create and initialize the ORB
ORB orb = ORB.init (args, null);
/* Get the root naming context and
narrow it down using the helper classes*/
org.omg.CORBA.Object objRef = orb.resolve_initial_references
("NameService");
NamingContext ncRef = NamingContextHelper.narrow (objRef);
// Resolve the object reference in
naming
NameComponent nc = new NameComponent ("DBAccess",
"");
NameComponent path[] = {nc};
/* In one step we
get the Object Reference and cast it to the proper type*/
DB.DBAccess dbRef = DBAccessHelper.narrow (ncRef.resolve
(path));
// Call the time server object and
print results
String query = "The result: \n" + dbRef.runQuery
(str);
System.out.println (query);
} catch (Exception e) {
e.printStackTrace ();
} //end try-catch
} // end main()
}//end
class
_____<Completes the list of
Client.java>______
This
class implements the method mention in the IDL. This class sits on the server
and the server program will create an object of this class and register it with
the Naming Service. This class also contains some JDBC code. The first step is
to load the JDBC-ODBC Brdige. Then we attach a ODBC connection to this. Then we
create a Statement object and pass it the SQL we want to run. The executeUpdate
method will send the query to the database. The results are returned in a
Resultset Object.
// The package
containing our stubs.
import DB.*;
// This package
contains the JDBC classes
import
java.sql.*;
// Server will
use the naming service.
import
org.omg.CosNaming.*;
/* The package
containing special exceptions thrown by the name
service.*/
import
org.omg.CosNaming.NamingContextPackage.*;
// All CORBA
applications need these classes.
import
org.omg.CORBA.*;
import
java.util.*;
import
java.text.*;
/* This is our
class which contains the interface and the operaton defined in the IDL. This
class extends the server-side skeleton files*/
class DBAccess
extends _DBAccessImplBase {
//This is the
operation that is exposed through the IDL
public String
runQuery (String q) {
String name = "";
// The JDBC
database operations are done in a try-catch block.
try {
// Load the
JDBC-ODBC Bridge.
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver").newInstance();
// make a
connection to the specified URL
Connection con =
DriverManager.getConnection( "jdbc:odbc:Shalimar_Access",
"", "");
// get a
Statement object from the Connection
Statement stmt = con.createStatement();
/* ignore any
exception for DROP TABLE; it will most assuredly throw one if the table does
not exist*/
try {
stmt.executeUpdate("DROP TABLE
test");
} catch (Exception e) {
// do nothing
}
// create a table
stmt.executeUpdate("CREATE TABLE
test(name CHAR(25), id INT) " );
// insert a
couple of rows.
stmt.executeUpdate("INSERT INTO test
VALUES('Brian', 1)");
stmt.executeUpdate("INSERT INTO test
VALUES('Hank', 2)");
// Execute a
query
ResultSet rs = stmt.executeQuery(q);
while(rs.next()) {
// retrieve each
column by name
name += rs.getString("name") +
", ID: " + rs.getInt("id") + "\n";
}
System.out.print(name);
stmt.close();
con.close();
System.out.println("Operation
successful.");
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
return (name);
}
}
_____<Completes the list of
DBAccess.java>______
The server class
contains code to created a new DBAccess object. This object reference is added
to the Naming Service. Then the server just waits of an invocation.
// The package
containing our stubs.
import DB.*;
// Server will
use the naming service.
import
org.omg.CosNaming.*;
/* The package
containing special exceptions thrown by the name
service.*/
import
org.omg.CosNaming.NamingContextPackage.*;
// All CORBA
applications need these classes.
import
org.omg.CORBA.*;
import
java.util.*;
import
java.text.*;
/* This class
creates the CORBA object and makes it available to the naming service*/
public class
Server {
public static void main (String args[]) {
try {
// Create and
initialize the ORB
ORB orb = ORB.init (args, null);
// Create the
servant and register it with the ORB
DBAccess dbRef = new DBAccess ();
orb.connect (dbRef);
// Get the root
naming context and narrow it
org.omg.CORBA.Object objRef =
orb.resolve_initial_references ("NameService");
NamingContext ncRef =
NamingContextHelper.narrow (objRef);
// Bind the
object reference in naming
NameComponent nc = new NameComponent
("DBAccess", "");
NameComponent path[] = {nc};
ncRef.rebind (path, dbRef);
// Wait forever
for current thread to die
Thread.currentThread ().join ();
} catch (Exception e) {
e.printStackTrace ();
}
}
}
_____<Completes the list of
Server.java>______
The follow source files
are generated by the IDL compiler. The are used to translate the User defined
objects to the ORB. The ORB knows how to transmit the data over the wires, but
it does not know the classed defined by the user. These IDL generated files
help the ORB to understand these classes.
This file contains the class which maps to the interface statement in the IDL.
package DB;
/**
*
DB/DBAccess.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from
DBAccess.idl
* Monday, December
4, 2000 4:22:43 PM EST
*/
public interface
DBAccess extends DBAccessOperations, org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
} // interface
DBAccess
_____<Completes the list of
DB.DBAccess.java>______
This
files contains an important method called “narrow”. This is used to convert the
type of the Object Reference returned. This method provides functionality
similar to the casting operation.
package DB;
/**
*
DB/DBAccessHelper.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from
DBAccess.idl
* Monday,
December 4, 2000 4:22:43 PM EST
*/
abstract public
class DBAccessHelper
{
private static String _id = "IDL:DB/DBAccess:1.0";
public static void insert (org.omg.CORBA.Any
a, DB.DBAccess that)
{
org.omg.CORBA.portable.OutputStream out =
a.create_output_stream ();
a.type (type ());
write (out, that);
a.read_value (out.create_input_stream (),
type ());
}
public static DB.DBAccess extract
(org.omg.CORBA.Any a)
{
return read (a.create_input_stream ());
}
private static org.omg.CORBA.TypeCode
__typeCode = null;
synchronized public static
org.omg.CORBA.TypeCode type ()
{
if (__typeCode == null)
{
__typeCode = org.omg.CORBA.ORB.init
().create_interface_tc (DB.DBAccessHelper.id (), "DBAccess");
}
return __typeCode;
}
public static String id ()
{
return _id;
}
public static DB.DBAccess read
(org.omg.CORBA.portable.InputStream istream)
{
return narrow (istream.read_Object
(_DBAccessStub.class));
}
public static void write
(org.omg.CORBA.portable.OutputStream ostream, DB.DBAccess value)
{
ostream.write_Object
((org.omg.CORBA.Object) value);
}
public static DB.DBAccess narrow
(org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof DB.DBAccess)
return (DB.DBAccess)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 ();
return new DB._DBAccessStub (delegate);
}
}
}
_____<Completes the list of
DB.DBAccessHelper.java>______
Holder class is generated to provide support to the out and
inout parameter passing modes.
package DB;
/**
*
DB/DBAccessHolder.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from DBAccess.idl
* Monday,
December 4, 2000 4:22:43 PM EST
*/
public final
class DBAccessHolder implements org.omg.CORBA.portable.Streamable
{
public DB.DBAccess value = null;
public DBAccessHolder ()
{
}
public DBAccessHolder (DB.DBAccess
initialValue)
{
value = initialValue;
}
public void _read
(org.omg.CORBA.portable.InputStream i)
{
value = DB.DBAccessHelper.read (i);
}
public void _write
(org.omg.CORBA.portable.OutputStream o)
{
DB.DBAccessHelper.write (o, value);
}
public org.omg.CORBA.TypeCode _type ()
{
return DB.DBAccessHelper.type ();
}
}
____<Completes the list of
DB.DBAccessHolder.java>______
This
files contains the operations mentioned in the IDL file.
package DB;
/**
*
DB/DBAccessOperations.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from
DBAccess.idl
* Monday,
December 4, 2000 4:22:43 PM EST
*/
public interface
DBAccessOperations
{
String runQuery (String query);
} // interface
DBAccessOperations
_____<Completes the list of
DB.DBAccessOperations.java>______
This is the
client stub file. The client uses this file to talk to the ORB.
package DB;
/**
*
DB/_DBAccessStub.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from
DBAccess.idl
* Monday,
December 4, 2000 4:22:43 PM EST
*/
public class
_DBAccessStub extends org.omg.CORBA.portable.ObjectImpl implements DB.DBAccess
{
// Constructors
// NOTE:
If the default constructor is used, the
//
object is useless until _set_delegate (...)
//
is called.
public _DBAccessStub ()
{
super ();
}
public _DBAccessStub
(org.omg.CORBA.portable.Delegate delegate)
{
super ();
_set_delegate (delegate);
}
public String runQuery (String query)
{
org.omg.CORBA.portable.InputStream _in =
null;
try {
org.omg.CORBA.portable.OutputStream
_out = _request ("runQuery", true);
_out.write_string (query);
_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 runQuery (query);
} finally {
_releaseReply (_in);
}
} // runQuery
// Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:DB/DBAccess:1.0"};
public String[] _ids ()
{
return (String[])__ids.clone ();
}
private void readObject
(java.io.ObjectInputStream s)
{
try
{
String str = s.readUTF ();
org.omg.CORBA.Object obj =
org.omg.CORBA.ORB.init ().string_to_object (str);
org.omg.CORBA.portable.Delegate
delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
_set_delegate (delegate);
} catch (java.io.IOException e) {}
}
private void writeObject
(java.io.ObjectOutputStream s)
{
try
{
String str = org.omg.CORBA.ORB.init
().object_to_string (this);
s.writeUTF (str);
} catch (java.io.IOException e) {}
}
} // class
_DBAccessStub
_____<Completes the list of
DB._DBAccessStub.java>______
This
the server-skeleton file. This sits on the server-side and call the methods
requested by the client. The requests are received by the ORB, which needs this
file to find the object and the method requested.
package DB;
/**
*
DB/_DBAccessImplBase.java
* Generated by
the IDL-to-Java compiler (portable), version "3.0"
* from
DBAccess.idl
* Monday,
December 4, 2000 4:22:43 PM EST
*/
public abstract
class _DBAccessImplBase extends org.omg.CORBA.portable.ObjectImpl
implements DB.DBAccess,
org.omg.CORBA.portable.InvokeHandler
{
// Constructors
public _DBAccessImplBase ()
{
}
private static java.util.Hashtable _methods
= new java.util.Hashtable ();
static
{
_methods.put ("runQuery", new
java.lang.Integer (0));
}
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: // DB/DBAccess/runQuery
{
String query = in.read_string ();
String __result = null;
__result = this.runQuery (query);
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
// Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:DB/DBAccess:1.0"};
public String[] _ids ()
{
return __ids;
}
} // class
_DBAccessImplBase
_____<Completes the list of
DB._DBAccessImplBase.java>______
BankSimple Example:
The server implements two
types of objects. On start-up it contains a single Bank objects, by calling the
create_account method, clients can request the Bank to create a named Account
object, or to look up an Account by name. Accounts allow you to to make
deposits, withdrawals etc.
The client is a simple
console menu based application which calls operations defined by in banksimple.idl. Parts of the IDL are given below;
The completed is attached at
the back.
interface Bank
{
Account create_account (in
string name); // Create a new account.
Account find_account (in
string name); // Find an existing account.
};
The Bank interface lets you
create an account or find an existing acccount.
interface Account
{
readonly attribute string
name;
readonly attribute CashAmount
balance;
void deposit (in CashAmount
amount);
void withdraw (in CashAmount
amount);
};
The account interface has
methods to deposit, withdraw, get cash balance and the name attribute on the
account.
·
banksimple.idl: defines the IDL interfaces . The interfaces are
defined inside a module to prevent clashes with similarly named interfaces
defined in the other bank demos.
·
client.cxx: The main client program, does client initialization
and then starts a menu. Uses the following files:
·
bankmenu.h, bankmenu.cxx: menu code which calls
operations on the IDL Bank interface
·
accountmenu.h, accountmenu.cxx: menu code which calls operations
on the IDL Account interface
·
server.cxx: the main server program, initializes the server and
creates IDL implementation objects. Uses the following files:
·
banksimple_bankimpl.h, banksimple_bankimpl.cxx: Class which
implements the Bank IDL interface
·
banksimple_accountimpl.h, banksimple_accountimpl.cxx: Class which
implements the Account IDL interface
Type nmake to build the executables.
First start a Dos Window and
change to the "c:\iona\config" folder. I have installed IONA on my
"c:" drive and in the default folder. Run the batch file called
"orbvars.bat" which sets all the environment variables. Now start the
Orbix ORB by typing the following command,
Type start orbixd to start
the ORB
Type nmake /I register to
register the servers
The makefile has the
necessary commands to start the naming service and create the entries for the
current application. The complete makefile is attached at the back.
Type client to run the
client. The client will display a text menu allowing you to choose the actions
you want to take, and then prompt you for the necessary information. The server
outputs messages when it processes incoming calls - you can see these messages
by looking at the Orbix daemon's output window.
The following is a sample of the output from the client and server
client
[192:
New IIOP Connection (machine:1570) ]
[192:
New IIOP Connection (machine:1591) ]
0 -
quit
1 -
create_account
2 -
find_account
Enter
selection (0 - 2): 1
Enter
account name: fred
[192:
New IIOP Connection (machine:1595) ]
Using
account named fred
0 -
quit
1 -
get the name attribute
2 -
get the balance attribute
3 -
call deposit operation
4 -
call withdraw operation
Enter
selection (0 - 4): 3
Current
balance is: 0
Enter
amount to deposit: 1000
Depositing
1000
New
balance is: 1000
0 -
quit
1 -
get the name attribute
2 -
get the balance attribute
3 -
call deposit operation
4 -
call withdraw operation
Enter
selection (0 - 4): 4
Current
balance is: 1000
Enter
amount to withdraw: 345.26
Withdrawing
345.26
New
balance is: 654.74
[IT_Demo/BankSimple/Bank:
Server "IT_Demo/BankSimple/Bank" is now available to the network ]
[
Configuration tcp/1604/cdr ]
[IT_Demo/BankSimple/Bank:
New IIOP Connection (machine:1604) ]
create_account:
Created account with name: fred
name:
returning value fred
balance:
returning value 0
deposit:
amount = 1000
balance:
returning value 1000
balance:
returning value 1000
withdraw:
amount = 345.26
balance:
returning value 654.74
Screen snap shoots
are included.
# @ Copyright 1998 IONA
Technologies, PLC. All Rights Reserved.
#
# @ VERSION: Orbix 3.0c
#
# Include standard make
variables & rules
#
!include
"..\..\orbixcpp.mk"
DEMO=BankSimple
IDL_BASENAME=banksimple
CLIENT_OBJS=$(IDL_BASENAME)$(IDL_CLT_OBJ)
BankMenu.obj AccountMenu.obj client.obj
SERVER_OBJS=$(IDL_BASENAME)$(IDL_SRV_OBJ)
$(IDL_BASENAME)_BankImpl.obj \
$(IDL_BASENAME)_AccountImpl.obj server.obj
# The following make rules
differ slightly between demos.
#
all: build
build: DemoLib client.exe
server.exe
@echo == Build complete, to register server type: $(MAKE)
/I register
register:
@echo == Checking that the orbix daemon is running ...
$(ORBIX_BINDIR)\pingit
@echo == Make Imp.Rep. and NS directories
@echo == Ignore errors indicating the directories already
exist
$(ORBIX_BINDIR)\mkdirit IT_Demo
$(ORBIX_BINDIR)\mkdirit IT_Demo/$(DEMO)
$(ORBIX_BINDIR)\putit NS
"%ORBIX_HOME%\bin\ns.bat"
$(ORBIX_BINDIR)\putit IT_Demo/$(DEMO)/Bank
$(MAKEDIR)\server.exe
$(NAMES_ROOT)\bin\putnewncns IT_Demo
$(NAMES_ROOT)\bin\putnewncns IT_Demo.$(DEMO)
server -bindns -timeout 0
@echo == Now run the client by typing: client
clean: default_clean
# IDL compile rule (invokes
implicit .idl.hh rule)
$(IDL_BASENAME)$(IDL_CLT_CPP)
$(IDL_BASENAME)$(IDL_SRV_CPP): $(IDL_BASENAME).hh
client.exe: $(CLIENT_OBJS)
$(LINK) $(LINK_FLAGS_EXE) /OUT:$@ $(CLIENT_OBJS)
$(LINK_LIBS)
server.exe: $(SERVER_OBJS)
$(LINK) $(LINK_FLAGS_EXE) /OUT:$@ $(SERVER_OBJS)
$(LINK_LIBS)
// @ Copyright 1999 IONA
Technologies, PLC. All Rights Reserved.
// @ VERSION: Orbix 3.0c
// DESCRIPTION:
// IDL for a simple,
no-frills client/server Bank
// NOTE: These interfaces
don't provide exceptions to indicate
// when things go wrong
//
#ifndef _BANK_BANKSIMPLE_IDL_
#define _BANK_BANKSIMPLE_IDL_
// Put our definitions inside
a module to prevent namespace clashes.
//
module BankSimple
{
typedef float CashAmount; //
Define a named type to represent money.
interface Account; //
Forward reference, Account is defined below.
// The Bank creates or locates Accounts.
//
interface Bank
{
Account create_account (in string name); //
Create a new account.
Account find_account (in string name); // Find an existing
account.
};
// The account lets you check your balance, deposit or
withdraw money.
//
interface Account
{
readonly attribute string name;
readonly attribute CashAmount balance;
void deposit
(in CashAmount amount);
void withdraw (in CashAmount amount);
};
};
#endif
Client Console Application:
Start menu has two option,
Create an Account or Find for a Account




Iona Orbix Product Documentaton.
Iona Knowledgebases on the Website.
Orb Specification document by OMG group.
IDL specification document by OMG group.
Enterprise Java Computing-Applications and
Architecture - Gopalan Suresh Raj (Co-Author)
The Awesome Power of JavaBeans - Gopalan Suresh Raj
(Co-Author)
Package org.omg.CORBA which provides the mapping of
the OMG CORBA APIs to the Java programming language
Sun's documentaton on CORBA on java.sun.com.
Java 2.0 API documentation.
Java IDL documentation on java.sun.com.
Iona Orbix 3.01 product documentation.
Iona Orbix 3.01 BankSimple example.