'; zhtm += ''; zhtm += '

' + pPage + ''; zhtm += '
'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Teach Yourself CORBA In 14 Days -- Ch 3 -- Mastering the Interface Definition Language (IDL)


Teach Yourself CORBA In 14 Days

Previous chapterNext chapterContents


Day 3
Mastering the Interface Definition Language (IDL)


Overview

On Day 2 you learned about the details of the CORBA architecture and attained an understanding of the various CORBA application components and their purposes. One chief component of the CORBA architecture, as you saw, is the use of the Interface Definition Language (IDL). IDL is used to describe the interfaces between CORBA objects. You also learned that IDL is neutral with respect to implementation language; in other words, IDL interfaces can be implemented in any language for which a language mapping exists, such as Java, C, C++, and a number of others.

Today you'll explore the various constructs of IDL and learn their uses. You'll start with the primitive data types, such as Booleans, floating point types, integer types, and characters and character strings, which you will find similar to data types found in most programming languages. You'll then move on to constructed types--the enumerated type, the structure type, the union type, and the interface type--which are simply types constructed from other types. Finally, you'll learn about advanced types, such as container types (sequences and arrays), exceptions, and others. By the end of the chapter you'll have covered virtually all there is to know about IDL.

IDL Ground Rules

Before you begin with IDL data types and other constructs, you'll want to cover a few ground rules of IDL syntax and other aspects of the IDL language. In particular, IDL has rules regarding case sensitivity, definition syntax, comment syntax, and C preprocessor usage.

Case Sensitivity

In IDL, identifiers (such as names of interfaces and operations) are case sensitive. In other words, an interface called myObject cannot be referred to later as myOBJECT. Besides these identifiers being case sensitive, IDL imposes another restriction: The names of identifiers in the same scope (for instance, two interfaces in the same module or two operations in the same interface) cannot differ in case only. For example, in the myObject interface, IDL would not allow an operation named anOperation and another operation named anOPERATION to be defined simultaneously. Obviously, you haven't yet been exposed to modules, interfaces, and operations; stay tuned to this chapter for more details on these constructs.


Note:What the OMG refers to as operations, you might know as methods, member functions, or even messages. Whatever name you know it by, an operation defines a particular behavior of an interface, including the input and output parameters of that particular behavior. Throughout this book, the terms operation and method will be used interchangeably, because they refer to exactly the same concept.

IDL Definition Syntax

All definitions in IDL are terminated by a semicolon (;), much as they are in C, C++, and Java. Definitions that enclose other definitions (such as modules and interfaces) do so with braces ({}), again like C, C++, and Java. When a closing brace also appears at the end of a definition, it is also followed by a semicolon. An example of this syntax appears in Listing 3.2 in the section, "The Module."

IDL Comments

Comments in IDL follow the same conventions as Java and C++. Both C-style and C++-style comments are allowed, as illustrated in Listing 3.1. (Note that the second comment in the listing contains embedded comment characters; these are for description purposes only and are not actually allowed by IDL.)

Listing 3.1. IDL comments.

1: // This is a C++-style comment. Anything following the "//"
2: // characters, to the end of the line, is treated as part of the
3: // comment.
4: /* This is a C-style comment. Anything between the beginning
5:    "/*" characters and the trailing "*/" characters is treated
6: as part of the comment. */

Use of the C Preprocessor

IDL assumes the existence of a C preprocessor to process constructs such as macro definitions and conditional compilation. If the IDL you write does not make use of these features, you can do without a C preprocessor, but you should recognize that IDL can make use of C preprocessor features.


Note: The C preprocessor, included with C and C++ compilers and with some operating systems, is a tool that is essential to the use of those languages. (The Java language does not use a preprocessor.) Before a C or C++ compiler compiles code, it runs the preprocessor on that code. The preprocessor, among other things, resolves macros, processes directives such as #ifdef...#endif and #include, and performs substitutions of #defined symbols. For more information on the C preprocessor, consult a C or C++ text, or if you have access to a UNIX system, try man cpp.

The Module

The first IDL language construct to examine is the module. The module construct is used to group together IDL definitions that share a common purpose. The use of the module construct is simple: A module declaration specifies the module name and encloses its members in braces, as illustrated in Listing 3.2.

New Term: The grouping together of similar interfaces, constant values, and the like is commonly referred to as partitioning and is a typical step in the system design process (particularly in more complex systems). Partitions are also often referred to as modules (which should be no surprise) or as packages (in fact, the IDL module concept closely resembles the Java package concept--or the other way around, because IDL came first).

Listing 3.2. Module example.

1: module Bank {
2:     interface Customer {
3:         ...
4:     };
5:     interface Account {
6:         ...
7:     };
8:     ...
9: }; 

The example in Listing 3.2 defines a module called Bank, which contains two interfaces called Customer and Account (ellipses are used to indicate that the actual definitions are omitted). The examples get ahead of themselves somewhat by using the interface construct here; interfaces are described later in this chapter.

Coupling and Cohesion

New Term: So, now that you have the ability to group interfaces together, how do you decide which interfaces to group together? This is really a question of system design and would be best answered in a text dedicated to that subject. (There are plenty of excellent books available on the subject of object-oriented analysis and design.) However, an overall guideline is that a good design generally exhibits two attributes: loose coupling and tight cohesion. The first means that components in separate modules are not tightly integrated with each other; an application using components in one module generally need not know about components in another module. (Of course, there is often some overlap between modules for various reasons, such as the need to share data between modules or to facilitate common functionality between modules.) When there is little or no dependency between components, they are said to be loosely coupled.

On the other hand, within a single module it is advantageous for a design to achieve tight cohesion. This means that interfaces within the module are tightly integrated with each other. For example, a module called InternalCombustionEngine might contain interfaces such as CylinderHead, TimingChain, Crankshaft, Piston, and many others. It is difficult to describe the purpose of one of these components without referring to the others; hence, one might say that the components are tightly cohesive. By way of comparison, you would probably find very little in common between the components of InternalCombustionEngine and, for instance, AudioSystem; InternalCombustionEngine components such as OilFilter and SparkPlug are loosely coupled to AudioSystem components such as CompactDiscPlayer and Subwoofer.

Figure 3.1 illustrates the concepts of coupling and cohesion; note that there are many relationships between components within the same module, whereas there are few relationships between components within separate modules. The figure also illustrates the advantage of loose coupling between modules: Imagine if, when you installed a new CD player in your car, you had to change the spark plugs and replace your timing chain! Loose coupling of components reduces the possibility that changes to one component will require changes to another.

Figure 3.1. Coupling and cohesion.

Primitive Types

Like most programming languages, IDL features a variety of primitive types (which can then be combined into aggregate types). These types store simple values such as integral numbers, floating point numbers, character strings, and so on.

void

The IDL void type is analogous to the void type of C, C++, and Java. It is pointless to have a variable of type void, but the type is useful for methods that don't return any value.

boolean

The IDL boolean type, as its name suggests, stores a Boolean value. IDL defines two boolean constants, true and false, which have obvious meanings. Depending on the programming language used, the IDL boolean type can map to an integral type (such as C/C++'s short) or to the language's native Boolean type (as is the case with Java).

boolean aBoolean;

char and wchar

The char type in IDL, analogous to the char type in C, C++, and Java, stores a single character value. As expected, it maps directly to the char type in these languages. The char data type is an 8-bit quantity.

char aChar;

In version 2.1, CORBA added the wchar, or wide character type, which has an implementation-dependent width (which is likely to be 16 bits, but you might want to consult with your ORB documentation).

Floating Point Types

IDL defines a number of types to represent floating point values; these are already familiar to most developers.

float

The IDL float type represents an IEEE single-precision floating point value. Again, it is analogous to the C, C++, and Java type of the same name.

float aFloat;

double and long double

The double type represents an IEEE double-precision floating point value. Not surprisingly, it corresponds to the familiar double type of a number of languages.

double aDouble;

The long double type, introduced in CORBA 2.1, represents an IEEE double-extended floating point value, having an exponent of at least 15 bits and a signed fraction of at least 64 bits.

Integer Types

IDL also defines a number of integral numeric types. Again, most developers will recognize these. Unlike most familiar programming languages, though, IDL doesn't define a plain int type, only short and long integer types.

long and long long

The IDL long type represents a 32-bit signed quantity (with a range of -231..231-1), like C/C++'s int (on most platforms) and Java's int (on all platforms, because Java, unlike C/C++, explicitly specifies the size of int).

long aLong;

In CORBA 2.1, the long long type was added, which is a 64-bit signed quantity (with a range of -263..263-1).

unsigned long and unsigned long long

The unsigned long type in IDL is an unsigned version of the long type; its range is 0..232-1.

unsigned long anUnsignedLong;

CORBA 2.1 added the unsigned long long type, which is a 64-bit unsigned quantity with a range of 0..264-1.

short

The short type represents a 16-bit signed quantity, like C/C++'s short or short int (again, on most platforms) and Java's short. Its range is -215..215-1.

short aShort;

unsigned short

The unsigned short type, as expected, is the unsigned version of short, with a range of 0..216-1.

unsigned short anUnsignedShort;

octet

The octet type is an 8-bit quantity that is not translated in any way during transmission. This type has no direct counterpart in C and C++, although the char or unsigned char types are often used to represent this type of value. Java, of course, has the byte type, which is similar.

octet anOctet;

string

The string type represents a string of characters, similar to C++'s Cstring and Java's String class. C has no direct counterpart (because there is no "true" string type in C); character arrays are used instead. IDL supports both fixed- and variable-length strings.

string aFixedLengthString[20];
string aVariableLengthString;

The const Modifier

In addition to these standard types, IDL, like C and C++, also allows constant values to be specified by using the const modifier. const values are useful for values that should not change, such as physical constants such as pi or c. The scope of a const value, like any value, is that of its enclosing interface or module.

const float aFloatConstant = 3.1415926535897932384;
const long aLongConstant = 12345;
const string aStringConstant = "Ain't IDL great?";

Constructed Types

Constructed types, which combine other types, enable the creation of user-defined types. Perhaps the most useful of these constructs is the interface, which defines the services provided by your application objects. Because IDL is, after all, the Interface Definition Language, it seems fitting that interfaces should comprise the bulk of IDL source code.

The Enumerated Type

The enumerated type, enum, allows the creation of types that can hold one of a set of predefined values specified by the enum. Although the identifiers in the enumeration comprise an ordered list, IDL does not specify the ordinal numbering for the identifiers. Therefore, comparing enum values to integral values might not be safe, and would almost certainly not be portable across languages. C and C++ also have an enumerated type that works similarly. An example of the enum type appears in Listing 3.3.

Listing 3.3. enum example.

1: enum DaysOfWeek {
2:     Sunday,
3:     Monday,
4:     Tuesday,
5:     Wednesday,
6:     Thursday,
7:     Friday,
8:     Saturday
9: };

The Structure Type

IDL provides a structure type--struct--that contains, as in C and C++, any number of member values of disparate types (even other structs). structs are especially useful in IDL because, unlike CORBA objects (which are represented by interfaces), structs are passed by value rather than by reference. In other words, when a struct is passed to a remote object, a copy of that struct's values is created and marshaled to the remote object. An example of the struct type appears in Listing 3.4.

Listing 3.4. struct example.

1: struct DateStruct {
2:     short year,
3:     short month,
4:     short day,
5:     short hour,
6:     short minute,
7:     short second,
8:     long microsecond
9: };

The union Type

The IDL union type, like a struct, represents values of different types. The IDL union type will appear somewhat odd to C and C++ programmers, resembling something of a cross between a C/C++ union and a case statement, but Pascal programmers should recognize the format. An example of a union definition appears in Listing 3.5.

Listing 3.5. union example.

1: union MultiplePersonalities switch(long) {
2:     case 1:
3:         short myShortPersonality;
4:     case 2:
5:         double myDoublePersonality;
6:     case 3:
7:     default:
8:         string myStringPersonality;
9: }; 

In the example in Listing 3.5, a variable of type MultiplePersonalities might have either a short value, a double value, or a string value, depending on the value of the parameter when the union is used in a method call. (The parameter is known as a discriminator.)

New Term: A discriminator, as used in an IDL union, is a parameter that determines the value used by the union. In the example in Listing 3.5, a long was used for the discriminator; other types can be used also, including long, long long, short, unsigned long, unsigned long long, unsigned short, char, boolean, or enum. The constant values in the case statements must match the discriminator's type.


Note:In practice, IDL unions are rarely useful. Whereas the traditional C union might find a use in optimization of native C code, unions almost never appear in distributed applications. Behavior of objects is usually better abstracted through the use of interfaces. However, should the need for a union arise, IDL provides this type, just in case.

The interface Type

The interface type is by far the most versatile of IDL data types. The interface describes the services provided by a CORBA object. These services appear in the form of operations (or methods), resembling methods in object-oriented languages like C++ and Java. The difference, again, is that IDL is used only to specify the interfaces of these methods, whereas languages like Java and C++ are used to specify interfaces and (usually) provide implementations for those interfaces' methods.

The IDL interface type is very much like the Java interface type because neither provides an implementation for the methods defined. (However, a major difference is that IDL interfaces can contain attributes, whereas Java interfaces don't.) C++, on the other hand, has no direct counterpart to IDL's interface, although it is common for C++ applications to use a header file to define the interface of a class. An IDL interface definition can thus be compared to a C++ header file containing a class definition. Also, a C++ class whose methods are all pure virtual can be considered analogous to the IDL interface.

Like a Java or C++ class, an IDL interface can contain attributes (also commonly known as member variables) and operations (which, again, you may know as methods or member functions). Like Java's interface, all methods defined within an IDL interface are public--they can be called by any other object having a reference to the interface's implementation object. Finally, because IDL interfaces usually describe remote objects, IDL also provides some additional modifiers to further describe the interface and its members. For example, methods can be declared as oneway; arguments to methods can be declared as in, out, or inout; and attributes can be declared as readonly. In a few moments, you'll explore what each of these modifiers means, how it is used, and why.

Methods and Parameters

Methods of an interface are, in essence, what define an object's functionality. Although the object's implementation determines how the object behaves, the interface's method definitions determine what behavior the object implementing that interface provides. These method definitions are often called method signatures, or just signatures. IDL methods can use any IDL data types as input and output parameters--primitive types, structs, sequences, and even interfaces. The general syntax for a method declaration is as follows:

[oneway] return_type methodName(param1_dir param1_type param1_name,
        param2_dir param2_type param2_name, ...);

The oneway modifier is optional; return_type specifies the type of data returned by the method, the paramndir modifier specifies the direction of each parameter (one of in, out, or inout), and paramntype specifies the type of each parameter. Modifiers such as these are not commonly found in programming languages and thus merit some attention.

New Term: A method signature, often simply called a signature, describes what a method does (ideally, the method name should specify, at least in general terms, what the method does), what parameters (and their types) the method takes as input, and what parameters (and their types) it returns as output. in, out, and inout Parameters As already mentioned, parameters in a method can be declared as in, out, or inout. These names are fairly self-explanatory: An in parameter serves as input to the method; an out parameter is an output from the method; and an inout parameter serves as an input to and an output from the method.

In remote method terms, this means that before the method is invoked, any in and inout parameters are marshaled across the network to the remote object. After the method executes, any out and inout parameters--along with the method's return value, if any--are marshaled back to the calling object. Note particularly that inout parameters are marshaled twice--once as an input value and once as an output value. oneway Methods The typical paradigm for calling a remote method is as follows: When an object calls a method on a remote object, that calling object waits (this is called blocking) for the method to execute and return. When the remote object finishes processing the method invocation (often called a request), it returns to the calling object, which then continues its processing. Figure 3.2 illustrates this process.

New Term: In general, the term blocking refers to any point at which a process or thread is waiting for a particular resource or another process/thread. Within the context of CORBA, if a client invokes a remote method and must wait for the result to be returned, the client is said to block.

A request is simply another name for a remote method invocation. The term is commonly used when referring to the operation of a distributed system. In fact, when you study CORBA's Dynamic Invocation Interface (DII), you'll see that remote methods can be invoked through a Request object.

Figure 3.2. Blocking on a remote method call.

When a method is declared oneway, it means that the object calling that method will not block. Rather, that object will call the remote method and then immediately continue processing, while the remote object executes the remote method. The advantage to this approach is that the calling object can continue working rather than wait for the remote object to complete the request. Figure 3.3 illustrates the operation of a oneway method.

Figure 3.3. The oneway (nonblocking) remote method call.

The flexibility of the oneway calling paradigm comes at a price, however. Because the method invocation returns before the method execution is completed, the method cannot return a value. Therefore, for a oneway method, the return value must be declared void, and all parameters must be declared as in (out and inout parameters are not allowed). In addition, a oneway method cannot raise any exceptions. Also, the calling object has no way of knowing whether the method executed successfully; the CORBA infrastructure makes a best-effort attempt to execute the method, but success is not guaranteed. (Readers familiar with the User Datagram Protocol will recognize the similarity.) Therefore, oneway methods are most useful for situations in which one object wants to inform another object of a particular status but (1) does not consider the message to be essential and (2) does not expect (or desire) a response.


Note:A method that has only in parameters, returns a void, and does not raise any exceptions is not automatically a oneway method. The difference between such a method and a oneway method is that, whereas the latter is not guaranteed to execute successfully (and the client will have no way of determining whether it did), the former will result in the client blocking until a result is returned from the remote method (even though the result will be null). The important difference here is that the success of the non-oneway method can be determined, because a CORBA system exception would be raised if this were not the case.

There are ways of overcoming the issues associated with blocking. Most commonly, the use of multithreading can circumvent the blocking "problem" by creating a separate thread to invoke the remote method. While that thread is blocked waiting for the result to be returned, other threads can continue working. On Day 10 you'll study issues such as this in greater detail and learn about some possible resolutions to these issues.

Attributes

An attribute of an IDL interface is analogous to an attribute of a Java or C++ class, with the exception that IDL attributes always have public visibility. (Indeed, everything in an IDL interface has public visibility.) The general syntax for defining an attribute is this:

[readonly] attribute attribute_type attributeName;

In Java and C++, it is generally considered good programming practice to provide accessor and mutator methods for attributes of a class, and to make the attribute itself protected or private. IDL advances this concept one step further: IDL attributes map to accessor and mutator methods when the IDL is compiled. For instance, the following definition:

attribute short myChannel;

maps to the following two methods:

short myChannel();
void myChannel(short value);
readonly Attributes 

The preceding example indicates the optional use of the readonly modifier. As the name suggests, this modifier is used to specify that a particular attribute is for reading only; its value cannot be modified directly by an external object. (The object implementing the interface is, of course, free to modify values of its own attributes as it sees fit.) Although a non-readonly attribute maps to a pair of accessor/mutator methods, for a readonly attribute the IDL compiler will generate only an accessor method.

Inheritance of interfaces

IDL interfaces, like the Java and C++ constructs they resemble, support the notion of inheritance. That is, an interface can inherit the methods and attributes of another (one can also say that the former interface derives from or is derived from the latter). In addition to inheriting its superclass' methods and attributes, a subclass can define additional methods and attributes of its own. The subclass can also be substituted anywhere that its superclass is expected; for example, if a method takes a parameter of type Fish and the Halibut interface is a subclass of the Fish interface, then that method can be called with a parameter of type Halibut instead of Fish.

New Term: A subclass, or derived class, is a class that inherits methods and attributes from another class. The class from which these methods and attributes are inherited is known as the superclass, or parent class. Although IDL uses interfaces and not classes, these general terms can still be applied.

New Term: In object speak, polymorphism refers to the ability to substitute a derived class into a parameter that expects that class's superclass. Because in a sense the subclass is an instance of the superclass (as a Beagle is a Dog), any operation that acts on the superclass can also act on the subclass. Polymorphism is a fundamental property of object-oriented architecture.

The syntax for specifying interface inheritance resembles the syntax used by C++ and Java. The exception in IDL is that no visibility for the superclass is specified (recall that all methods and attributes are implicitly public). The inheritance syntax is illustrated in Listing 3.6.

Listing 3.6. Inheritance syntax example.

1: interface Fish {
2:     ...
3: };
4: interface Halibut : Fish {
5:     ...
6: }; 

In Listing 3.6, the Halibut interface inherits from the Fish interface, as indicated by the colon (:) operator. Attributes and methods have, of course, been omitted.

One last word with respect to inheritance of IDL interfaces: IDL interfaces, like C++ classes, can inherit from more than one superclass. This capability, known as multiple inheritance, is not available in Java, although Java allows a single class to implement multiple interfaces, a feature that often achieves the same result as multiple inheritance. The syntax for multiple inheritance in IDL is illustrated in Listing 3.7.

Listing 3.7. Multiple inheritance syntax example.

1: interface LandVehicle {
2:     ...
3: };
4: interface WaterVehicle {
5:     ...
6: };
7: interface AmphibiousVehicle : LandVehicle, WaterVehicle {
8:     ...
9: }; 

In Listing 3.7, the AmphibiousVehicle interface, being both a LandVehicle and a WaterVehicle, inherits both those interfaces. Note that due to the polymorphic behavior of derived classes, an AmphibiousVehicle can be substituted for either a LandVechile or a WaterVehicle.

interface Definition Syntax

The syntax for an interface definition is not unlike the syntax used in C++ or Java. The body of the interface contains method signatures and attribute declarations, in no particular order. A few sample interface definitions appear in Listing 3.8.

Listing 3.8. Sample IDL interfaces.

 1: // This module defines some useful household appliances.
 2: module Appliances {
 3:     // Television interface definition.
 4:     interface Television {
 5:         // My serial number.
 6:         readonly attribute string mySerialNumber;
 7:         // My current volume level.
 8:         attribute short myVolume;
 9:         // My current channel.
10:         attribute short myChannel;
11:         // Turn this Television on.
12:         void turnOn();
13:         // Turn this Television off.
14:         void turnOff();
15:         // Set this Television's sleep timer.
16:         void setSleepTimer(in short minutes);
17:     };
18:     interface WWWTelevision : Television {
19:         // Surf to the given URL.
20:         void surfTo(in Internet::URL url);
21:     };
22: }; 


Close inspection reveals that IDL interfaces don't specify constructors or destructors. Java developers should not be surprised at this because Java interfaces don't include constructors or destructors either (but then again, there are also no destructors in Java). C++ developers, however, might be wondering what happened to the constructors and destructors. As it turns out, constructors and destructors are still a part of CORBA objects; they just aren't included as part of the object's interface. You'll see the reason for this when you begin building a CORBA application in the next chapter.

Other IDL Constructs

IDL supports several other useful constructs. Among these are the capability to refer to any IDL type by a user-specified type name and the capability to declare a type name without defining it, which is helpful for handling circular references.

typedef

Like C and C++, IDL supports a typedef statement to create a user-defined type name. typedef can make any IDL type accessible by a type name of the user's choosing, a capability that adds convenience to the language. For example, in Listing 3.8, the Television interface contains the member mySerialNumber, which is of type string. Because the use of the string type isn't very telling, it might be preferable to define a SerialNumber type that can then be used by any interfaces and methods requiring a serial number. Assuming that the string type is adequate for storing serial numbers, it would be convenient to use a string type but refer to it as a SerialNumber. The typedef statement allows you to do just this. The statement

typedef string SerialNumber;

means that anywhere the SerialNumber type is found, it should be treated as a string.

Forward Declarations

Occasionally, you will create interfaces that reference each other--that is, within the first interface you'll have an attribute of the second interface's type, or a method that uses the second interface's type as a parameter or return value. The second interface, similarly, will reference the first interface in the same way. Listing 3.9 illustrates this concept, known as a circular reference.

New Term: A circular reference occurs when two classes (or interfaces, in the context of IDL) each have attributes or methods that refer to the other class.

Listing 3.9. Circular reference example.

1: module Circular {
2:     interface A {
3:         void useB(in B aB);
4:     }
5:     interface B {
6:         void useA(in A anA);
7:     }
8: }; 

In Listing 3.9, the A interface references the B interface, which in turn references the A interface. If you were to attempt to compile this code as it is listed, the IDL compiler would report an error in the useB() method definition because the B interface is unknown. If you were to reverse the order of definition of the A and B interfaces, the IDL compiler would signal an error in the useA() method because the A interface is unknown. As you can see, the circular reference problem is a bit of a Catch 22.

C and C++ programmers might already know the answer to the circular reference problem: the forward declaration. A forward declaration allows you to inform the IDL compiler that you intend to define the declared type later, without defining the type at that point. (You will have to define the type at some point, however.) The syntax of a forward declaration, which closely resembles the C/C++ syntax, is simple:

interface B;

This tells the IDL compiler that a definition for the Bar interface will appear at some point in the future but that for now it should accept references to the as-yet-undefined Bar. Listing 3.10 illustrates how the forward declaration would fit into the previous example.

Listing 3.10. Circular reference example.

 1: module Circular {
 2:     // Forward declaration of the B interface.
 3:     interface B;
 4:     interface A {
 5:         void useB(in B aB);
 6:     }
 7:     interface B {
 8:         void useA(in A anA);
 9:     }
10: }; 

In Listing 3.10, when the IDL compiler reaches the definition for useB(), it sees that the B interface has already been declared (by the forward declaration) and thus will not report an error.


Note:You could just as well have defined the B interface first and made a forward declaration to the A interface; the IDL compiler does not impose a particular order in which interfaces must be defined.

Container Types

Most programming languages include constructs for dealing with multiple values of similar types. Arrays are common throughout programming languages: Java includes java.util.Vector, C++ features its Standard Template Library (STL), and, of course, various libraries of container classes abound. IDL is no exception, featuring two such constructs: the sequence, which is a dynamically sizable array, and the array, which mirrors the array constructs found in many languages.

The sequence Type

An IDL sequence is simply a dynamically sizable array of values. These values can be dynamically inserted in or removed from the sequence; the sequence manages its size accordingly. All values of a sequence must be of the same type or derived from the same type (with the exception of the any type. For example:

sequence<float> temperatureSequence;

defines a sequence of float values and assigns this type to the variable temperatureSequence. Values of type float can subsequently be added to temperatureSequence, or values can be removed. Recalling the Appliances example (refer to Listing 3.8), you can also create a sequence of Television objects:

sequence<Television> televisionInventory;

In this case, televisionInventory can be populated with objects of type Television, or any objects derived from Television, in this case WWWTelevision. If you had created a third interface derived from Television--for instance, PortableTelevision--the televisionInventory sequence could contain Televisions, WWWTelevisions, and PortableTelevisions.

The Array

An IDL array corresponds directly to the array constructs in C, C++, and Java. An array stores a known-length series of similar data types. For example:

string DayNames[7];

defines an array, with the name DayNames, of seven string values. Arrays can hold elements of any IDL data type; as in the Appliances example (refer to Listing 3.8), you can define the following array:

Television TVArray[10];

to define an array of ten Televisions. Remember that, due to polymorphism, each array element can hold either a plain Television or the derived WWWTelevision.

The exception Type

One concept embraced recently by developers is the use of exceptions to perform error handling. Exceptions, featured in object-oriented languages such as Java and C++, are constructs which are created to signify some error condition. When a method raises an exception, the method stops what it is doing and returns immediately. When the calling method catches the exception, it can either handle the exception or throw it up to that method's caller. This process might continue all the way up to the top level (typically, main()); if the top-level method does not handle the exception, the application usually exits (although allowing this to happen is generally considered poor programming practice).

New Term: When a method passes an exception back to its caller, it is said that the method throws an exception, or in CORBA-speak, raises an exception.

CORBA and IDL fully support exception handling through predefined standard exceptions and user-defined exceptions. IDL allows developers to define exceptions and specify which exceptions are raised by what methods. When an exception is raised, the ORB passes that exception back to the calling object's ORB, which then passes the exception back to the calling object. In this way, CORBA extends the familiar exception-passing mechanism to a distributed architecture.

In addition to their distributed nature, IDL exceptions differ from their counterparts in C++ and Java in other ways. C++ exceptions can be virtually any type; C++ does not even require that exception objects be derived from a certain type (for instance, a method could throw a const char*, if it so desired). Java exceptions can be any type that implements the java.lang.Throwable interface. IDL, however, is somewhat more restrictive than these languages; exception objects must be declared explicitly as such. Furthermore, whereas C++ and Java allow exception types to be derived from other types, IDL does not support inheritance of exception types.

exception

The IDL exception type itself is similar to a struct type; it can contain various data members (but no methods). The definition for an exception type also resembles the struct definition, as the example in Listing 3.11 shows. The example demonstrates that an exception need not have any members at all; sometimes the mere act of raising an exception provides enough error-handling information.

Listing 3.11. exception definition example.

 1: // This exception might be used where a file to be opened
 2: // couldn't be located. Since it might be useful for the caller
 3: // to know the invalid filename, the exception provides that
 4: // information.
 5: exception FileNotFoundException {
 6:     string fileName;
 7: };
 8: // This exception might be used where a particuar operation,
 9: // which was supposed to have completed within a given amount
10: // of time, failed to do so. The exception provides the
11: // operation name and the time length given for the operation.
12: exception OperationTimedOutException {
13:     string operationName;
14:     long timeoutLength;
15: };
16: // This exception might be used where an attempt to log into a
17: // system failed. No other information is necessary, so none is
18: // provided.
19: exception InvalidLoginException {
20: };

Standard Exceptions

In addition to allowing developers to create user-defined exceptions, CORBA also provides a number of standard exceptions, or system exceptions. Exceptions in this set might be raised by any remote method invocation. IDL method definitions don't explicitly declare that they raise a system exception; rather, these exceptions are raised implicitly. (Actually, when the IDL code is compiled, the generated method definitions do declare that CORBA system exceptions are raised, as well as any user-defined exceptions raised by those methods.) In addition to regular methods, even the accessor/mutator methods--corresponding to the attributes of interfaces--can raise standard exceptions, even though they cannot raise user-defined exceptions.

The standard exceptions provided by CORBA are listed in Table 3.1.

Table 3.1. CORBA standard exceptions.

Exception Name Description
UNKNOWN The unknown exception.
BAD_PARAM An invalid parameter was passed.
NO_MEMORY Dynamic memory allocation failure.
IMP_LIMIT Violated implementation limit.
COMM_FAILURE Communication failure.
INV_OBJREF Invalid object reference.
NO_PERMISSION No permission for attempted operation.
INTERNAL ORB internal error.
MARSHAL Error marshaling parameter or result.
INITIALIZE ORB initialization failure.
NO_IMPLEMENT Operation implementation unavailable.
BAD_TYPECODE Bad TypeCode.
BAD_OPERATION Invalid operation.
NO_RESOURCES Insufficient resources for request.
NO_RESPONSE Response to request not yet available.
PERSIST_STORE Persistent storage failure.
BAD_INV_ORDER Routine invocations out of order.
TRANSIENT Transient failure--re-issue request.
FREE_MEM Cannot free memory.
INV_IDENT Invalid identifier syntax.
INV_FLAG Invalid flag was specified.
INTF_REPOS Error accessing interface repository.
BAD_CONTEXT Error processing context object.
OBJ_ADAPTER Failure detected by object adapter.
DATA_CONVERSION Data conversion error.
OBJECT_NOT_EXIST Nonexistent object--delete reference.

The any Type

For those occasional methods that need to accept any sort of CORBA object as a parameter or take a parameter that could potentially be one of several unrelated data types (in other words, none of the data types are inherited from any of the others), IDL provides the any type. When any is used as the type of a parameter or return value, that value can literally be any IDL type. A method that accepts an any as an input parameter will usually need to determine precisely what type of object is being passed before it can manipulate the object; you will see how this is done when you begin implementing CORBA clients and servers.


Note:For a method that must accept one of various types of CORBA objects as a parameter, the any is not always the only choice. If the type of the parameter can be one of only a few types, and the type of the parameter is known, then the union type might be a better choice. However, with the union type, all of the types must be known in advance; this is not true for an any type. An any parameter can literally hold any type of object, even one that is unknown to the server receiving the any parameter.

For an example of the any type, see Listing 3.12. In this example, the browseObject() method accepts a single parameter, called object, of type any. A client calling this method can use an object of any IDL type to fill this parameter. Internally to browseObject(), the method will attempt to determine what the actual type of object is. If it determines that object is a type of object it can interact with, it will do so. Otherwise, it will raise the UnknownObjectType exception, which is returned to the caller.

Listing 3.12. any example.

1: interface ObjectBrowser {
2:     exception UnknownObjectType {
3:         string message;
4:     };
5:     void browseObject(in any object) raises (UnknownObjectType);
6: };

The TypeCode Pseudotype

Along with the any type comes the TypeCode pseudotype. TypeCode is not actually an IDL type; instead, it provides type information to a CORBA application. In most method calls, the types of the parameters passed to the method are known because they are specified by the IDL method signatures. However, when a method accepts an any type as an argument, the actual type of that object is unknown. This is where TypeCode comes in. Because every CORBA type--both standard types such as long and string and user-defined types such as Television--has a unique TypeCode associated with it, a method implementation can determine which type of object was sent through its any parameter. When the object's type is determined, the method can act on that object. Think of TypeCodes as a sort of runtime-type information for CORBA applications.

Summary

This chapter presented the basic data types provided by CORBA's Interface Definition Language (IDL), including integer and floating point numeric types, Boolean values, and characters and character strings. Because many of these data types closely resemble data types of programming languages like C, C++, or Java, readers who are already familiar with one of these languages will have little difficulty assimilating the IDL types.

You have expanded your knowledge of IDL to now include higher-level data types--particularly the user-defined types--and their uses. Most importantly, you are familiar with the interface construct and how it defines the behavior of CORBA objects. Because the interface is one of the fundamental IDL data types, a clear understanding of this construct is essential to the design of CORBA applications. Indeed, you cannot design or implement a CORBA application without interfaces.

Today you have seen some very useful IDL data types and constructs, in particular, the sequence and array types for storing multiple values of similar types. You learned about the exceptions in IDL, including the predefined CORBA standard exceptions. Finally, you looked at the any type, which can contain a value of any IDL type, and its counterpart, the TypeCode pseudotype, for determining unknown data types passed to a method. These constructs--particularly the sequences and exceptions--are essential for building robust CORBA applications.

You've covered just about everything there is to know about IDL; now it's time to apply it. In the next chapter--Day 4, "Building a CORBA Application"--you'll do just that, translating IDL definitions into working CORBA server and client applications.

Q&A

Q What's the point of having both sequence and array types?

A
Array types are useful when the number of elements in an array is fixed and known in advance. In many instances, though, this is not the case; arrays often vary in size as members are dynamically added and removed. Consequently, a dynamically sizable array, such as the IDL sequence, is often much more convenient. For maximum flexibility, IDL offers both.

Q What are all those
exceptions and when (or how) are they raised?

A
First of all, nondistributed methods have little need for many of the CORBA standard exceptions, but they make a lot more sense in a distributed environment. As mentioned previously, the standard exceptions are generally not raised by any code that you will write; rather, the exceptions in this set are raised automatically by the ORB when an error condition occurs. Remember that all IDL methods implicitly raise these exceptions, even the accessor/mutator methods generated by the IDL compiler for each attribute.

Q Why isn't (insert your favorite primitive data type here) supported?


A
Because one primary goal of IDL is to be language-neutral, it isn't practical to support all primitive data types contained in all languages. Rather than attempt to provide this level of support (nearly impossible to achieve), IDL provides the most common and useful primitive data types.

Q How does an object modify one of its
attributes when that attribute is declared to be readonly?

A
If you're asking questions like this, you're definitely thinking ahead. When you learn about implementation of IDL interfaces on Day 4, it will become clear how this is accomplished. For the time being, though, remember that the object implementing an interface has full access to its own state. A readonly attribute is mapped to a method of that object, and that method can return any value the object wants. Typically, the implementing object will define actual attributes of its own that correspond to attributes in the IDL interface, although this isn't strictly necessary.

Q Because Java does not support multiple inheritance, how is multiple inheritance of IDL interfaces achieved in Java?

A
You'll see when you begin implementing IDL interfaces in Java that the IDL language mapping for Java maps IDL interfaces to Java interfaces. Although multiple inheritance of classes is not supported in Java, multiple inheritance of interfaces is.

Workshop

The following section will help you test your comprehension of the material presented in this chapter and put what you've learned into practice. You'll find the answers to the quiz in Appendix A.

Quiz

1. Define a type (using typedef) called temperatureSequence that is a sequence of sequences of floats (yes, this is legal).

2
. Why might a type as described in the preceding question be useful?

3
. Why are exceptions useful?

4
. Why is the module construct useful?

5
. Name some practical uses for the octet data type.

6
. Define an enumerated type containing the months of the year.

7
. Why might a nonblocking remote method call be advantageous, compared to a blocking method call?

8
. Imagine a compound data type with a large number of data members. This data type will frequently be used by a client application, which will generally need to access each of the data members. Would it be more efficient to encapsulate this data type into a struct or an interface? Why?

9
. Because an IDL method can return a value, what is the purpose of out and inout parameter types?

10
. Why is a oneway method unable to return any value to the caller? Can you think of a mechanism, using oneway calls, to return a result to the caller?

Exercises

1. Consider the following classes: Conduit, Faucet, FuseBox, Outlet, Pipe, WaterHeater, WaterPump, and Wire. How would you partition these classes? What relationships, if any, are there between the partitions you have created?

2
. Create an interface that describes a clock/radio (which can set the hours, set the minutes, set the alarm time, and so on).


Previous chapterNext chapterContents


© Copyright, Macmillan Computer Publishing. All rights reserved.