Skip to main content

skip to main content

developerWorks  >  SOA and Web services  >

IDL-to-Java mapping, Part 2

Using IDL mapping to create component interfaces

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


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.



Back to top


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.)



Back to top


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;
   }



Back to top


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).



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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); 
   }



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top