|
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.
- Mr.
Jacob Jose Cherakal - Software Architect,
1. CORBA® is a trademark
of a Object Management Group, Inc., registered in
the United States of America
2. Java is a trademark
of Sun Microsystems, Inc.
|