Previous Up Next

Chapter 1  Core Concepts and Terminology

1.1  Object Management Group (OMG)

The Object Management Group (OMG) is a not-for-profit organization that promotes the use of object-oriented technologies. Among other things, it defines the CORBA and UML standards. The OMG web site (www.omg.org) provides all of its standards documents available free-of-charge in the form of downloadable PDF files. The OMG has a relatively small staff that focuses on administrative tasks, such as maintaining the OMG web site and organizing meetings of its members.

The work of defining standards is carried out by the members of the OMG, of which there are about 600. Any organization (or individual) that is interested in the work of the OMG can become a member. Member organizations typically include universities, software vendors and software users. Members can volunteer to take part in task forces that have the goal of defining new OMG standards or enhancing existing OMG standards. It is through this work that the OMG standards evolve in directions directed by the real-world concerns of its members.

1.2  CORBA

CORBA is an acronym for Common ORB Architecture. The phrase common architecture means a technical standard, so CORBA is simply a technical standard for something called an ORB.

ORB is an acronym for Object Request Broker, which is an object-oriented version of an older technology called Remote Procedure Call (RPC). An ORB or RPC is a mechanism for invoking operations on an object (or calling a procedure) in a different (“remote”) process that may be running on the same, or a different, computer. At a programming level, these “remote” calls look similar to “local” calls.

Many people refer to CORBA as middleware or integration software. This is because CORBA is often used to get existing, stand-alone applications communicating with each other. A tag-line used by IONA Technologies, Making Software Work Together™, sums up the purpose of CORBA.

Of course, CORBA is not the only middleware technology in existence. Some other brand names of middleware include Java Remote Method Invocation (RMI), IBM MQ Series, Microsoft’s COM and .NET, SOAP, and TIBCO Rendezvous. Scripting languages—such as UNIX shells, Perl, Python and Tcl—can also be classified as middleware because scripts are often used to connect programs together. A famous example of this is the pipe operator in UNIX shells, as illustrated in the example below:


ls -l | grep ^d

The pipe operator sends the output of the first command to the second command. Put simply, it helps two applications communicate with each other, which is what middleware is all about.

One of CORBA’s strong points is that it is distributed middleware. In particular, it allows applications to talk to each other even if the applications are:

CORBA is also an object-oriented, distributed middleware. This means that a client does not make calls to a server process. Instead, a CORBA client makes calls to objects (which happen to live in a server process).

1.3  Client and Server

In some computer technologies, the terms client and server have a strict meaning and an application is either one or the other. CORBA is not so strict. In CORBA terminology, a server is a process that contains objects, and a client is a process that makes calls to objects. A CORBA application can be both a client and a server at the same time.

1.4  Interface Definition Language (IDL)

An IDL file defines the public application programming interface (API) that is exposed by objects in a server application. The type of a CORBA object is called an interface, which is similar in concept to a C++ class or a Java interface. IDL interfaces support multiple inheritance.

An example IDL file is shown in Figure 1.1. An IDL interface may contain operations and attributes. Many people mistakingly assume that an attribute is similar in concept to an instance variable in C++ (a field in Java). This is wrong. An attribute is simply syntactic sugar for a pair of get- and set-style operations. An attribute can be readonly, in which case it maps to just a get-style operation.


module Finance { typedef sequence<string> StringSeq; struct AccountDetails { string name; StringSeq address; long account_number; double current_balance; }; exception insufficientFunds { }; interface Account { void deposit(in double amount); void withdraw(in double amount) raises(insufficientFunds); readonly attribute AccountDetails details; }; };
Figure 1.1: Example IDL file

The parameters of an operation have a specified direction, which can be in (meaning that the parameter is passed from the client to the server), out (the parameter is passed from the server back to the client) or inout (the parameter is passed in both directions). Operations can also have a return value. An operation can raise (throw) an exception if something goes wrong. There are over 30 predefined exception types, called system exceptions, that all operations can throw, although in practice system exceptions are raised by the CORBA runtime system much more frequently than by application code. In addition to the pre-defined system exceptions, new exception types can be defined in an IDL file. These are called user-defined exceptions. A raises clause on the signature of an operation specifies the user-defined exceptions that it might throw.

Parameters to an operation (and the return value) can be one of the built-in types—for example, string, boolean or long—or a “user-defined” type that is declared in an IDL file. User-defined types can be any of the following:

IDL types may be grouped into a module. This construct has a similar purpose to a namespace in C++ or a package in Java, that is, it prepends a prefix on to the names of types in order to prevent namespace pollution. The scoping operator in IDL is "::". For example, Finance::Account is the fully-scoped name of the Account type defined in the Finance module.

1.4.1  The C++ Preprocessor

An IDL compiler uses a C++-compatible preprocessor to preprocess input IDL files. The preprocessor removes C++-style comments and also processes directives that may be in IDL source files. An example of some of these directives is illustrated in Figure 1.2.


#ifndef FOO_IDL #define FOO_IDL #include "another-file.idl" #pragma prefix "acme.com" // This is a one-line comment /* This is a multi-line comment */ module Foo { ... }; #endif
Figure 1.2: Example Foo.idl file

It is common practice to have one module per IDL file and to name the IDL after after the module that it contains. For example, a file called Foo.idl typically contains a module called Foo.

The #include directive instructs the preprocessor to include the contents of the specified file. This makes it feasible to spread IDL definitions over several files in a modular manner rather than having to put all the definitions required for a project in one monolithic file.

The #ifndef...#define...#endif construct shown in Figure 1.2 is typically used to protect against the possibility that an IDL file might be #included multiple times.

A discussion of the #pragma prefix directive is deferred until Section 9.4.

1.4.2  Common IDL Idioms

1.4.2.1  Factory Interfaces

Many object-oriented languages have a constructor that is used to create and initialize an object. However, a constructor creates the object locally, that is, within the address space of the process that calls the constructor. Because of this, a constructor cannot be used to create an object in a different process, and this is the reason why you cannot define a constructor for an IDL interface.

The way for a client process to create an object in a different (server) process is for the client to invoke an operation on an existing object in the server, and for that operation (in the server process) to create a new object. The term factory is typically used to refer to an object that can create other objects. The operation that is used to create an object is often called create()2pt—or has "create" embedded in its name, for example, create_account()2pt—but that is just a naming convention rather than a requirement. No additional syntax is required to define factory interfaces or create-style operations. Rather, these are defined using “normal” IDL syntax. An example of a factory interface is shown in Figure 1.3.


interface Foo { void destroy(); ... }; interface FooFactory { Foo create(...); ... };
Figure 1.3: Example of a factory interface

Just as an IDL interface does not have a constructor, neither does it have a destructor. Sometimes, the decision about when to destroy an object is made solely within a server, without any input from client applications. However, if there is a need for clients to control the destruction of an object then this is typically achieved by defining an operation that, when invoked, destroys the object. This operation is usually called destroy(), but that is just a naming convention rather than a requirement.

1.4.2.2  Callback Interfaces

Callback procedures/objects are commonly used in GUI (graphical user interface) toolkits: an application developer registers a procedure/object with the GUI toolkit runtime and the runtime can “call back” to the procedure/object whenever something relevant occurs, such as the mouse button is pressed or a key on the keyboard is typed. Callback objects are occasionally used in CORBA applications. As far as the IDL compiler is concerned, a callback interface (such as FooCallback, defined in Figure 1.4) is just a normal IDL interface, so there is no special syntax required to define a callback interface.


interface FooCallBack { void notify_something_has_happened(...); }; interface FooCallbackRegistry { void register_callback(in FooCallback cb_obj); void unregister_callback(in FooCallback cb_obj); ... };
Figure 1.4: Example of a callback interface

1.4.2.3  Iterator Interfaces

Let us assume that an IDL interface has a query() operation that uses a sequence to return query results. If the number of items in the returned results could potentially be quite large then it is inadvisable to return all the results in one monolithic lump. There are several reasons for this:


struct Data { ... }; typedef sequence<Data> DataSeq; interface DataIterator { DataSeq next_n_items(in unsigned long how_many); void destroy(); }; interface SearchEngine { DataSeq query( in string search_condition, in unsigned long how_many, out DataSeq results, out DataIterator iter); };
Figure 1.5: An Iterator interface

Figure 1.5 shows how an iterator interface is typically used.2 A query() operation initially returns up to how_many items in the results. If this holds all the items then the iter parameter is set to a nil object reference. Otherwise, the iter parameter contains a reference to an DataIterator object that can be used to obtain more results, again how_many at a time. When the iterator has no more results to return, next_n_items() returns an empty sequence and the client can then destroy() the iterator.

1.4.3  Limitations of IDL

The complexity of data-types that can be defined in IDL is quite limited compared to the complexity of data-types that can be defined in a programming language. The limited flexibility of IDL data-types is due mainly to the lack of pointers. For example, an IDL struct cannot contain a pointer to another IDL type. This lack of pointers makes it impossible to build arbitrary graph structures in IDL. This limitation of IDL is deliberate and is due to a combination of several reasons:

It should be noted that the relatively recent addition of objects by value (OBV) to IDL has finally provided IDL with some functionality similar to what C++ pointers provide. However, as I discuss in Section 9.2, OBV has been a controversial addition to IDL.

Perhaps the most commonly-perceived limitation of IDL is that there is no inheritance of exceptions, that is, one exception type cannot be defined as a subtype of another exception type. Although this limitation is never a show-stopper problem in projects, it certainly provides an irritation for developers. This is because an IDL operation may wish to report, say, 10 different types of exception, and it may be natural to arrange these into an inheritance hierarchy. Because IDL does not allow inheritance of exceptions, the designer is typically forced to either list 10 separate exceptions in the raises clause of the operation or to define one “generic” exception that uses, say, an error_code field to specify which category of error occurred. Both of these approaches can be awkward for client-side developers to handle. With the first approach, they may have 10 different catch clauses in a try-catch block surrounding an operation call. With the second approach, there will be just one catch clause but this will need to use a switch statement or a cascading if-then-else to determine the exact cause of failure.

1.4.4  Mapping IDL to a Programming Language

As Section 1.4 mentioned, IDL is used to define the public API that is exposed by objects in a server application. IDL defines this API in a way that is independent of any particular programming language. However, for CORBA to be useful, there must be a mapping from IDL to a particular programming language. For example, the IDL-to-C++ mapping allows people to develop CORBA applications in C++ and the IDL-to-Java mapping allows people to develop CORBA applications in Java.

The CORBA standard currently defines mappings from IDL to the following programming languages: C, C++, Java, Ada, Smalltalk, COBOL, PL/I, LISP, Python and IDLScript. These officially-endorsed language mappings provide source-code portability of applications across different CORBA products (portability is discussed in Chapter 25). There are unofficial—or, if you prefer, proprietary—mappings for a few other languages, such as Eiffel, Tcl and Perl. Obviously, you could develop a CORBA application with an unofficial language mapping, but you would not have any guarantees of source-code portability to other CORBA vendor products.

1.4.5  IDL Compilers

An IDL compiler translates IDL definitions (for example, struct, union, sequence and so on) into similar definitions in a programming language, such as C++, Java, Ada or Cobol. In addition, for each IDL interface, the IDL compiler generates both stub code—also called proxy types (Section 10.3)—and skeleton code. These terms are often confusing to people for whom English is not their native language, so I explain them below:

A CORBA product must provide an IDL compiler, but the CORBA specification does not state what is the name of the compiler or what command-line options it accepts. These details vary from one CORBA product to another.

1.5  Interoperable Object Reference (IOR)

An object reference is the “contact details” that a client application uses to communicate with a CORBA object. Some people refer to an object reference as an interoperable object reference (IOR) or proxy. The interoperable in interoperable object reference comes about because an IOR works (or interoperates) across different implementations of CORBA. This means that an IOR for an object in, say, an Orbix server can be used by a client that is implemented with a different CORBA product, such as Orbacus, Visibroker, TAO, omniORB or JacORB. An in-depth discussion of object references is provided in Chapter 10.

1.6  CORBA Services

Many programming languages are equipped with a standardized library of functions and/or classes that complement the core language. These standardized libraries usually provide collection data-types (for example, linked lists, sets, hash tables and so on), file input-output and other functionality that is useful for the development of a wide variety of applications. If you asked a developer to write an application in, say, Java, C or C++ but without making use of that language’s standard library then the developer would find it very difficult.

A similar situation exists for CORBA. The core part of CORBA (an object-oriented RPC mechanism built with IDL and common on-the-wire protocols) is of limited use by itself—in the same way that a programming language stripped of its standardized library is of limited use. What greatly enhances the power of CORBA is a standardized collection of services—called CORBA Services—that provide functionality useful for the development of a wide variety of distributed applications. The CORBA Services have APIs that are defined in IDL. In effect, you can think of the CORBA Services as being like a standardized class library. However, one point to note is that most CORBA Services are provided as prebuilt server applications rather than as libraries that are linked into your own application. Because of this, the CORBA Services are really a distributed, standardized class library.

Some of the commonly-used CORBA Services are discussed in other chapters of this book:


1
IDLScript is a scripting language that was invented specifically for CORBA. The people who invented IDLScript felt that CORBA would be best served by having one official scripting language. Some other people felt that just as CORBA supported many “systems” languages (C, C++, Java, Ada, COBOL and so on), so too it would be good for CORBA to support several existing scripting languages (Perl, Python, Tcl, Visual Basic and so on) rather than inventing a new scripting language specifically for CORBA.
2
Iterator is a term denoting an object that is used to “iterate over” (traverse) a collection of items.

Previous Up Next