Mark is a senior consultant with Semaphore (Andover, MA), specializing in client/server development, object-oriented design, and distributed-object computing. He can be reached on CompuServe at 76605,2346.
As enterprise information systems move down from the big iron onto the desktop, much of the activity in object technology now revolves around its role as a foundation for client/server applications. The model for client/server is maturing into one based on peer-to-peer distributed processing. Consequently, a considerable amount of attention is being focused on technologies for linking applications and objects across machine boundaries in a heterogeneous, networked environment. These technologies fall under the rubric "distributed object computing."
Although the technologies vying for market dominance are quite varied, a large number rely on an emerging standard called the "Common Object Request Broker Architecture" (CORBA) specification. This article provides an introduction to CORBA and its related technologies.
Current systems based on (or compliant with) CORBA include the Distributed System Object Model (DSOM) from IBM, Digital's Object Request Broker (ORB), Portable Distributed Objects (PDO) from Next, and Sunsoft's Distributed Objects Environment (DOE). In addition, many CORBA implementations are being offered from smaller vendors, such as Iona's Orbix.
The CORBA spec is being promulgated by the Object Management Group (OMG), which is a consortium of more than 300 hardware, software, and end-user companies, including every heavyweight in the business from IBM to Microsoft. The group was founded in 1989 by a group of 11 companies (original members included Digital, Hewlett-Packard, Hyperdesk, NCR, and SunSoft). Those companies, along with Object Design, were authors of the CORBA 1.0 spec, released in October 1991. It was followed in March of 1992 by revision 1.1, and the group is currently working on revision 2.0, due before the end of 1994. The CORBA spec defines the architecture of an ORB, whose job is to enable and regulate interoperability between objects and applications. This facility is part of a larger vision called the "Object Management Architecture" (OMA), which defines the OMG object model.
The OMA sets forth the OMG's vision of the complete distributed environment. Where the concern of the CORBA spec is solely the interaction of apps and objects, and the mechanisms that enable it, OMA defines a broad architecture of services and relationships within an environment, as well as the object and reference models (both of which are discussed later). As shown in Figure 1, the OMA is built upon the ORB services defined by CORBA, which provide the interaction model for the architecture. The environment is made richer with the addition of Object Services and Common Facilities, both of which are intended to serve as building blocks for assembling the frameworks within which distributed solutions are built.
The Object Services facility consists of a set of objects that perform fundamental operations. The OMG Object Services Task Force (OSTF) accepted the first stage of the Common Object Services Specification (COSS) volume 1, in November of 1993. This part of the specification covers life-cycle, naming, event, and persistence services. A request for proposal was issued during the summer of 1993 for the second stage of the specification (expected during the latter half of 1994), which defines relationships, externalization, transactions, and concurrency control. The OSTF envisions two additional stages beyond the second, which will address issues such as security, licensing, queries, and versioning. Stages 3 and 4 are called for in 1995 and 1996, respectively. As with all of the OMG specifications, Object Services are defined as "interfaces" expressed in the OMG's Interface Definition Language (IDL), which is discussed later. The specifications do not address implementation details. In this sense, a spec defines an abstraction that may have any number of unique implementations--a fact that is both philosophically satisfying and practically worrisome, and something OMG competitors have not overlooked.
Common Facilities (CF) are the newest area of effort by the OMG. The focus is on application-level functions, unlike CORBA and Object Services, which are low-level, fundamental capabilities. CF defines objects that provide key workgroup-support functions (such as printing, mail, database queries, bulletin boards and newsgroups, and compound documents). The OMG accords CF high priority, because they envision these services as comprising the layer most often utilized by developers who work in a distributed environment. In December 1993, the Common Facilities Task Force (CFTF) was formed to create three documents: the CF Architecture, the CF Roadmap, and the CF Request for Proposals (RFP). The architecture spec identifies and describes the primary groups of facilities required. The roadmap groups these categories according to importance, and schedules them for work. The OMG has charged the CFTF with producing an architecture that provides key services required by most applications, while leaving room for specialized vertical solutions. The CFTF anticipates releasing the first CF RFP sometime in 1994.
The OMG object model underlies the OMA and is described in the CORBA specification. The object model is a classical model in which clients send messages to servers, and in which a message identifies an object, with zero or more parameters in the request. The OMG model strictly separates interface from implementation. The model itself is concerned only with interfaces, to the extent that "interface" and "object type" are synonymous. This is largely a result of the need to define the interface between components independent of their implementation languages. In this case, "interface" means the methods that can be called on an object, together with the object's accessible attributes (those attributes intended to be retrievable by way of get/set methods). By defining these things, the developer is describing how the object appears to the ORB and to clients. In addition to a set of behaviors and attributes, an object must have an identity, in order that it can be referenced by an application.
In C++ programs, an object is identified by the unique memory address at which it resides. By contrast, in the OMG model, objects are identified by references. References are guaranteed to identify the same object each time the reference is used in a request. The specification is mostly silent on how references are implemented. All current ORB vendors implement them as objects that carry enough descriptive information that they are effectively unique. However, the OMG specifically states that references are not guaranteed to be unique. They chose not to define a Universal Unique Identifier (UUID) scheme in Version 1.1 of the specification because of concerns about management and interaction with legacy applications that have a different idea of an object ID. The lack of a universal means of "federating" (or making globally compatible) the names used to reference objects is a failing that the OMG intends to address in Version 2.0 of the specification.
Objects in the OMG model are created and destroyed dynamically in response to the issuance of requests. The specification does not define a method for the application to create and destroy objects. However, vendors such as IBM have augmented their implementations with this capability. Objects can also participate in any of the normal types of relationships, with perhaps the most important being subtype/supertype relationships or inheritance. Multiple inheritance is also permitted. "Inheritance," in this sense, is inheritance of interface only. There is no provision in the specification for implementation inheritance. Inheritance between object interfaces is specified syntactically by using the OMG's IDL. There is nothing to prevent the developer of a set of server objects from using implementation inheritance in the design of the servers, but the dependency is not made explicit in the Interface Definition syntax. The fact that a set of servers accessed through an interface hierarchy are also related by implementation inheritance is unknown to the ORB.
The OMG object-model specification makes no accommodation for polymorphism. In object-oriented systems, polymorphism (whose Greek roots mean "many forms") refers to being able to invoke the same method on a number of different objects (for example, invoking the "draw" method on both a pushbutton object and menu object). Implementing polymorphic behavior usually requires dynamic binding. Polymorphism is a key benefit of object technology. It allows the knowledge of specific implementations of an operation to reside with the server, rather than the client. The alternative (and the usual situation prior to OO) is to make the client aware of all specific implementations. Though the OMG model lacks polymorphism, implementors of CORBA-compliant products are free to provide it as part of a superset of capabilities.
The OMG model is strongly typed. As in C++, types are used to restrict and characterize operations. Unlike languages such as Smalltalk, types in the OMG model are not first-order objects, and cannot be manipulated as objects. The two primary categories of types in the object model are Basic types and Constructed types. Both of these types are used in declaring interface methods and accessible attributes. Basic types represent fundamental data types. These include integers (signed and unsigned, both short and long), floating-point numbers (in both 32-bit and 64-bit IEEE formats), ISO Latin-1 characters, Booleans, enums, strings, and a nonspecific type called "any." In addition, a special 8-bit datatype is defined that is guaranteed not to undergo conversion when transferred from one system to another. This type is sometimes called an "octet."
Constructed types are more-complex, higher-level entities. The most important of the constructed types is the Interface type, which specifies the set of operations that an instance of that type must support. An object is an instance of an interface type if it satisfies the set of operations defined on that type. An interface type, in turn, is satisfied by any reference to an object that satisfies the interface. Other types include Structs, Unions, Sequences, and Arrays. Structs are pure data structures which operate much like C++ structs. Likewise, Unions operate like C++ unions. Sequences are a variable-length array type that may contain any single type of object (including other sequences). Arrays are fixed-length arrays of a single type. Figure 2 shows the OMG type hierarchy.
The job of an ORB is to manage the interaction between clients and server objects. This includes all the responsibilities of a distributed computing system, from location and referencing of objects to the "marshaling" of request parameters and results. Marshaling refers to the process of translating and transferring parameters and results between machines, processes, and address spaces.
To provide all these capabilities the CORBA specification defines an architecture of interfaces that may be implemented in different ways by different vendors. The architecture was specifically designed to separate the concerns of interface and implementation. Figure 3 shows the architecture of an OMG ORB. The main components of the architecture may be divided into three specific groups: client side, implementation side, and ORB core. The client and implementation sides represent interfaces to the ORB.
The client-side architecture consists of three components: the Dynamic Invocation interface, the IDL stub interface, and the ORB services interface. In general, the stub interface consists of method "thunks" (small pieces of machine-language interface code), which are generated according to IDL interface definitions. These method thunks are linked into the client program. The thunks call out to the ORB, which passes the method call onto the server, as described later. The stubs represent a language mapping between the client language and the ORB implementation, allowing use by clients written in any language for which a mapping exists. There is currently an accepted mapping for C, and mappings for C++ and Smalltalk are planned. Most current vendors have provided a C++ mapping based on current proposals to the OMG. The use of the stub interface brings the ORB right into the application programmer's domain. The client interacts with server objects by invoking methods just as it would on local objects.
The Dynamic Invocation interface is a mechanism for specifying requests at run time. The dynamic interface is accessed by using a call to the ORB in which the interface typename, request, and parameters are specified. The client code is responsible for specifying the argument and return-value types. This information may come from an Interface Repository, which is discussed later. The information may also come from some other source. The details of which interface is used to invoke a method are not relevant to servers. The dynamic interface is necessary when the interface type cannot be known at compile time. Otherwise, it is better to use the stub interface, which is more efficient and typesafe. As with much of CORBA, the specification document says little about the specifics of the dynamic interface, and, in fact, is explicit in warning that the interface may vary widely across language mappings.
The last of the client-side interfaces are the ORB services. These are functions of the ORB that may be accessed directly by the client code. An example might be retrieving a reference to an object. The specific nature of these services is largely undefined by the specification.
One aspect of the client-side interface is shared by object implementations: the ORB services. The other two components on the implementation side are the IDL skeleton interface and the Object Adapter. The skeleton interface and the stub interface are examined in the discussion about IDL. In general, the skeleton interface is an up-call interface through which the ORB calls the method skeletons of the implementation, on a request by a client. Most of the functionality provided by the ORB to object implementations is provided through the IDL skeletons and the Object Adapter. The OMG expects only a few services to be common across all objects and accessed by way of the ORB core. Since the skeleton interface is, in fact, implemented on top of the Object Adapter, the focus here is on the Object Adapter.
The Object Adapter is the means by which server implementations access most of the services provided by the ORB. These services include generation and interpretation of object references, method invocation, security, activation (the process of locating an object's implementation and starting it running), mapping references to implementations, and object registration. The adapter actually exports three separate interfaces: a private interface to the skeletons, a private interface to the ORB core, and a public interface for use by implementations.
The CORBA specification is not explicit about what services an adapter must support, but it is clear that the adapter is intended to isolate object implementations from the ORB core as much as possible. The spec envisions a variety of adapters that provide services needed by specific kinds of objects. The most generic adapter described is the Basic Object Adapter (BOA). The BOA allows a variety of object-implementation schemes to be accommodated--from separate programs for each method, to separate programs for each object, to a shared implementation for all objects of a given type (the C++ model). The specification also describes adapters suited to objects stored in libraries and object-oriented databases.
As mentioned previously, interfaces to servers can be specified in an abstract, symbolic language. This interface representation predates CORBA and object-oriented systems. Since the early days of remote procedure call (RPC) systems, these languages have been known as "Interface Definition Languages" (IDLs). The purpose of an IDL is to allow the language-independent expression of interfaces, including the complete signatures (name, parameters, parameter and result types) of methods or functions, and the names and types of accessible attributes. This goal is achieved by way of a mapping between the IDL syntax and whatever language is used to implement client and server objects. Clients and servers need not be implemented using the same language, and, in fact, it is anticipated that they will not be. All that's needed is a mapping for both the client and server implementation languages.
CORBA IDL is a language with many constructs that resemble those in C++. In fact, the specification credits the Annotated C++ Reference Manual as the source for what eventually became the CORBA IDL specification. IDL obeys the same lexical rules as C++, while introducing a number of new keywords specific to the needs of a distributed system. Anyone familiar with C++ should have no trouble adapting to IDL. Writing interface definitions in IDL is quite a bit like writing class declarations in C++. Because IDL is designed purely for interface specification, it lacks the constructs of an implementation language, such as flow control, operators, and object definitions (used here in the C/C++ sense of allocating storage for a variable or object, as opposed to declaring its type). There is no concept of public and private parts of the interface declaration, since the notion of encapsulation is implicit in the separation of the IDL interface from the implementation.
Two interesting aspects of IDL are exceptions and modules. Exception declarations define a struct-like data structure with attributes that can be used to pass information about an exception condition to a service requestor. An exception is declared with an identifier (an exception name), which is accessible as a value when the exception is raised, allowing the client to determine which exception has been received. Members of the exception, if declared, are accessible to the client. An object uses the raises keyword to throw an exception. The name of the exception thrown must be in scope at the point where the raises expression is encountered. In addition to user-defined exceptions, the CORBA specification defines a number of standard exceptions for conditions such as a bad parameter, or a memory-allocation failure.
Modules extend the IDL scoping rules in a way similar to that of the C++ namespaces, which are a recent addition to the C++ language. The module keyword defines a nested scope within a file or within another module. By prepending an identifier with the module name and the :: operator, a program can specify that the identifier be searched for within the scope defined by the module. The goal of the module construct is to allow the encapsulation of namespaces so as to prevent name collisions with third-party libraries. In all other circumstances, IDL scope rules are very similar to those for C and C++.
Although this article cannot examine IDL in great detail, a simple interface definition is examined, and the relationship between the interface definition and the rest of the ORB architecture is described. Example 1 presents the IDL description of an interface to an object that represents a stock. Note that the declaration is roughly similar to a C structure declaration, with the struct keyword replaced by interface. At the top of the declaration are the exceptions raised by the implementation of this interface. The next three lines declare read-only attributes. Declaring an attribute in IDL is equivalent to declaring get/set methods for that attribute. Read-only attributes are effectively constant, and so only the get method skeletons will be generated for them by the IDL compiler. Finally, the example shows the buy() and sell() methods for the stock object. The buy() method takes two input parameters (identified by the in keyword), and raises the not_enough_cash exception. The sell() method takes a single input parameter and raises not_enough_shares. The syntax for declaring exceptions in IDL is similar to that in C++, except that the keyword is raises rather than throws.
Also shown in Example 1 is an interface derived from the Stock interface. Note that the syntax for specifying inheritance is similar to C++. One difference is that there is no "private" inheritance. Interface inheritance in CORBA IDL is public. All methods and attributes of the parent interface are accessible in the derived interface. In addition, inherited methods can be overridden in order to specialize the behavior for the derived interface. If you need explicit access to base interface methods or attributes, you can specify this by way of qualification, as in Stock::Buy(), again emulating the C++ usage. Multiple inheritance is legal in IDL. As in C++, there are rules for resolving ambiguities in cases where a base class is inherited more than once or a name is introduced by multiple base classes.
Once the interface to an object has been defined, it is mapped into the client and server languages by using an IDL compiler. The compiler produces stubs for the methods on the client side, and skeletons for the method implementations on the server side. If the implementation provides a C++ binding, the generated stubs are member functions of a class, which may inherit from a system-supplied base class that provides a private interface to the ORB. Keep in mind that any such binding--Iona's Orbix binding is an example--makes assumptions about what the standard C++ mapping will look like. A C++ mapping is expected to be finalized as part of Version 2.0 of the CORBA specification. An earlier mapping devised by Hyperdesk was accepted, but later withdrawn. When the accepted C language mapping is used, the method definitions produce external function declarations. The client simply invokes the linked-in stubs when a service is required. The request is forwarded to the ORB through the stub interface. If the object is activated, the ORB makes an up-call to the implementation through the generated method skeleton. If the object is not activated, the ORB first locates and activates it, then performs the up-call.
As an alternative to IDL, the CORBA spec devotes a couple of paragraphs to the idea of repositories for both interface and implementation definitions. On the interface side, the repository is intended to augment the dynamic invocation interface by providing persistent objects that represent information about a server's interface. By using an interface repository, it should be possible for a client to locate an object which was not known at compile time, query for the specifics of its interface, and then build a request to be forwarded through the ORB.
The implementation repository contains information that allows the ORB to locate and activate objects to fulfill dynamic requests. In addition to implementation information, the spec envisions this repository being used to contain other incidental information about an object (such as debugging info, versioning data, administrative data, and so on). The specification does not define the implementation of either repository, and so different vendors have gone their separate ways, as they have with CORBA Version 1.1.
The OMG has taken some flak for resembling other industry consortia that ultimately produced nothing substantive beyond many statements about open architectures and cooperation. In the case of the OMG, the comparison is unfair because the spec has garnered broad support. Many implementations are now available, and serious work is being undertaken to move the specification forward and address the shortcomings of Version 1.1. CORBA is now being taken very seriously by a number of large organizations that see it as the only viable technology truly headed in a cross-platform, nonproprietary direction. As a specification for an architecture, the model is rather versatile. Because CORBA is intended to be distributed technology, it is not necessarily efficient at utilizing objects located on a single processor (though it does not forestall such use).
The vision of the OMG is clearly cross-platform and cross-operating system. Is it a standard? Yes and no. Within the consortium, it is a standard description of an architecture. It is not a standard for implementation of that architecture, and it is not as well defined as it needs to be. The result is that each one of the many implementations of CORBA is effectively a proprietary product. There is currently no interoperability between ORBs, although various partnerships have been announced (one example being the SunSoft/Iona effort to make at least two of the implementations interoperate).
As a technology, CORBA is maturing rapidly. Various companies have produced tools that ease the building of distributed applications using CORBA. A number of training companies now offer hands-on courses in building such applications using one or more of the available implementations. Version 2.0 of the specification promises to address a number of critical issues necessary for interoperability. Any success at doing so will go a long way toward nudging CORBA a rung or two higher on the ladder of consideration. CORBA implementations are currently available for nearly all the major operating systems. If an organization is willing to bank on one implementation, real-world solutions can be--and are being--built now.
Figure 1 The object management architecture (OMA). Figure 2 The OMG type tree. Figure 3 The architecture of an object request broker.
interface Stock
{
exception not_enough_cash{ float amount_short };
exception not_enough_shares{ short shares_short };
readonly attribute string ticker;
readonly attribute string companyName;
readonly attribute float currentPrice;
void buy( in short numberOfShares, in float cash)
raises ( not_enough_cash );
void sell( in short numberOfShares )
raises ( not_enough_shares );
};
interface BlueChipStock : Stock
{
// ...
};
Copyright © 1994, Dr. Dobb's Journal