 |
|
By
Mr. Jacob Jose Cherakal,
Computer Architect,
iCMG, Bangalore |
|
Though Java and CORBA are complementary
technologies, Java has a private world of
its own and so does CORBA. All CORBA objects
were intended to live beyond a programming
language world like that of Java or C++.
In fact, all CORBA objects live in a virtual
world with semantics of their own, and remember,
they can only be accessed through the interfaces
they expose. Another point to be noted is
that CORBA does not associate remoteness
into the objects, only location transparency
is what is introduced at this point.
|
| Blending
Java and CORBA |
|
As we all know that Java is a simple language
to learn. I would say the same for CORBA.
If both are simple, how could it be that
the mapping is complex? CORBA has a well-defined
set of basic and complex types that Java
has in abundance as well. However, there
were structural features of CORBA that Java
lacked. The structs and unions were missing
in Java. So were the typedefs and enumeration.
Java's logic was simple, everything can
be achieved as a class and the mapping is
a simple testament to this principle. Let's
take a closer look at the mapping specification.
From the beginning, it
would have been possible for a very tightly
integrated mapping to Java, with less semantics
of CORBA made visible. Some of the goals
of mapping are listed below:
|
 |
Client
side and server side source code portability |
 |
ORB
replaceability |
 |
Binary
compatibility between client stubs/sever
skeletons and the ORBs. |
 |
Reverse
mapping was also considered. |
|
| ORB Replaceability
|
|
Most of the mapping stops short of defining
an interface for the stubs/skeletons and
the ORB. This was usually left as an exercise
for the ORB vendors. However, in the case
of Java, an interface between the stub/skeleton
and the ORB would be best suited.
Imagine the scenario of
having a CORBA enabled Applet downloaded
to a Netscape browser. However, the server
side is written in OrbixWeb. Suppose you
have different versions of the stub and
skeleton code, then the application developer
would have to recompile the IDL file each
time an incompatible ORB was found on the
client side. In the eventuality of having
to cope with a non-compatible ORB, the ORB
code is to be downloaded into the local
machine along with the stub and skeleton
code, which is not a good proposition for
most of the clients.
So is the case for downloading
of the skeleton code. An alternative to
this is to use the dynamic invocation interface,
which however is not efficient. Hence, it
is required of a compliant ORB, be it at
the client side or at the server side, they
have to adhere to the interface specifications
that exist between the stub/skeleton and
ORB.
All the stubs inherit from
the org.omg.CORBA.portable.ObjectImpl class.
|
| Request
Transfer Mechanism |
 |
Stream-based
approach (stub and skeleton)
Stream based skeletons directly extend
from the org.omg.PortableServent.Servant.
|
 |
DII
based approach (stub) |
 |
DSI
approach (skeleton)
DSI based approach extends from the
org.omg.PortableServer.DynamicImplementation
class. |
|
|
This is one way of achieving the binary
compatibility. The main advantage of this
approach will be the generation of applets
that can work with any Java Mapping complaint
ORB's stubs and skeletons.
|
| Mapping
Types -Basic and Complex |
| Basic
Types: |
| boolean |
boolean |
| char |
char - CORBA::DATA_CONVERSION |
| wchar |
char |
| octet |
byte
|
| string |
java.lang.String
- CORBA::DATA_CONVERSION, CORBA::MARSHAL
|
| wstring |
java.lang.String
- CORBA::MARSHAL |
| short |
short
|
| unsigned
short |
short
|
| long |
int
|
| unsigned
long |
int
|
| long
long |
long
|
| unsigned
long long |
long
|
| float |
float |
| double
|
double
|
| fixed |
java.lang.BigDecimal
- CORBA::DATA_CONVERSION |
| any |
CORBA.Any
class |
|
|
| Complex Types: |
| Mapping
of struct: |
|
A struct is mapped to a final class with
public members, just as in the case of a
struct that is seen in C/C++. It's definitely
not familiar for Java programmers to use
public members. You can use either the null
constructor or a constructor, which takes
arguments in the order of members declared
in the IDL struct.
In a way, you can use this
to pass objects by value; however, this
is not the best of solutions. It would be
better if you are able to send an object
as a sequence of octets and have your own
marshalling function defined.

|
| Mapping of
union |
|
This is another of those C/C++ features
with little application in the current world.
A union will be mapped to a final class
with
|
 |
Default
constructor |
 |
Assessor
methods for the discriminator, named
discriminator |
 |
An
assessor method for each branch |
 |
A modifier
method for branch |
 |
A modifier
method for each which has more than
one case labels |
 |
A default
constructor method of needed. |
|
| Mapping of
array: |
|
An array is mapped to a Java Array. It is
simple and efficient. The bounds of the
marshaled array is checked and this would
result in a CORBA::MARSHAL exception if
an incompatibility is detected.
|
| Mapping of
sequence: |
|
A special array, which can vary in size.
There are two types of sequences. Those
that are bound to a maximum size and those
that are not delimited by an upper limit..
The advantage that sequence has over an
array is simple, you do not have to pass
extra data if it is not necessary. As for
an array, you have to initialize unwanted
elements as well. Hence, a sequence is widely
used.
A sequence is mapped to
a Java array with the same name. In the
mapping, everywhere the sequence type is
needed, an array of the mapped type of the
sequence element is used. Bounds checking
shall be done on bounded sequences when
they are marshaled as parameters to IDL
operations and an IDL CORBA::MARSHAL exception
is raised if needed.
|
| Mapping of
enum: |
|
IDL enum is mapped to a Java final class
with the same name as the enum type, which
declares a value method, two static data
members per label, an integer conversion
method and a private constructor.
|
| Mapping of
exception: |
|
IDL exceptions are mapped to Java exception
classes. CORBA system exceptions are unchecked
exceptions. They inherit from java.lang.RuntimeException.
User defined exceptions
are checked exceptions. They inherit from
java.lang.Exception.
|
| Mapping of
interfaces: |
|
The most important of them all feature in
CORBA. As we all know, an IDL is used primarily
for specification, and to specify your system
in object oriented terms, you do so with
the help of interfaces.
An IDL interface is mapped
to the Java equivalent - interfaces. But
we need to put CORBA semantics into the
interface and for this purpose, they inherit
from another interface - org.omg.CORBA.Object
interface.
The Java interface contains
the mapped operation signatures. Methods
can be invoked on an object reference to
this interface. Attributes are mapped to
get and set methods, with the exception
of read-only attributes, which can have
only get methods generated. out and inout
parameters have their holders as generated
argument signatures.
|
| Mapping of
modules: |
|
Simple, they are mapped to packages.
|
| Mapping of
typedefs: |
|
Recursively unwound to the original type.
|
| Mapping of
constants: |
|
Depends upon the scope in which they are
defined. If it's defined in an interface,
the constant declarations - public static
final type is added to the Java interface.
However, if it is declared outside one interface,
they are mapped to public interfaces with
the same name as the constant and containing
a public static final field, named value,
that holds the constant's value. Note that
the Java compiler will normally inline the
value when the class is used in other Java
code.
|
| Helpers and
Holders |
|
The helper classes are classes that contain
methods that are needed to access type information,
Any insertion and extraction and portable
methods to manipulate the input/output streams.
What is special about all these methods,
are that they are static.
|
| Type
Information Static Method |
| Static
method |
Function |
| id() |
Get the Repository ID |
| type() |
Get the typecode |
| narrow() |
Downcast |
| insert()/extract() |
Manipulation |
| read()/write() |
Provide
portable access to the input and
output streams |
|
|
|
Holders are containers that contain the
respective type. Mainly used for the passing
of a particular type by reference. This
constrain is not visible in C++ as it has
the pass by reference semantics to it. As
in the case of Java, when you pass a reference,
you pass it by value and not by reference.
This is much visible when you attempt to
swap two objects, say of type Person. You
will be able to achieve this with the help
of a container that contains the reference.

|
| ORB interface |
|
You still need APIs to connect to the CORBA
world. This can be done using the init()
method in the ORB abstract class that is
provided by the ORB interface. Initializing
an ORB in an applet is different from that
of applications.
|
| Java Portability
Interfaces: |
These are API's used to provide the minimal
set of functionality to allow portable stubs
and skeletons to be used with a Java ORB.
Let's talk about the design goals (I quote
from the mapping specification):
|
 |
Size:
Stubs and skeletons must have a small
bytecode footprint in order to make
downloading fast in a browser environment
and to minimize memory requirements
when bundled with a Java VM. |
 |
Performance:
Care must be taken to minimize temporary
Java object creation during invocations
in order to avoid Java VM garbage collection. |
 |
Reverse
Mapping: The design does not require
adding methods to user defined types
such as structures and exceptions. It
need not ensure that stubs and skeletons
generated by IDL to Java compilers and
reverse Java to IDL mapping tools are
interoperable and binary compatible. |
|
|
For this purpose, the mapping has adopted
a delegation-based approach to achieve the
goals stated above. Also, the stubs and
skeletons use the DII and DSI to achieve
maximum portability.
|
| The major components
of the architecture are: |
|
 |
Portable
Streamable - provides standard APIs
to read and write IDL datatypes (discussed
above in the Helper section). |
 |
Portable
Streams - provide standard to the ORB
marshalling engine (classes like the
InputStream and the OutputStream). |
 |
Portable
Stubs and Skeletons - discussed above. |
 |
Portable
Delegate - provides vendor specific
implementation of CORBA object. |
 |
Portable
Servant Delegate - Provides vendor specific
implementation of the PortableServer::Servant. |
 |
ORB
initialization - discussed above. |
|
|
I would like to talk more about the Portable
Delegate. The Delegate abstract class specifies
a portable API for ORB-vendor-specific implementation
of the org.omg.CORBA.Object methods. When
a proxy is instantiated a delegate object
is passed to it. All calls to org.omg.CORBA.Object
methods are forwarded to this class. This
allows a stub generated by one vendor's
ORB to work with the delegate from another
vendor's ORB. Operations like create_request(),
equals(), is_a(), is_equivalent(), is_local(),
hash() are all delegated to the ORB through
the delegate object. All this is applicable
to the stubs. There are compatible operations
for the skeleton as well. This is one of
the best mappings that I have seen.
You will see more of valuetypes
and components in the forthcoming discussions.
|
|
CORBA® is a trademark
of a Object Management Group, Inc., registered
in the United States of America
Java is a trademark
of Sun Microsystems, Inc.
|
|
|