JavaSoft, a Sun Microsystems Business


Mapping IDL to Java

22nd May 1996 (alpha2.0)

Copyright 1996, Sun Microsystems Inc. All rights reserved.


Table of Contents


Introduction

Purpose of this document

This document is intended to enable an early review by interested parties of the design of the IDL Java mapping. Please send comments to:

idl-java@wombat.eng.sun.com

Please note that some of the material in this document is likely to change as a result of further prototyping work and possibly as a result of feedback. This version of the mapping is fully consistent with the Java Developer's Kit 1.0.

The document provides the design rationale for the proposed mapping from IDL to Java. The handling of dynamic types (Any, typecodes, DII, and DSI) remain to be added.

Assumptions

The reader is assumed to be familiar with OMG IDL (for which, see ``The Common Object Request Broker: Architecture and Specification'', Revision 2.0, July 1995) and the Java Language (for which, see ftp://ftp.javasoft.com/docs/javaspec.ps.tar.Z until something better shows up).

Goals of mapping

Among the goals for this mapping was that it be ``natural'' for Java programmers. Alternatives included slavishly following the style used by, for example, the mapping from IDL to C++. Where we thought we could do things better in this mapping, or in Java, we did what we thought Java programmers would appreciate.

Keep in mind that the Java language is still undergoing some change as it moves into its final released form, and we will revise this mapping to match (and exploit) changes in the Java language.

Plan of the document

Each section describes an IDL feature and describes how it is mapped to Java. Each section includes an English description of the mapping, an example of the feature in IDL and as it is mapped into Java, and usually some rationale for our choice of mapping. Occasionally there are implementation limitations, or usage suggestions for the mapping. These will also be described. We reserve the right to remove limitations in the future. Where alternatives were considered, those will also be described in an attempt to provoke discussion of possible alternative mappings, and to provide design history so decisions are not revisited unintentionally. Except as noted, the examples of the mapping are taken directly from the output of our IDL-to-Java compiler, so they are faithful as of the date of the document. Where the details of the generated code are unimportant, they will be elided with the symbol ``....''.


Modules

Each IDL module is mapped to a Java package. The Java package has the same name as the IDL module. For all IDL types within the IDL module that are mapped to Java classes or Java interfaces, the corresponding Java class or Java interface are declared inside the generated Java package.

Example

Anticipating the mapping of IDL enums:

/* From Example.idl: */
module Example {
	enum EnumType { .... };	 
};
// Generated to Example/EnumType.java:
package Example;
public class EnumType {
	....
}

Rationale

IDL modules are a name-scoping mechanism. The corresponding name-scoping mechanism in Java is the Java package.

To cover the case where IDL declarations are not within an IDL module, the mapping uses a Java package to hold those declarations. The Java package is named ``idlGlobal''. Declarations are added to this Java package for any IDL declarations that are not in an IDL module. It is possible to over-write the mapping of one IDL declaration (from one compilation unit) with the mapping for another IDL declaration of the same name (from another compilation unit). Detecting such name collisions is left to the IDL development system.

An alternative mapping tries to map IDL declarations that are not in an IDL module to a Java package named for the compilation unit. This is at least as awkward as mapping all such IDL declarations to a single Java package, and is harder to explain to people and more difficult to remember and use. The single Java package mimics the collisions that happen because two IDL declarations have the same name.

One could say it was a bug in IDL that the unnamed compilation-unit scope is ``reopenable'' in each IDL file, whereas all other scopes must have exactly one declaration point. (That excuse goes away when we implement reopenable IDL modules.)

People should be encouraged to put IDL declarations inside of IDL modules.

All the IDL examples shown will be from the IDL module Example. Therefore, the generated Java declarations will all be inside the Java package Example.


Const Values

IDL constant values are mapped to public static final instances in Java. The name of the IDL const is used as the name of a Java class, and the value is available as the instance variable ``value''.

Example

Anticipating that an IDL long is mapped to a Java int (see below)

/* From Example.idl: */
module Example {
	const long aLong = -12345;	 
};
// Generated to Example/aLong.java:
package Example;
public final class aLong {
    public static final int value = (int) (-12345L);
}

A client of Example::aLong would reference the value as

Example.aLong.value

Rationale

At least three Java name components are needed to refer to a constant value. The constant itself must be an Java instance in a Java class, and because the Java language makes it awkward to refer to unpackaged Java classes from within packaged Java classes, the class for the mapping should be in a Java package. Since IDL constants can appear with fewer than three IDL name components, some mapping that adds name components is needed, at least some of the time. Rather than have different rules for different IDL constants, we adopted the simple mapping show above, which, with the mapping for IDL declarations that are not in IDL modules guarantees at least three components to the mapped constant.

A possible objection to the simple mapping is that it generates a Java class for each IDL const. The alternative is to generate one Java class that holds all the IDL consts for a given IDL scope. On the one hand, we would like to minimize the number of Java classes the mapping generates. On the other hand, our mapping to a public static final int allows the value to be inlined into any Java uses of the IDL const, so the class file for the IDL const need not be loaded at run time. The disadvantage of using one Java class for all the IDL consts in a scope is that the Java class for IDL consts that are not in IDL modules cannot be extended as more IDL compilation units are mapped to Java.


Basic Types

This section describes the mapping of the IDL basic types: boolean, char, octet, string, and the integer and floating point types.

Boolean

The IDL type boolean is mapped to the Java type boolean. The IDL constants TRUE and FALSE are mapped to the Java constants true and false.

Example

/* From Example.idl: */
module Example {
	const boolean truth = TRUE;
};
// Generated to Example/truth.java:
package Example;
public final class truth {
    public static final boolean value = (true);
}

Char

The IDL type char is mapped to the Java type char.

Example

/* From Example.idl: */
module Example {
	const char aChar = 'A';
};
// Generated to Example/aChar.java:
package Example;
public final class aChar {
    public static final char value = ('A');
}

IDL chars come from the ISO 8859.1 character set, having at most 255 different values. Java chars come from the Unicode character set, having at most 65536 different values. IDL characters occupy a small corner of the Java character space. During marshaling Java chars are checked to make sure they are in the ISO 8859.1 subset of Unicode. If a Java char is found to be outside the range of ISO 8859.1, an omg.corba.CharacterRangeException is thrown.

Rationale

The fact that Java chars are at least as wide as IDL chars meets the needs of an IDL mapping for chars. The unfortunate part is that Java programs can form Java chars that are outside the range of IDL chars, necessitating a range check during marshaling.

According to the IDL specification, we should map the IDL characters in a way that expresses the meaning of the IDL characters in Java characters but preserves the round-trip values of those characters (e.g. the characters as defined in Tables 2, 3, 4, and 5 of the CORBA 2.0 specification Section 3.2, see Section 3.8.1), but doing anything exotic in the way of translation seems likely to break things.

Octet

The IDL type octet is mapped to the Java type byte.

Example

/* From Example.idl: */
module Example {
	const octet anOctet = 42;
};
// Generated to Example/anOctet.java:
package Example;
public final class anOctet {
    public static final byte value = (byte) (42L);
}

String

The IDL string type is mapped to the Java type java.lang.String. Recall that java.lang.Strings contain Unicode characters, which are wider than IDL chars. When marshaled, the characters of the IDL string are checked to make sure they are in the ISO 8859.1 subset of Unicode, and an omg.corba.CharacterRangeException exception may be thrown.

Example

/* From Example.idl: */
module Example {
	const string aString = "Hello world!";
};
// Generated to Example/aString.java:
package Example;
public final class aString {
    public static final String value = ("Hello world!");
}

Since java.lang.Strings are unbounded, IDL bounded strings have their length checked when marshaling and unmarshaling as parameters. However, their length is not checked at other times. You can check the length of the java.lang.String instance at any time. (If you want the bound of a bounded IDL string to be available at run time, declare an IDL constant with the bound and use that IDL constant as the bound of the bounded IDL string. The IDL constant will be available in Java through its mapping.)

Rationale

Both bounded and unbounded IDL strings are mapped to java.lang.Strings, which are unbounded (but immutable). We could have mapped IDL strings to a class that implemented a sequence of characters, but decided it would be more ``natural'' for Java programmers to use java.lang.Strings. That allows Java programmers to use double-quoted java.lang.String constants and the rich set of operations on java.lang.Strings.

Integers

IDL includes six integer data types: 16-bit, 32-bit, and 64-bit sizes, in both signed and unsigned varieties. Java has three integral types: short, int, and long. The IDL integer data types are mapped to the correspondingly-sized Java integer data types.

Example

/* From Example.idl: */
module Example {
	const short aShort = -1;
	const unsigned short anUnsignedShort = 15907;
	const long aLong = -12345;
	const unsigned long anUnsignedLong = 901008;
	const long long aLongLong = -1234567890;
	const unsigned long long anUnsignedLongLong = 987654321;
};
// Generated to Example/aShort.java:
package Example;
public final class aShort {
    public static final short value = (short) (-1L);
}
// Generated to Example/anUnsignedShort.java:
package Example;
public final class anUnsignedShort {
    public static final short value = (short) (15907L);
}
// Generated to Example/aLong.java:
package Example;
public final class aLong {
    public static final int value = (int) (-12345L);
}
// Generated to Example/anUnsignedLong.java:
package Example;
public final class anUnsignedLong {
    public static final int value = (int) (901008L);
}
// Generated to Example/aLongLong.java:
package Example;
public final class aLongLong {
    public static final long value = (long) (-1234567890L);
}
// Generated to Example/anUnsignedLongLong.java:
package Example;
public final class anUnsignedLongLong {
    public static final long value = (long) (987654321L);
}

Rationale

Mapping the IDL signed integer types to the corresponding Java integer types is uncontroversial.

The lack of unsigned integer types in Java forces a choice. Either we can map the unsigned IDL integer types to the next larger Java integer type to try to preserve the magnitude of the integer, or we can map IDL unsigned types to the correspondingly-sized Java integer types and leave the user to figure out that negative signed values are actually large unsigned values, where appropriate.

Mapping up to the next larger size doesn't extend to mapping the IDL unsigned long long type, since Java doesn't have an integer type larger than 64 bits. It would also be undesirable to have the IDL unsigned integer types map to differently-sized Java integer types than the corresponding IDL signed integer types. If IDL integers are mapped to the corresponding Java integer types, there is a potential loss of specification, in that negative values can be stored in what should be IDL unsigned integers. If IDL integers are mapped to larger Java integer types there is a potential loss of specification in that numbers outside the range of an IDL unsigned integer type could be stored in the larger Java integer type. In the end we decided that 32-bit quantities should occupy 32 bits, and so on. This is the ``natural'' mapping for Java programmers.

The major difference between signed and unsigned integers in programs comes when making comparisons (also when computing a modulus). If necessary, we should encourage the Java language run time libraries to provide unsigned methods for java.lang.Integer and java.lang.Long. If not, we can provide those methods ourselves.

Floating Points

IDL floating point types map to the corresponding Java floating point types. Both languages use ANSI/IEEE 754-1985 floating point types, so this decision is uncontroversial.

Example

/* From Example.idl: */
module Example {
	const float aFloat = 2.71828;
	const double aDouble = 3.14159265358979323846;
};
// Generated to Example/aFloat.java:
package Example;
public final class aFloat {
    public static final float value = (float) (2.71828D);
}
// Generated to Example/aDouble.java:
package Example;
public final class aDouble {
    public static final double value = (double) (3.14159265358979323846D);
}

Constructed Types

This section describes the mapping of IDL constructed types: enum, struct, union, sequence, and array. Sequences and arrays are mapped to Java arrays. Each of the other IDL constructed types is mapped to a Java class that implements the semantics of the IDL type. The name of the generated Java class is the name of the IDL type.

Enum

Each IDL enum type is mapped to a Java class that defines a Java static final int with the value of each enum member, and a method to check that a Java int is in the range defined by the IDL enum. The name of the class is the name of the IDL enum type.

Example

/* From Example.idl: */
module Example {
	enum EnumType { none, first, second, third, fourth };
};
// Generated to Example/EnumType.java:
package Example;
public class EnumType {
    public static final int none = 0,
			    first = 1,
			    second = 2,
			    third = 3,
			    fourth = 4;
    public static final int narrow(int i) 
	throws omg.corba.EnumerationRangeException { .... }
}

The narrow method either returns the argument value or throws an exception, so it can be used inline in expressions to verify that Java int's are in the range of the IDL enum. The exception omg.corba.EnumerationRangeException extends omg.corba.RuntimeException, which extends java.lang.RuntimeException, so the exception need not be declared in any methods that call the narrow method.

Rationale

The purpose of an enumeration is to indicate a choice from within a set of alternatives. As such, the obvious mapping is to a range of Java int's and the selection among alternatives is made with a Java switch statement.

An alternative mapping makes the IDL enum a Java class, with the members of the IDL enum represented by run time instances of the Java class. The Java class has a method that reveals the position of each Java instance in the IDL enum. (A private Java constructor prevents any additional instances from being created.) The problem with this mapping is that the instances cannot be used in the most obvious application of enum members: as case labels in Java switch statements. Instead, to select among the alternatives, one would invoke the method to determine the rank in the enumeration of the instance in hand, and transfer through a switch statement whose labels were the various ranks. Having names for the ranks that can be used to label switch cases seems like a more natural mapping.

Another alternative mapping makes the IDL enum an abstract Java class, with one method: int getValue(). Then each member of the IDL enum is mapped to a member Java class that extends the abstract Java class, providing the implementation of the getValue method (to return the position of the member in the enum). Clients can declare variables of the enum Java class and assign to it instances of the member Java class and all the assignments are type checked. (This approach is like the immutable union approach, except it makes more sense here since enum members are immutable.) The member classes also provide a public static final int set to the position of the member. The public static final int value can be used to label switch statement cases. So everyone is happy. Well, almost everyone; because now we have a class for each enum member, which might be a lot of classes.

Struct

An IDL struct is mapped to a Java class that provides instance variables for the fields and a constructor from values (also a null constructor so the fields can be filled in later).

Example

/* From Example.idl: */
module Example {
	struct StructType {
		long these;
		string those;
	};
};
// Generated to Example/StructType.java:
package Example;
public final class StructType {
    //	instance variables
    public int these;
    public String those;
    //	constructors
    public StructType() { }
    public StructType(int __these, String __those) { .... }
}

Rationale

We could have implemented methods on the mapped class to provide access to the instance variables (and made the instance variables private). That's more in the ``style'' of recent Java code. Leaving the instance variables exposed prevents us from interposing interesting accessor methods in the future without changing the mapping, but generates less code and allows faster access than if accessor methods were used. One should also remember that if you wanted accessor methods for fields one could declare an IDL interface with attributes. The purpose of IDL structs is to define data that will be directly accessible, so we left the fields exposed.

Union

An IDL union is mapped to a Java class that provides a default constructor, an accessor method for the discriminator, an accessor method for each of the branches, and a modifier method for each of the branches. The Java class is named by the name of the IDL union. The accessor method for a branch is named by the name of the branch with the prefix ``get''. The modifier method for a branch is named by the name of the branch with the prefix ``set''.

If an IDL union branch has a non-default case label, then a static method is generated to construct an instance from an instance of the branch type. The static constructor is named by the name of the branch with the prefix ``create''. In addition, a modifier method is generated that does not require a discriminator parameter. The single-argument modifier method is named by the name of the branch with the prefix ``set'' (this overloads the mandatory modifier method).

Example

/* From Example.idl: */
module Example {
	union UnionType switch (EnumType) {
	case first:	long Win;
	case second:	short Place;
	case third:	octet Show;
	case fourth:
	default:	boolean Other;
	};
};
// Generated to Example/UnionType.java:
package Example;
public class UnionType {
    //	constructor
    public UnionType() { .... }
    //	discriminator accessor
    public int discriminator()
	throws omg.corba.UnionDiscriminantException { .... }
    //	branch constructors and get and set accessors
    //	    Win
    public static UnionType createWin(int value) { .... }
    public int getWin()
	throws omg.corba.UnionDiscriminantException { .... }
    public void setWin(int value) { .... }
    public void setWin(int discriminator, int value) 
	throws omg.corba.UnionDiscriminantException { .... }
    //	    Place
    public static UnionType createPlace(short value) { .... }
    public short getPlace()
	throws omg.corba.UnionDiscriminantException { .... }
    public void setPlace(short value) { .... }
    public void setPlace(int discriminator, short value) 
	throws omg.corba.UnionDiscriminantException { .... }
    //	    Show
    public static UnionType createShow(byte value) { .... }
    public byte getShow() 
	throws omg.corba.UnionDiscriminantException { .... }
    public void setShow(byte value) { .... }
    public void setShow(int discriminator, byte value) 
	throws omg.corba.UnionDiscriminantException { .... }
    //	    Other
    public static UnionType createOther(boolean value) { .... }
    public boolean getOther() 
	throws omg.corba.UnionDiscriminantException { .... }
    public void setOther(boolean value) { .... }
    public void setOther(int discriminator, boolean value) 
	throws omg.corba.UnionDiscriminantException { .... }
}

The default constructor leaves the union in an uninitialized state. Invoking accessor methods, including the discriminator accessor, on uninitialized unions will throw an omg.corba.UnionDiscriminantException. That exception is an omg.corba.RuntimeException, and so need not be declared in methods that call any of the methods on a mapped union.

The branch constructors initialize the discriminator to an unspecified member of the case labels for the branch. If a branch has only a single non-default case label, then that label is used as the implicit discriminator.

The value for the current branch can be accessed by calling the accessor method for the current branch. Attempts to call an accessor for other than the current branch will throw omg.corba.UnionDiscriminantException.

A union can be initialized (or changed after initialization) by calling one of the branch modifier methods. Branch modifier methods can come in two forms. If the branch has a non-default label, then a single-argument modifier method is generated that supplies a discriminator that is appropriate for the branch, and sets the branch to the single argument. In any case, a two argument modifier method is generated that takes both a discriminator value and a branch value. Attempts to supply a discriminator that is not valid for the branch will throw omg.corba.UnionDiscriminantException.

Note that the discriminator cannot be set without supplying an appropriate value for the branch. If one wants to change the discriminator without changing the branch value then one should pass the new discriminator and the current branch value to the branch modifier method. If one wants to change the branch value without changing the discriminator then one should pass the current discriminator and the new branch value to the branch modifier method.

Rationale

IDL unions must be type-safe in Java, since Java is a type-safe language. Therefore there the correspondence between the discriminator and the branch value must be tightly coupled. This need to control the modification of the discriminator and the branch values rules out a mapping with exposed discriminator or branch instances, or separate discriminator and branch modifier methods.

An alternative mapping has the IDL union mapped to a Java class, and each of the branches mapped to a Java class that extends the Java class for the union. Once the branch of a union instance is set (by calling an extending Java class constructor), which branch the union instance represents is immutable. The accessor and modifier methods on the extending Java classes are as strongly typed as the accessors in the current mapping (except that they have simpler names, since the name of the branch is encoded in the name of the extending Java classes). The discrimination of the branches can be performed using the Java run time typing information. An advantage of the extending Java classes is that once the branch is determined it can be passed around as the extending Java class, avoiding some run time checks. A disadvantage of the extending Java classes is that in order to access or modify the value the instance must be cast to the appropriate extending Java class. Another disadvantage is that more Java classes are generated, though only up to the complexity of the user's IDL union declaration. The big difference between these approaches is whether a union instance can be changed from one branch to another.

Sequence

Each IDL sequence is mapped to a Java array. In the mapping, everywhere the sequence type is needed, an array of the mapped type of the element is used. Bounded sequences have their bounds checked when they are marshaled as parameters to IDL operations.

Example

Recalling the mapping for IDL structs:

/* From Example.idl: */
module Example {
	struct SequenceContainer {
		sequence< StructType > unbounded;
		sequence< StructType, 42 > bounded;
	};
};
// Generated to Example/SequenceContainer.java:
package Example;
public final class SequenceContainer {
    //	instance variables
    public Example.StructType[] unbounded;
    public Example.StructType[] bounded;
    //	constructors
    public SequenceContainer() { }
    public SequenceContainer(Example.StructType[] __unbounded, 
			     Example.StructType[] __bounded) { .... }
}

Rationale

The mapping to Java arrays allows the natural Java subscripting operator to be applied to mapped IDL sequences. Java arrays can be resized by allocating a new array instance. This is convenient for unbounded sequences, but awkward for bounded sequences. The bounds on bounded sequences are checked when the sequence is passed as a parameter to an IDL operation, but not otherwise. The alternative is to map bounded sequences to a class with a subscript method (not "[]"s) that would provide access to a private instance array of elements. The instance would be private to prevent it from being resized (i.e., reallocated with a different size). The choice of having different mappings for bounded and unbounded sequences, and losing the subscripting operator for bounded sequences, seems worse than only checking the bounds of the bounded arrays as they crosses an IDL boundary.

Array

An IDL array is mapped the same way as an IDL bounded sequence is mapped. This mapping allows the natural Java subscripting operator to be applied to the mapped array. The bounds for the array are checked when the array is marshaled as an argument to an IDL operation. If you want the length of the array to be available in Java, bound the array with an IDL constant, which will be available through its mapping.

Example

/* From Example.idl: */
module Example {
	const long ArrayBound = 42;
	struct ArrayContainer {
		long	array[ArrayBound];
	};
};
// Generated to Example/ArrayContainer.java:
package Example;
public final class ArrayContainer {
    //	instance variables
    public int[] array;
    //	constructors
    public ArrayContainer() { }
    public ArrayContainer(int[] __array) { .... }
}

Exceptions

IDL user-defined exceptions are mapped essentially as IDL structs are mapped. A Java class is generated that provides instance variables for the fields of the exception, and a constructor for the exception from the fields (and a default constructor). All IDL user-defined exceptions extend the Java class omg.corba.UserException (which in turn extends omg.corba.CORBAException, which in turn extends java.lang.Exception), which makes it possible to catch specific user-defined exceptions, or to catch all user-defined exceptions by catching omg.corba.UserException, or to catch all user-defined exceptions and system exceptions by catching omg.corba.CORBAException.

Example

/* From Example.idl: */
module Example {
	exception That {
		string reason;
	};
};
// Generated to Example/That.java:
package Example;
public class That
	extends omg.corba.UserException {
    //	instance variables
    public String reason;
    //	constructors
    public That() { .... }
    public That(String __reason) { .... }
}

Interfaces

This section describes the mapping for IDL interfaces. The description comes in several parts. First there's a discussion of interfaces versus object references. Next is a discussion of inheritance. Then comes the mapping for operations on interfaces. Last comes the mapping for parameter passing modes.

If this were an OMG document we would have to have some mealy-mouthed phrases about how any implementation has to support operations as if they were implemented by the examples below. This is the JavaSoft mapping from IDL to Java, and this is the document that explains our mapping, not a mapping specification.

Interfaces and Object References

In IDL, users can define interfaces to objects. In the mapping to any particular language, clients operate on references to objects, via object references that are made available in the mapping language. In the mapping to Java, each user-defined IDL interface maps to two Java interfaces and a Java class.

One Java interface gives the signatures of the operations directly defined in the IDL interface. That Java interface will be referred to as the operations Java interface. The other Java interface describes any IDL interfaces that are inherited by this IDL interface. That Java interface will be referred to as the object reference Java interface. The object reference Java interface extends the operations from the IDL interface with some additional methods that are available on the object reference. Java instances that support the Java object reference interface support all the operations on the user-defined IDL interface, and the usual methods on object references.

The Java class is the client-side stub for the IDL interface. That Java class implements the IDL operations and the object reference methods and provides methods for object reference creation and object reference type checking. The Java class will be referred to as the stub Java class.

Methods on the object reference, e.g. operations from the IDL interface or general methods on IDL objects, are invoked on instances that support the object reference Java interface, while methods that operate on the client-side representation, e.g. object reference creation and type-checking, are invoked on the stub Java class.

In case you hadn't noticed, this section is going to be full of the words ``interface'' and ``class''. I'll try to be careful to use the following phrases to keep things as clear as they can be. I'll use ``the IDL interface'' to mean the IDL interface that is written by the user. I'll use ``the operations Java interface'' to mean the Java interface that is generated to encapsulate the IDL operations that are available on the IDL interface. I'll use ``the object reference Java interface'' to mean the Java interface that gives the signatures of the operations from the IDL interface and its inherited IDL interfaces. I'll use ``the stub Java class'' to mean the Java class that is implementation of the IDL interface. (Just as an aside, I always use ``operation'' to mean IDL operations, and ``method'' to mean Java methods.)

Object Reference Interface Versus Stub Class

This section explains the mapping from the user-defined IDL interface to the object reference Java interface and the stub Java class. The point of this section is to demonstrate how the operations from the IDL interface are kept separate from the other Java methods that are needed on IDL object references. However, this distinction won't be that important until we get to inheritance. (It's also an important distinction when we explain the server-side mapping.)

Example

/* From Example.idl: */
module Example {
	interface Face {
		void method();
	};
};
Object reference Java interface

First we show the generated object reference Java interface, which is named by the name of the IDL interface, with the suffix ``Ref''.

// Generated to Example/FaceRef.java:
package Example;
public interface FaceRef
    extends omg.corba.ObjectRef,
	    Example.FaceOperations {
}

The object reference Java interface extends the omg.corba.ObjectRef object reference Java interface, to get the declarations of the methods available on all IDL object references. It also extends the Example.FaceOperations interface, which gives the signatures of the operations defined directly in the IDL interface. (That interface is not shown because this section describes just the object reference Java interface and the stub Java class.)

Stub Java class

Next we show the stub Java class for the same example IDL interface. The stub Java class is named with the name of the IDL interface, with the suffix ``Stub''.

Example

/* From Example.idl: */
module Example {
	interface Face { };		 
};
// Generated to Example/FaceStub.java:
package Example;
public class FaceStub
	extends omg.corba.ObjectImpl
    	implements Example.FaceRef { 
    //	IDL operations
    //	    Implementation of ::Example::Face::method
    public void method() throws omg.corba.SystemException { .... }
    //	Type-specific CORBA::Object operations
    public omg.corba.ObjectRef duplicate() { .... }
    public static boolean isA(omg.corba.ObjectRef that)
	    throws omg.corba.SystemException { .... }
    public static Example.FaceRef narrow(omg.corba.ObjectRef that)
	    throws omg.corba.NarrowCoercionException, 
		   omg.corba.SystemException { .... }
    public static Example.FaceRef duplicate(Example.FaceRef that) { .... }
}

The stub Java class implements the operations defined on (or inherited into) the IDL interface and any type-specific object reference methods. Some of the object reference methods are inherited from omg.corba.ObjectRef, and some are defined by each stub Java class. See Methods on CORBA::Object for details on what methods are available on all IDL object references.

Some of the object reference methods appear in two forms: a non-static method that operates on the object reference on which the method is invoked, and a static method that operates on an object reference passed as a parameter. The advantage of the static methods is that they can return a properly typed object reference. The advantage of the non-static methods is that they are more conveniently invoked on object reference instances.

The static duplicate method takes a typed IDL object reference as a parameter and returns a typed IDL object reference as a result. The non-static duplicate method duplicates the IDL object reference and returns it widened to a omg.corba.ObjectRef (so this non-static method can be overridden in each stub Java class). The result can be cast (using the Java cast syntax) to the type of the original object. (The corresponding non-static method to release an IDL object reference (release) is available on omg.corba.ObjectRef.)

The static isA method tests whether the IDL object reference supplied as a parameter supports the IDL interface on whose stub Java class the isA method is called. (The corresponding non-static method to check whether an IDL object reference supports a particular IDL interface is available on omg.corba.ObjectRef.)

The static narrow method takes an IDL object reference and returns a duplicate of that object reference typed as the IDL object reference on whose stub Java class the narrow method is called.

The stub Java class has a private constructor, so clients can not create new IDL object references. A Java null can be passed anywhere a null object reference is needed.

Rationale

The obvious name for the stub Java class is the unadorned name of the IDL interface. (One could argue that the unadorned name could instead be used for the object reference Java interface.) But because the scoping mechanism in Java is the Java package, and because IDL allows IDL declarations within IDL interface declarations, and because a Java class may not have the same name as the prefix of a Java package, the unadorned name is used for the Java package that contains any mappings of declarations within the IDL interface, and the stub Java class (and the object reference Java interface) have suffixes. This approach is justified because almost all Java identifiers are written with at least some package qualifier, and the hope is to minimize the number of suffixed that are needed.

If Java has contravariant return types, then the object-reference returning object reference methods could be declared on the object reference Java interface (and over-ridden by each extending object reference Java interface, and implemented by each implementing stub Java class). That would allow a user to say, for example:

Example.FaceRef original = ....;
Example.FaceRef copy = original.duplicate();	// contravariant method

But since Java does not have contravariant return types, the type-specific object reference methods must either return a common type, or be static methods taking the object reference as a parameter. In reality, one must either cast the generic return, as in:

Example.FaceRef original = ....;
Example.FaceRef copy = (Example.FaceRef) original.duplicate();
or avoid the Java cast by calling the static method, as in:
Example.FaceRef original = ....;
Example.FaceRef copy = Example.FaceStub.duplicate(original);

Since Java static methods can be invoked on instances as well as by giving the name of the Java class, one can also say:

Example.FaceRef original = ....;
Example.FaceRef copy = original.duplicate(original);

IDL Interface Inheritance

An IDL interface can inherit from other IDL interfaces, meaning that the operations defined on the inherited IDL interfaces are also defined on the inheriting IDL interface. (Such inheritance is interface inheritance. Nothing is implied about the inheritance of implementations of the inherited operations.) The inherited interfaces are called base interfaces, and the inheriting interface is called the derived interface.) The derived IDL interface can also define additional operations that are not available on any of its base interfaces.

An IDL interface may inherit from more than one base IDL interface (multiple inheritance), and an IDL interface may be inherited more than once in an inheritance lattice. References to derived interfaces may be used anywhere a reference to a base interface is needed (implicit widening). References that have been widened to base interfaces may be narrowed to any interface in the inheritance lattice of the object, at the cost of an explicit runtime check.

We have seen above the basic object reference Java interface for an IDL interface with no inheritance. To model IDL multiple interface inheritance in Java we use multiple object reference Java interfaces in which the object reference Java interfaces for the more derived IDL interfaces extend the object reference Java interfaces for the more base IDL interfaces.

Example

Here we show the standard diamond multiple inheritance lattice:

/* From Example.idl: */
module Example {
	interface Base { };
	interface Left: Base { };
	interface Right: Base { };
	interface Derived: Left, Right { };
};

or, graphically:

		Example::Base
                      /  \
                     /    \
                    /      \
                   /        \
                  /          \
	 Example::Left    Example::Right
                  \          /
                   \        /
                    \      /
                     \    /
                      \  /
		Example::Derived       

and its mapping to Java:

// Generated to Example/BaseRef.java:
package Example;
public interface BaseRef
    extends omg.corba.ObjectRef,
	    Example.BaseOperations {
}
// Generated to Example/LeftRef.java:
package Example;
public interface LeftRef
    extends omg.corba.ObjectRef,
	    Example.BaseRef,
	    Example.LeftOperations {
}
// Generated to Example/RightRef.java:
package Example;
public interface RightRef
    extends omg.corba.ObjectRef,
	    Example.BaseRef,
	    Example.RightOperations {
}
// Generated to Example/DerivedRef.java:
package Example;
public interface DerivedRef
    extends omg.corba.ObjectRef,
	    Example.LeftRef,
	    Example.RightRef,
	    Example.DerivedOperations {
}

One can see from the above four generated object reference Java interfaces how the IDL inheritance lattice is modeled in Java. Each object reference Java interface extends the object reference Java interfaces for any base IDL interfaces, and also extends the operations Java interface for the IDL interface.

Operations on IDL interfaces

Operations on IDL interfaces are mapped as operations declared on the operations Java interface, which is extended by the object reference Java interface, and implemented by the stub Java class. The operations Java interface is named with the name of the IDL interface and the suffix ``Operations''. The operations Java interface is not used directly by client code, so the name of the operations Java interface need not be known to users.

Example

/* From Example.idl: */
module Example {
	typedef .... ResultType;	 
	typedef .... ParameterType;	 
	exception That { .... };	 
	interface Service {
		ResultType operation(in ParameterType arg) raises (That);
	};
};
// Generated to Example/ServiceOperations.java:
package Example;
public interface ServiceOperations {
    Example.ResultType operation(Example.ParameterType arg)
        throws omg.corba.SystemException, Example.That;
}
// Generated to Example/ServiceRef.java:
package Example;
public interface ServiceRef
    extends omg.corba.ObjectRef,
	    Example.ServiceOperations {
}
// Generated to Example/ServiceStub.java:
package Example;
public class ServiceStub
	extends omg.corba.ObjectImpl
    	implements Example.ServiceRef { 
    //	IDL operations
    //	    Implementation of ::Example::Service::operation
    public Example.ResultType operation(Example.ParameterType arg)
        throws omg.corba.SystemException, Example.That {
    ....
}

The stub Java class implements the methods from the object reference Java interface. In the usual case, the stub Java class methods will be stubs for remote method invocations.

Rationale

The operations were separated from the object reference so that they could be inherited separately from the object reference inheritance. The collection of operations is re-used, for example, on the server-side to define the interface that a server must support. The mapping of operations is straightforward, except for the additional declaration that all IDL operations can throw omg.corba.SystemException in addition to any user-defined exceptions the IDL operation is declared to raise.

Parameter passing modes

IDL defines three parameter passing modes: in, out, and inout. The semantics of in is pass-by-value: the client supplies a value which is not changed by the server (nor can the client modify the value during the call). The semantics of out is pass-by-result: the server supplies a value which is not visible to the client until the invocation returns. The semantics of inout is pass-by-value-result: the client supplies an actual parameter which is changed by the server only when the invocation returns. Java has only pass-by-value semantics, which only matches what is needed for IDL in simple and object reference parameters and results. In the mapping, IDL in parameters are supplied by supplying the actual parameter to a call. Similarly, the results of IDL operations are returned as the results of the corresponding Java method.

For IDL out and inout parameters, some additional mechanism is necessary to support call-by-result and call-by-value-result. The mapping defines holder Java classes for all the IDL basic and user-defined types, and a client supplies an instance of the appropriate holder Java class that is passed (by value) for each IDL out or inout parameter. The contents of the holder instance (but not the instance itself) is modified by the invocation, and the client extracts the changed contents after the invocation returns. Each holder class has a constructor from an instance and a default constructor, and has a public instance member which is the typed value. The holder Java classes for the IDL basic types are available from the omg.corba Java package. For example, the omg.corba.LongHolder class is:

package omg.corba;
public class LongHolder
{
    //	Instance variable
    public int value;
    //	Constructors
    public LongHolder() {
	this(0);
    }
    public LongHolder(int initial) {
	value = initial;
    }
}

The IDL-to-Java compiler generates holder classes for all named user-defined types. Each generated holder Java class is named by the name of the IDL type for which it is the holder, with the suffix ``Holder''.

Example:

/* From Example.idl: */
module Example {
	typedef .... ResultType;	 
	typedef .... InType;		 
	typedef .... OutType;		 
	typedef .... InOutType;		 
	interface Modes {
		ResultType operation(in InType inArg, 
				     out OutType outArg, 
				     inout InOutType inoutArg);
	};
};
// Generated to Example/ModesOperations.java:
package Example;
public interface ModesOperations {
    Example.ResultType operation(Example.InType inArg,
                                 Example.OutTypeHolder outArg,
                                 Example.InOutTypeHolder inoutArg)
        throws omg.corba.SystemException;
}

From the above one can see that the result comes back as an ordinary result; that the in parameter needs only an ordinary value as the actual; but for the out and inout parameters, an appropriate holder must be constructed. A typical calling sequence for this operation might look like:


Example.ModesRef target = ....			// select a target object
Example.InType inArg = ....			// get the in actual
Example.OutTypeHolder outHolder =		// prepare to receive out
	new Example.OutTypeHolder();
Example.InOutTypeHolder inoutHolder = 		// set up in side of inout
	new Example.InOutTypeHolder(....);
Example.ResultType result = 			// make the invocation
	target.operation(inArg, outHolder, inoutHolder);
.... outHolder.value ....			// use value of outHolder
.... inoutHolder.value ....			// use value of inoutHolder

Before the invocation, the in side of the inout parameter must be put in the holder for the inout actual. The inout holder can be filled in either by constructing a new holder from a value, or by assigning to the value of an existing holder of the appropriate type. After the invocation, the client can use outHolder.value to access the value of the out parameter, and inoutHolder.value to access the out side of the inout parameter. The return result of the IDL operation is available as the result of the invocation.

Rationale

An alternative design was considered in which IDL operations with out (or inout) parameters would return instances of classes with public fields for the result of the operation and the out parameters. The nice thing about this so-called multi-valued return style was that there was some indication that multi-valued returns might eventually be added to the Java language itself, in which case multi-valued returns would be the ``natural'' mapping for out parameters. If the Java language had multi-valued returns (using a made-up aggregate assignment syntax ``{....} =''), the code could look like:

Example.ModesRef target = ....			// select a target object
Example.InType inArg = ....			// get the in actual
Example.OutType outArg;				// prepare to receive out
Example.InOutType inoutArg = ....		// set up in side of inout
Example.ResultType result;
{outArg, inoutArg, result} = target.operation(inArg, inoutArg);

Before the invocation the client would have to set up the actual parameters for the in parameters and the in sides of any inout parameters. The invocation returns multiple values, which the client assigns to the appropriate variables. Note the clean separation between the in parameters and the out parameters, especially the separation between the in and out sides of inout parameters.

The disadvantage of using the multi-valued return style when Java does not have multi-valued returns is the extra code needed to describe the classes for each multi-valued return type, and the added complexity when an IDL operation evolves from a single-valued return (an operation with a result, but no out parameters) to a multi-valued return (by the addition of out or inout parameters). Using multi-valued return classes, the above example would be:

Example.ModesRef target = ....			// select a target object
Example.InType inArg= ....			// get the in actual
Example.InOutType inoutArg = ....		// set up in side of inout
Example.ModesOperationMultiReturn result = 	// make the invocation
	target.operation(inArg, inoutArg);
.... result.value ....				// use result of operation
.... result.outArg ....				// use out parameter
.... result.inoutArg ....			// use out side of inout

Before the invocation, the client has to select the target object reference, and compute the in actual parameters and the in sides of inout parameters. The invocation returns an instance with instance variables for the return type of the IDL operation and for each of the out parameters (or the out sides of inout parameters). The client could then access result.value, result.outArg, and result.inoutArg to extract the returned values. Changing an IDL operation to add an out parameter would thus complicate the client code for all invocations, and require the construction of the result class instance and the extraction of the values (even the result of the operation) from the result class instance. Not to mention the formation of the client-visible name for the class of the return type of the operation, which has to be based on the name of the IDL interface, and the name of the IDL operation.

Attributes

IDL interfaces can have attributes, which are syntactic sugar for accessor and modifier operations for typed fields. IDL attributes can be designated read-only, in which case only the accessor operation is defined; otherwise both an accessor operation and a modifier operation are defined. In the mapping, the name of the accessor method is the name of the attribute prefixed by ``get''. The name of the modifier method is the name of the attribute prefixed by ``set''.

Example

/* From Example.idl: */
module Example {
	interface Attributes {
		attribute		long Assignable;
		readonly attribute	long Fetchable;
	};
};
// Generated to Example/AttributesOperations.java:
package Example;
public interface AttributesOperations {
    int getAssignable() throws omg.corba.SystemException;
    void setAssignable(int arg) throws omg.corba.SystemException;
    int getFetchable() throws omg.corba.SystemException;
}

The corresponding implementations of those methods appear in the stub Java class.


Server-side mapping

Implementors write services. They make those services available via object references. The mapping describes how an implementation instance is made into an IDL object reference, and how invocations against the implementation are delivered.

Being a servant

For each IDL interface the IDL-to-Java compiler generates an interface that defines the methods that any servant must implement to support the interface. This interface is called the servant Java interface. The servant Java interface is named by the name of the IDL interface with the suffix `Servant'. The servant Java interface is composed from servant Java interfaces of any inherited IDL interfaces and the operations defined directly by the IDL interface.

Example

Using the previously defined IDL interface Derived:

/* From Example.idl: */
module Example {
	interface Derived: Left, Right { }; 	 
}; 
// Generated to Example/DerivedServant.java:
package Example;
public interface DerivedServant
    extends Example.LeftServant,
	    Example.RightServant,
	    Example.DerivedOperations {
}

Here we can see the other use of the operations Java interface, to express the shared interface between the client-side object reference and the servant. The servant Java interface uses interface inheritance to inherit the methods required by any inherited IDL interfaces.

An implementation which wants to be a servant for an IDL interface declares itself to implement the servant Java interface. No other demands are made on an implementation. In particular, there is no requirement that an implementation extend any Java classes.

Rationale

The methods declared in the operations Java interface could instead be declared in both the object reference Java interface and the servant Java interface, saving one Java interface per IDL interface, but then the correspondence between the object reference and any servants would be obscured.

The operations Java interface could express the inheritance relationship among the corresponding IDL interfaces. If we did that, a derived operations Java interface would say it extended all the operation Java interfaces of directly inherited IDL interfaces, and added any operations defined in the derived IDL interface. That might let us get rid of the servant Java interface, which exists to declare the inheritance, but is also a place where we can inherit in any methods that are common to all servant (currently none). Having the inheritance in the operations Java interface might confuse people since we also express the IDL inheritance in the object reference Java interfaces on the client side so implicit widening between object references works.

From servant to object reference

An implementation is written to support the methods from the servant Java interface. In addition, the implementation may provide its own constructors, post-construction initialization methods, and so on.

In response to a request from a client, the service creates an instance of the implementation object, calling constructors with appropriate arguments, recording the instance in the state of the service, or whatever set-up the service requires. Finally, an object reference is created from the implementation instance that can be passed to clients.

The method to create an object reference from a servant instance is defined on the skeleton Java class generated for each IDL interface. The skeleton Java class is named from the name of the IDL interface and the suffix `Skeleton'. The skeleton Java class provides two static methods to create object references. The simpler method just takes the servant instance as a parameter and returns an object reference that uses a default object adaptor. The two-argument method takes an additional object adaptor parameter and creates the object reference using that object adaptor.

Example

Using the previously defined IDL interface Derived:

// Generated to Example/DerivedSkeleton.java:
package Example;
package Example;
public class DerivedSkeleton implements omg.orb.Skeleton {
    // Public methods to create references from servers
    public static Example.DerivedRef createRef(
	Example.DerivedServant servant) { .... }
    public static Example.DerivedRef createRef(
	omg.orb.Server server,
	Example.DerivedServant servant) { .... }

Rationale

An alternative server-side architecture has the implementation extending a servant Java class (not interface). Such an architecture has the advantage that then an implementation can inherit common implementations of methods from the skeleton Java class. The disadvantage is that Java allows each class to extend only a single other class, so this scarce resource is preempted for the implementation and can not be used by the implementation to inherit methods needed by the implementation.

Complex schemes were worked out to allow a servant to either inherit from a skeleton Java class or to allow a server writer to explicitly call an object reference creation method on the skeleton Java class (or both!). In the end we decided that a single mechanism for object reference creation was better than two mechanisms, and that explicitly turning an implementation into an object reference was better than using up the only available implementation inheritance.


Methods on CORBA::Object

The CORBA::Object type maps to the object reference Java interface omg.corba.ObjectRef and the stub Java class omg.corba.ObjectStub as shown below. The object reference Java interface omg.corba.ObjectRef is empty because CORBA::Object defines no ordinary methods, and inherits no IDL interfaces. The stub Java class omg.corba.ObjectStub implements the Java methods that manipulate CORBA::Object object references. The object reference Java interface for each user-defined IDL interface extends the object reference Java interface omg.corba.ObjectRef, so that any object reference Java interface can be passed anywhere an omg.corba.ObjectRef is expected.

package omg.corba;
public interface ObjectOperations { }

package omg.corba;
public interface ObjectRef {
    //	Public methods:
    //	    Check if this object supports the interface represented 
    //	    by the argument.
    boolean isA(String repositoryIdentifier)
	throws omg.corba.SystemException;
    //	    Create a new object reference from this object reference.
    //	    The duplicate method can be called on any object reference, 
    //	    and the result cast (or narrowed) to the type of the
    //	    original object reference.
    omg.corba.ObjectRef duplicate();
    //	    Indicate that resources for this object reference 
    //	    are no longer needed.
    void release();
}

package omg.corba;
public class ObjectStub
    extends omg.corba.ObjectImpl
    implements omg.corba.ObjectRef {
    //	Type-specific object reference methods
    public omg.corba.ObjectRef duplicate() { .... }
    public static boolean isA(omg.corba.ObjectRef that) 
	    throws omg.corba.SystemException { .... }
    public static omg.corba.ObjectRef narrow(omg.corba.ObjectRef that) {
	....
    }
    public static omg.corba.ObjectRef duplicate(omg.corba.ObjectRef that)
	    throws omg.corba.SystemException { .... }
}

The Java class omg.corba.ObjectImpl provides shared (that is, implementation inheritable) implementations fo some of the methods on object references:

package omg.corba;
abstract public class ObjectImpl {
    //	Shared implementations on type-neutral object reference methods.
    //	Non-static type-neutral object reference methods.
    public boolean isA(java.lang.String repositoryIdentifier)
	throws omg.corba.SystemException { .... }
    public void release() { .... }
    //	Static type-neutral object reference methods
    public static void release(omg.corba.ObjectRef that) { .... }
}

Standard Exceptions

The mappings for all IDL defined exceptions inherit from the Java class omg.corba.CORBAException. There are two Java subclasses of omg.corba.CORBAException, omg.corba.SystemException for standard system exceptions, and omg.corba.UserException for user-defined exceptions. This hierarchy allows one to catch exceptions by their category as well as by their particular type.

The class omg.corba.CORBAException might be called omg.corba.Exception, but there's currently a bug in the Java compiler that prevents us from defining a class named plain ``Exception''. And even if the bug did not exist, it would probably be confusing to have two types called ``Exception''.

The Java class omg.corba.CORBAException provides the following declarations:

//	From omg/corba/Exception.java:
package omg.corba;
public class CORBAException extends java.lang.Exception {
    // CORBA defined exception types.
    static public final int None = 0,
			    User = 1,
			    System = 2;
    // Constructor
    protected CORBAException(int type, String id) ....
    // Accessors
    public int exceptionType() ....
    public String exceptionId() ....
}

Note that the constructor is protected, so that only specific subclasses can create instances of omg.corba.CORBAException. The exceptionId method returns the repository identifier for the exception.

The standard system exceptions are mapped to Java classes that allow one to access the minor code and completion status for the exception. All standard system exceptions are subclasses of omg.corba.SystemException, shown below:

// From omg.corba.SystemException.java:
package omg.corba;
public class SystemException extends omg.corba.CORBAException {
    //	Completion status constants.
    static public final int CompletedYes = 0,
			    CompletedNo = 1,
			    CompletedMaybe = 2;
    //	Accessors
    public int getCompleted() { .... }
    public void setCompleted(int completed) { .... }
    public int getMinorCode() { .... }
    public void setMinorCode(int minorCode) { .... }
}

Note that there are no public constructors for omg.corba.SystemException; only classes that extend omg.corba.SystemException can be instantiated.

The standard IDL system exceptions are mapped to Java classes that extend omg.corba.SystemException. As an example, omg.corba.UnknownException is declared to include:

// From omg/corba/UnknownException.java:
package omg.corba;
public class UnknownException extends omg.corba.SystemException {
    public UnknownException() ....
    public UnknownException(int minor, int completed) ....
}

The default constructor supplies some default values for the minor code and completion status. The constructor from elements allows one to supply those fields (and supplies the appropriate exception category and exception identifier to the constructor for omg.corba.CORBAException. The standard system exceptions are mapped to the Java classes:

omg.corba.BadContextException
omg.corba.BadInvocationOrderException
omg.corba.BadOperationException
omg.corba.BadParameterException
omg.corba.BadTypeCodeException
omg.corba.CommunicationFailureException
omg.corba.DataConversionException
omg.corba.ImplementationLimitException
omg.corba.InterfaceRepositoryException
omg.corba.InternalException
omg.corba.InvalidFlagException
omg.corba.InvalidIdentiferException
omg.corba.InvalidObjRefException
omg.corba.MarshalingException
omg.corba.MemoryAllocationException
omg.corba.MemoryDeallocationException
omg.corba.NoImplementationException
omg.corba.NonexistentObjectException
omg.corba.NoPermissionException
omg.corba.NoResourcesException
omg.corba.NoResponseException
omg.corba.ObjectAdapterException
omg.corba.OrbInitializationException
omg.corba.PersistentStorageException
omg.corba.TransientException
omg.corba.UnknownException

Rationale

The system exceptions could have been mapped using the names declared in the corba module of the IDL specification. That would have resulted in names that were ``unnatural'' for Java programmers. Instead, we made up ``natural'' Java names for the system exceptions.

For the time being, the types shown as being in the omg package are in the sunw package. This will be fixed at some appropriate future time.

Copyright © 1996 Sun Microsystems, Inc., 2550 Garcia Ave., Mtn. View, CA 94043-1100 USA.
All rights reserved.

Talk with Java IDL users via the mailing list idl-users@java.sun.com.
To subscribe, send subscribe idl-users to listserv@java.sun.com.
Contact Java IDL support via idl-support@java.sun.com.

Send questions or comments about this site to webmaster@wombat.eng.sun.com.

 Java Powered