Level: Introductory Dave Bartlett (dbartlett@pobox.com), Consultant, author, and lecturer, Freelance
01 Nov 2000 We continue our examination of the IDL-to-Java mapping with a look at more complex types and helper classes in this month's CORBA Junction. Last month, in part
one of IDL-to-Java mapping, we looked at the basic data types,
structure, and data transfer. This month we will sink our teeth into mapping constants and structures,
and we will take a look at some of the more complex types such as
sequences and arrays, exceptions and the
Any
type. Lastly,
we will look at helper classes and what they provide. First, I should remind you that we are using an Interface Definition Language and
the goal of any IDL is to create interfaces to some component or server.
This means that we are creating new types. So let's start by looking at
the mapping for the
interface
keyword, the mechanism to
create new types using OMG IDL. Interfaces
We will go back to the mother of all IDL files: MotherIDL.idl.
I use this file to look at all the nooks and crannies of the IDL-to-Java
mapping. In MotherIDL there is an interface named FindPerson. It looks
like this: Listing 1. FindPerson interface
interface FindPerson {
Person GetByName(in string sName);
Person GetBySSN(in string sSSN);
};
|
Nothing elaborate but for now we will focus on the interface and the
two methods GetByName() and GetBySSN(). What does that keyword
interface mean, exactly? Looking closely at the OMG CORBA
specification we see the interface keyword is followed by an
identifier (in this example FindPerson). FindPerson names the
interface and defines a legal type name. This type name can now be used
anywhere that an identifier is legal within the grammar of IDL. When
FindPerson is used within the IDL as a method parameter or as a member
of a structure it is important to remember that FindPerson represents a
reference to an object that supports the FindPerson interface. The goal with the IDL-to-Java mapping is to produce a representation of
that interface reference in the Java programming language that a Java
programmer can use, whether client programmer or server programmer. I ran
MotherIDL through the IDL-to-Java compiler that comes with the Orbacus ORB,
JIDL, using this command line:
jidl -- output-dir ..\..\.. motheridl.idl
Since MotherIDL has lots of stuff in it, you will get a pile of generated
code. We are interested in the files FindPerson.java and
FindPersonOperations.java that were dropped into the
corbasem\gen\motheridl\holderexample directory.
FindPerson.java is called the signature interface. It is simple: Listing 2. FindPerson.java public interface FindPerson extends FindPersonOperations,
org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
} |
What is going on here? There's nothing in there! There is actually a
great deal in there, it's just hidden. First notice two Java keywords:
public interface
. Yes, the Java programming language has
its own
interface
keyword. This is great because it makes the
mapping quite nice and smooth. The Java programming language's
interface
keyword produces a
completely abstract class, a class that provides no implementation at all.
However, the
interface
keyword in Java is designed to allow
the interface creator to establish the form of a class: method names,
argument lists, and return types, but no method bodies. So where is
GetByName() and GetBySSN()? The methods signatures are located in FindPersonOperations.java.
FindPersonOperations.java, known as the operations interface, is the file
containing the method specifics such as name, parameters, return types and
any exceptions to be raised. Listing 3. FindPersonOperations.java public interface FindPersonOperations
{
public Person GetByName(String sName);
public Person GetBySSN(String sSSN);
} |
This looks more like the interface file that one would expect. But why
two interface files? Flexibility, that's why. The previous version of
the mapping generated different files depending on whether you were going to
implement the interface using inheritance or implement the interface using
delegation. Since the Java programming language does not support multiple
implementation inheritance, an inheritance-based implementation is not
always best. Delegation is just as powerful a composition mechanism as
inheritance and it usually provides the solution when an inheritance-based
approach doesn't fit. In this version of the specification there is just
one mapping for the interface. There is a 'tie' switch on the IDL-to-Java
compilers because the two implementation methods are different enough to
require some downstream tinkering. The topic of implementation using
inheritance versus implementing using delegation will be saved for another
month and requires a side trip into design patterns. (If you are interested in a terrific introduction to inheritance vs.
delegation, read the first chapter of the Design Patterns book by the
Gang-of-Four. See Resources for more information.) Let's just say that the mapping produces a set of files that gives you the flexibility to choose an implementation design to fit your needs.
Constants
Variables and constants -- they're pretty straightforward. Variables have
values that can change and constants have values that never change. In
some programming languages the difference between the two can be quite
severe -- and between programming languages it's downright messy. As an
example, in pre-Standard C, if you wanted to make a constant, you had to
use the preprocessor. This placed control of those constants outside the
scope of the compiler and hence no type checking was performed. Of
course, this changed in C++ and eventually in C, but as you can see, it is
not as nice and simple as you would have guessed and mapping constants to
the various languages supported by the OMG is important. The Java programming language is quite capable of handling constants
although it does not use the constant keyword like C++. In the Java
programming language, constants must be primitives and they are expressed
with the final keyword. A value must be given at the time of
the definition. A field that is both static and final has only one piece
of storage that cannot be changed. If the constant is an object reference
rather than a primitive, then it is the reference that must always point
to the same object and never another object. The IDL-to-Java mapping handles constants in two ways: constants that are
declared within interfaces and constants that are declared outside
interfaces. Constants declared within interfaces are easy because they
simply map to fields within the signature interface class. Within
MotherIDL I have a module named ConstructedTypes. There we create a set
of tests for the use of the constant keyword. The section to review is: Listing 4. Constant examples // Constant examples
// Constant inside an interface
interface PhysicalConstants {
const float EquatorialRadiusEarth = 6378.388; // km
};
// A constant outside an interface
const float MeanDensityEarth = 5.522; // g/cm^3 |
You can see that EquatorialRadiusEarth is inside an interface and
MeanDensityEarth is outside of any interface and resides only within a
nesting of modules. The file corbasem\gen\motheridl\constructuredtypes
\PhysicalConstants.java was produced from the jidl command given earlier
and it looks like this: Listing 5. PhysicalConstants.java public interface PhysicalConstants extends
PhysicalConstantsOperations,
org.omg.CORBA.Object,
org.omg.CORBA.portable.IDLEntity
{
public static final float EquatorialRadiusEarth =
(float)(6378.38818359375D);
} |
This is clear-cut. The constant is defined inside the interface and it
adheres to the underlying goal of the interface, which is to allow the
designer to establish the form of the class. Now the user of the
interface has the ability to use EquatorialRadiusEarth throughout the
interface implementation. But what if we would like to use a constant across several interfaces?
This is bit trickier with the Java programming language because of the
fact that everything must be an object and creating a new type means
creating a class. This is exactly what is done for the constant
MeanDensityEarth. A new interface class, MeanDensityEarth.java, is
created: Listing 6. MeanDensityEarth.java public interface MeanDensityEarth
{
public static final float value = (float)(5.5219998359680176D);
} |
The interface class MeanDensityEarth wraps up the constant in a single
float member named value. Notice that the interface is public and has the
same name as the constant defined in the IDL. Most Java programming
language compilers will normally inline the value when the class is used
in other Java code. Although this may seem more indirection than you
would care to have, in the end the value is inserted by the compiler and
the Java programming language gets to adhere to one of its design
principles --
everything is an object. (Well OK, almost
everything.)
Structures
OMG's IDL is capable of expressing the concept of a structure through the
keyword struct. Basically, this just lets you weld together
several primitive or complex types into one type. The Java programming
language does not have the struct keyword and, as stated
earlier, programmers are encouraged to put everything into an object
(for example, a class). For these reasons, an IDL struct is mapped to a final
class with the same name. The class provides instance variables for the
fields in IDL member ordering and a constructor for all values, and
implements IDLEntity. A null constructor is also provided, which allows a
programmer to fill them later. Let's return to the constructedtypes module of MotherIDL and find the
definition of the struct SurfReport: Listing 7. SurfReport struct
// use of struct
struct SurfReport {
Weather presentWeather;
waveHeightT waveHeight;
waterTempT waterTemp;
windDirectionT windDirection;
windSpeedT windSpeed;
}; |
This IDL struct maps to the Java programming language file SurfReport.java
that can be found in constructedtypes directory of MotherIDL: Listing 8. SurfReport.java
final public class SurfReport implements
org.omg.CORBA.portable.IDLEntity
{
public
SurfReport()
{
}
public
SurfReport(Weather _ob_a0,
short _ob_a1,
short _ob_a2,
char[] _ob_a3,
short _ob_a4)
{
presentWeather = _ob_a0;
waveHeight = _ob_a1;
waterTemp = _ob_a2;
windDirection = _ob_a3;
windSpeed = _ob_a4;
}
public Weather presentWeather;
public short waveHeight;
public short waterTemp;
public char[] windDirection;
public short windSpeed;
}
|
Enum
Enumerations have always proved useful. Enums are used frequently and are
easy to understand. But it has always been difficult for me to understand
why the Java programming language never included an enum
keyword. It's easy enough to code and I'm sure they didn't want the
language to bloat but, hey, I miss it. So how does this mapping happen?
Let look at the MotherIDL enum Weather: Listing 9. enum Weather
enum Weather{cloudy,
sunny};
|
The IDL enum Weather will be mapped to a Java programming language class
using the same name as the enum type -- Weather. This class
declares a value method that returns the present value of Weather, two
static data members per enum element, an integer conversion
method, from_int(), and a protected constructor. This class is shown
below: Listing 10. Class Weather
final public class Weather implements org.omg.CORBA.portable.IDLEntity
{
private static Weather [] values_ = new Weather[2];
private int value_;
public final static int _cloudy = 0;
public final static Weather cloudy = new Weather(_cloudy);
public final static int _sunny = 1;
public final static Weather sunny = new Weather(_sunny);
protected Weather(int value)
{
values_[value] = this;
value_ = value;
}
public int value()
{
return value_;
}
public static Weather from_int(int value)
{
return values_[value];
}
}
|
Notice that one of the public static final data members is an int and has an underscore (_) prepended to the name of one of
the IDL enum elements. The intended use of this member is in
switch statements. The other data member is of the enum type(in this case, Weather). It has the same name as the IDL
enum element label (cloudy or sunny, in this example).
Union
A discriminated union is really just a structure designed to save memory
space. The union will reference only one of several data members based
upon a discriminator. These data members may be different types. Only
one of the data members will be in memory at any point in time. As an
example, MotherIDL defines the union BarometricPressure: Listing 11. Union BaraometricPressure
enum PressureScale{inches,cc};
union BarometricPressure switch (PressureScale) {
case inches :
float BarometerInInches;
case cc :
short BarometerInCCs;
};
|
Depending upon whether PressureScale is inches or cubic centimeters
determines whether the element to be used needs to be a float or a short.
Java does not support the union natively; therefore, it has to be built. The mapping for an IDL union is similar to the mapping for all structures
except there are certain methods added to create the functionality of a
discriminated union. The mapping starts out with a final class with the
same name as the IDL union. Added to this class are:
- A default constructor
- An accessor method for the discriminator, named
discriminator()
- An accessor method for each branch
- A modifier method for branch
- A modifier method for each branch that has more than one case
label
- A modifier method for the branch corresponding to the default label
if present
- A default modifier method if needed
Exact implementations vary from vendor to vendor but to adhere to the
specification that they must have methods defined above. See
BarometricPressure.java for an example of how Orbacus ORB implements the
IDL union.
Sequences and arrays
When passing more than one element at a time through an OMG IDL interface,
you want to use an array or a sequence. When to use an array or a
sequence and the differences between them was discussed in the IDL article
two months ago (see Resources for a link). The topic we need to discuss
now is how IDL sequences and arrays are mapped to the Java programming
language. The mapping is similar -- with some subtle differences -- but
it is quite tidy. So let's look at a code snippet from MotherIDL that contains the
definition of a couple of arrays and a couple of sequences: Listing 12. Arrays
// array examples
typedef char MyString[105];
struct ofArrays {
long shares[1000];
string spreadsheet[100][100];
};
// bounded and unbounded sequence examples
typedef sequence<long> Unbounded;
typedef sequence<long, 31> Bounded;
interface testbound {
void testStub(in Bounded inB, out Bounded outB);
};
|
In this example IDL, we are creating a struct of arrays, the first being
one dimensional, the second, two dimensional, and the third, a single
solitary array MyString[]. Next, two sequences are created: one unbounded, the other bounded.
Remember from the IDL column that arrays cannot be unbounded but sequences
can. Lastly we put the sequences into the method of an interface.
Arrays
The mapping of the arrays is rather simple. No java classes were produced
for either shares[] or spreadsheet[]. However, a java class was produced
for the struct ofArrays. Let's look at ofArrays.java: Listing 13. ofArrays.java
final public class ofArrays implements
org.omg.CORBA.portable.IDLEntity
{
public
ofArrays()
{
}
public
ofArrays(int[] _ob_a0,
String[][] _ob_a1)
{
shares = _ob_a0;
spreadsheet = _ob_a1;
}
public int[] shares;
public String[][] spreadsheet;
}
|
The IDL arrays are mapped straight away to Java arrays with the same name
as the IDL array identifier. No Java programming language classes were
needed to support the array mechanics. I qualified that with mechanics
because ofArray produced three classes: ofArrays.java,
ofArraysHelper.java, and ofArraysHolder.java, while MyString produced two:
MyStringHolder.java and MyStringHelper.java. Last month's column (part one
of this two-part series, see Resources for a link) looked into the holder
class that is produced, and shortly we will look at the helper in detail.
But for now, just notice that the arrays in the IDL are bounded and
the arrays in ofArrays.java have no bounds. So where do you think the
array bounds are being checked? That's right, in the helper class of the
struct -- ofArraysHelper.java and in MyStringHelper.java. Any time the
array (or struct) is marshaled for use as a parameter in an interface
method, its bounds are checked and a CORBA::MARSHAL exception is thrown
from within the Helper class' write() method if they are out of bounds.
 |
Sequences
The sequences also map quite cleanly to a Java array. The identifier for
the sequence is used as the name of the Java array. In the mapping,
everywhere the sequence type is needed (for example, as a method parameter), an
array of the mapped type of the sequence element is used, just as we saw
in the IDL array example. Bounds checking is done on bounded sequences
when they are marshaled as parameters to IDL operations, and an IDL
CORBA::MARSHAL is raised if the array goes out of bounds. The following
code is the interface operations class for the testbound interface which
uses the two sequences in its testStub() method: Listing 14. testboundOperations interface
public interface testboundOperations
{
public void
testStub(int[] inB,
BoundedHolder outB);
}
|
Exceptions
IDL exceptions map so cleanly to the Java programming language exceptions
that you would think they looked them over before they designed them.
It's such a pure and unaltered mapping it makes using exceptions easy. So
use them! You should notice that the IDL exceptions mapping is very similar to
structs. They are mapped to a Java class that provides instance variables
for the fields of the exception, as well as constructors. CORBA system
exceptions inherit from java.lang.RuntimeException. User-defined
exceptions inherit from java.lang.Exception through
org.omg.CORBA.UserException which extends IDLEntity.
User Exceptions
In our example a user exception is created named OilSpill: Listing 15. User exception OilSpill
// user defined exception
exception OilSpill {
long leakage;
};
|
When run through the IDL-to-Java compiler an OilSpill class is generated: Listing 16. Class OilSpill
final public class OilSpill extends org.omg.CORBA.UserException
{
public
OilSpill()
{
super(OilSpillHelper.id());
}
public
OilSpill(int _ob_a0)
{
super(OilSpillHelper.id());
leakage = _ob_a0;
}
public
OilSpill(String _ob_reason,
int _ob_a0)
{
super(OilSpillHelper.id() + " " + _ob_reason);
leakage = _ob_a0;
}
public int leakage;
}
|
OilSpill maps to a Java class that is designated final and extends
org.omg.CORBA.UserException, has a public int named leakage and has three
constructors: a default constructor, a normal or full constructor and an
"extra full" constructor. The additional "extra full" constructor has an
additional initial string reason parameter, which is concatenated to the
id before calling the base UserException constructor. Also generated are
the ubiquitous Helper and Holder classes.
System exceptions
The CORBA specification comes with a whole host of standard IDL system
exceptions. After spending any time working with CORBA, you will
become intimately familiar with certain system exceptions. System
exceptions are mapped to final classes that extend
org.omg.CORBA.SystemException. The Java class name for each standard IDL
exception is the same as its IDL name and is declared to be in the
org.omg.CORBA package. This class includes the IDL major and minor
exception code, as well as a string describing the reason for the
exception. The default constructor supplies 0 for the minor code,
COMPLETED_NO for the completion code, and "" for the reason string. There
is also a constructor that takes the reason and uses defaults for the
other fields, as well as one that requires all three parameters to be
specified. There are no public constructors for
org.omg.CORBA.SystemException; only classes that extend it can be
instantiated.
Any
As we discussed in CORBA Junction a couple of months ago, the CORBA Any is a self-describing data structure that maintains its type.
Any lets you extract and insert values of predefined IDL types at run time using type-safe conversion functions. You get an Any by calling the create_any() method on an ORB. The Any is quite useful and it is a very different type, like a super type. So how do you map this thing to the Java language? The IDL type Any comes with its own "wash and wear" mapped Java class -- org.omg.CORBA.Any -- already available in the standard CORBA library. It is a rather beefy class that includes methods to insert and extract instances of predefined types. The Any class also comes with a pair of generic streamable methods to handle non-primitive IDL types. Earlier, we looked at creating new types using the interface keyword or the struct keyword. These types are non-primitive or user-defined types, and the Any type must be capable of handling these types as well. To do this you call the create_input_stream() method to create a stream containing the Any 's value and then the method read_value() is used to read anAny 's value from the input stream. To go in the other direction, to an empty output stream, you invoke create_out_stream() and use
write_value() to write anAny 's value to that output stream. But what really makes all this work are those Helper files we have been talking about all along. These Helper files were produced for all of the previous types. They are everywhere. Just look in the directories generated by your IDL-to-Java compiler. Each user-defined IDL type generates a Helper class that includes a pair of methods to convert it to and from anAny. The combination of these generated Helper classes and the Any class provided with the CORBA library is quite a flexible mechanism. So let's take a closer look at the Helper class.
 |
Helpers
It should be no surprise to you by now that there is a great deal to
CORBA. If the OMG mappers had tried to put everything into one generated
file, it would have been too bloated and cumbersome. Therefore, they
created the Helper class, which is a class that has the name of the type
(or interface) with "Helper" appended to the name. The type "My" would
then have a generated helper class named "MyHelper." The Helper classes that are provided or that are generated contain methods that let you manipulate IDL types in various ways. The Helper class
provides static methods that clients can use to manipulate the type. These include the Any insert and extract methods described above, getting the repository ID, getting the typecode, and reading and writing the type from or to a stream. For a mapped IDL interface, the Helper class will also contain a narrow() operation that is used to cast an object reference of org.omg.CORBA.Object to the base type of the helper.
Conclusion
At this point it should be clear that getting a firm and complete hold of
CORBA requires a solid understanding of the programming language or
languages that you will be using to implement CORBA components. Over the
past two columns we have looked at the IDL-to-Java mapping but there are
several other mappings as well -- C, C++, COBOL, ADA, and Smalltalk just
to name several (which isn't even to mention Java-to-IDL mapping, which is
rather new, and somewhat the opposite of what we covered here because it gives
RMI developers the ability to make their interfaces available to CORBA
clients). I am sure I missed several more and there are always
initiatives going on at the OMG to add more languages (Python and C#, for
example). The mapping is important to understanding how CORBA
interweaves itself into your system and its components. Success in using CORBA with the Java language requires thorough understanding of
the IDL-to-Java mapping; but the extra effort you put into having a
complete understanding of this mapping will pay off enormously in your
projects and efforts with CORBA.
Resources
About the author  | 
|  |
Dave Bartlett lives in Berwyn, Pennsylvania, consulting, writing and teaching. He is the author of Hands-On CORBA with Java, a 5-day
course presented via public sessions or in-house to organizations.
Presently, Dave is working to turn the course material into the book
Thinking in CORBA with Java. Dave has Masters degrees in
Engineering and Business from Penn State. If you have questions or are
interested in a specific topic, you can contact Dave at dbartlett@pobox.com. |
Rate this page
|