On the first day, you learned about CORBA's history and saw how the CORBA architecture fits into the world of client/server application development. You were also presented with a brief overview of the CORBA architecture. By the end of this Day, you will have a deeper understanding of the CORBA architecture and its components. These are the major aspects covered in this chapter:
As one might guess, a fundamental part of the Common Object Request Broker architecture
is the Object Request Broker (ORB). The concept of an ORB is this:
When an application component wants to use a service provided by another component,
it first must obtain an object reference for the object providing that service. (How
this object reference is obtained is an issue in its own right--and will be discussed
later--but for the purposes of studying the ORB mechanism, assume for the time being
that the object reference is already available.) After an object reference is obtained,
the component can call methods on that object, thus accessing the desired services
provided by that object. (The developer of the client component knows at compile
time which methods are available from a particular server object.) The primary responsibility
of the ORB is to resolve requests for object references, enabling application components
to establish connectivity with each other. (See Figure 2.1 for an illustration of
these ORB concepts.) As you will see, the ORB has other responsibilities as well.
Figure 2.1. ORB resolution of object requests.
After an application component has obtained a reference to an object whose services
the component wants to use, that component can invoke methods of that object. Generally,
these methods take parameters as input and return other parameters as output. Another
responsibility of the ORB is to receive the input parameters from the component that
is calling the method and to marshal these parameters. What this means is
that the ORB translates the parameters into a format that can be transmitted across
the network to the remote object. (This is sometimes referred to as an on-the-wire
format.) The ORB also unmarshals the returned parameters, converting them
from the on-the-wire format into a format that the calling component understands.
The marshaling process can be seen in Figure 2.2.
Figure 2.2. Marshaling parameters and return values.
New Term: Marhsaling refers to the process of translating input parameters to a format that can be transmitted across a network.
Unmarshaling is the reverse of marshaling; this process converts data from the network to output parameters.
An On-the-wire format specifies the format in which data is transmitted across the network for the marshaling and unmarshaling processes.
The entire marshaling process takes place without any programmer intervention whatsoever. A client application simply invokes the desired remote method--which has the appearance of being a local method, as far as the client is concerned--and a result is returned (or an exception is raised), again, just as would happen with a local method. The entire process of marshaling input parameters, initiating the method invocation on the server, and unmarshaling the return parameters is performed automatically and transparently by the ORB.
A product of the marshaling/unmarshaling process is that, because parameters are converted upon transmission into a platform-independent format (the on-the-wire format is provided as part of the CORBA specification) and converted into a platform-specific format upon reception, the communication between components is platform-independent. This means that a client running on, for instance, a Macintosh system can invoke methods on a server running on a UNIX system. In addition to independence of operating system used, differences in hardware (such as processor byte ordering, or endianness) are also rendered irrelevant because the ORB automatically makes these conversions as necessary. In essence, any differences in platforms--be it operating system, endianness, word size, and so on--are accounted for by the ORB.
Note again that the process of marshaling and unmarshaling parameters is handled completely by the ORB, entirely transparent to both the client and server. Because the entire process is handled by the ORB, the developer need not concern himself with the details of the mechanism by which the parameters are marshaled and unmarshaled.
Because the concept of the ORB is central to an understanding of the CORBA architecture, it is important to make sure that you grasp the ORB concepts. To summarize the purpose of the ORB, its responsibilities are as follows:
The major benefit offered by the ORB is its platform-independent treatment of data; parameters can be converted on-the-fly between varying machine formats as they are marshaled and unmarshaled.
If the concept of the Object Request Broker is one cornerstone of the CORBA architecture, the Interface Definition Language (IDL) is the other. IDL, as its name suggests, is the language used to define interfaces between application components. Note that IDL is not a procedural language; it can define only interfaces, not implementations. C++ programmers can think of IDL definitions as analogous to header files for classes; a header file typically does not contain any implementation of a class but rather describes that class's interface. Java programmers might liken IDL definitions to definitions of Java interfaces; again, only the interface is described--no implementation is provided.
New Term: The Interface Definition Language (IDL) is a standard language used to define the interfaces used by CORBA objects. It is covered in great detail on Day 3.
The IDL specification is responsible for ensuring that data is properly exchanged between dissimilar languages. For example, the IDL long type is a 32-bit signed integer quantity, which can map to a C++ long (depending on the platform) or to a Java int. It is the responsibility of the IDL specification--and the IDL compilers that implement it--to define such data types in a language-independent way.
IDL will be covered in great detail in the next chapter. After that, you will use IDL to--what else?--define interfaces for the examples used throughout this book.
The IDL language is part of the standard CORBA specification and is independent of any programming language. It achieves this language independence through the concept of a language mapping. The OMG has defined a number of standard language mappings for many popular languages, including C, C++, COBOL, Java, and Smalltalk. Mappings for other languages exist as well; these mappings are either nonstandard or are in the process of being standardized by the OMG.
New Term: A language mapping is a specification that maps IDL language constructs to the constructs of a particular programming language. For example, in the C++ language mapping, the IDL interface maps to a C++ class.
Language independence is a very important feature of the CORBA architecture. Because CORBA does not dictate a particular language to use, it gives application developers the freedom to choose the language that best suits the needs of their applications. Taking this freedom one step further, developers can also choose multiple languages for various components of an application. For instance, the client components of an application might be implemented in Java, which ensures that the clients can run on virtually any type of machine. The server components of that application might be implemented in C++ for high performance. CORBA makes possible the communication between these various components.
In order to understand CORBA, you must first understand its role in a network of computing systems. Typically, a computer network consists of systems that are physically connected (although the advent of wireless network technology might force us to revise our understanding of what "physically connected" means). This physical layer provides the medium through which communication can take place, whether that medium is a telephone line, a fiber-optic cable, a satellite uplink, or any combination of networking technologies.
Somewhere above the physical layer lies the transport layer, which involves protocols responsible for moving packets of data from one point to another. In this age of the Internet, perhaps the most common transport protocol in use is TCP/IP (Transmission Control Protocol/Internet Protocol). Most Internet-based applications use TCP/IP to communicate with each other, including applications based on FTP (File Transfer Protocol), Telnet (a host communication protocol), and HTTP (Hypertext Transport Protocol, the basis for the World Wide Web).
So how does CORBA fit into this networking model? It turns out that the CORBA specification is neutral with respect to network protocols; the CORBA standard specifies what is known as the General Inter-ORB Protocol (GIOP), which specifies, on a high level, a standard for communication between various CORBA ORBs and components. GIOP, as its name suggests, is only a general protocol; the CORBA standard also specifies additional protocols that specialize GIOP to use a particular transport protocol. For instance, GIOP-based protocols exist for TCP/IP and DCE (the Open Software Foundation's Distributed Computing Environment protocol). Additionally, vendors can (and do) define and use proprietary protocols for communication between CORBA components.
New Term: The General Inter-ORB Protocol (GIOP) is a high-level standard protocol for communication between ORBs. Because GIOP is a generalized protocol, it is not used directly; instead, it is specialized by a particular protocol that would then be used directly.
For discussion and use of CORBA in this book, your main interest will be the GIOP-based protocol for TCP/IP networks, known as the Internet Inter-ORB Protocol (IIOP). As of the 2.0 version of the CORBA specification, vendors are required to implement the IIOP protocol in order to be considered CORBA-compliant (although they might offer their proprietary protocols in addition to IIOP). This requirement helps to ensure interoperability between CORBA products from different vendors because each CORBA 2.0-compliant product must be able to speak the same language. Some vendors have gone so far as to adopt IIOP as their products' native protocol (the protocol used by default) rather than use a proprietary protocol; however, an ORB is allowed to support any number of protocols, as long as IIOP is supported (when communicating with each other, ORBs can negotiate which protocol to use). Additionally, a number of vendors are including IIOP-compliant ORBs with products ranging from database servers to application development tools to Web browsers. IIOP, as you can see, is an important key to CORBA interoperability.
New Term: The Internet Inter-ORB Protocol (IIOP) is a specialization of the GIOP. IIOP is the standard protocol for communication between ORBs on TCP/IP based networks. An ORB must support IIOP (but can support other additional protocols) in order to be considered CORBA 2.0-compliant.
With all this discussion of inter-ORB protocols, you have yet to see where CORBA
fits in with the rest of the networking model. Figure 2.3 illustrates the network
architecture of a typical CORBA application. Essentially, CORBA applications are
built on top of GIOP-derived protocols such as IIOP. These protocols, in turn, rest
on top of TCP/IP, DCE, or whatever underlying transport protocol the network uses.
CORBA applications aren't limited to using only one of these protocols; an application
architecture can be designed to use a bridge that would interconnect, for instance,
DCE-based application components with IIOP-based ones. You can see, then, that rather
than supplant network transport protocols, the CORBA architecture creates another
layer--the inter-ORB protocol layer--which uses the underlying transport layer as
its foundation. This, too, is a key to interoperability between CORBA applications,
as CORBA does not dictate the use of a particular network transport protocol.
Figure 2.3. Architecture of a distributed CORBA application.
Every object-oriented architecture features an object model, which describes how objects are represented in the system. Of course, CORBA, being an object-oriented architecture, has an object model as well. Because CORBA is a distributed architecture, however, its object model probably differs somewhat from the traditional object models with which most readers are familiar (such as C++'s or Java's object model). Three of the major differences between the CORBA object model and traditional models lie in CORBA's "semi-transparent" support for object distribution, its treatment of object references, and its use of what are called object adapters--particularly the Basic Object Adapter (BOA). You will now explore these concepts in greater depth.
To a CORBA client, a remote method call looks exactly like a local method call, thanks to the use of client stubs (a concept you'll explore later in this chapter). Thus, the distributed nature of CORBA objects is transparent to the users of those objects; the clients are unaware that they are actually dealing with objects which are distributed on a network.
Actually, the preceding statement is almost true. Because object distribution brings with it more potential for failure (due to a network outage, server crash, and so on), CORBA must offer a contingency to handle such possibilities. It does so by offering a set of system exceptions, which can be raised by any remote method. You'll learn about exceptions more in later chapters--on Day 3, you'll see how exceptions are declared in IDL; on Day 7, you'll add exception handling to a sample application. For the time being, though, all you need to know is that all operations in all CORBA objects implicitly can raise a CORBA system exception, which signals a network error, server unavailability, or other such situation. Thus, with the exception--pun intended--of this additional exception raised by CORBA object methods, a remote method is otherwise identical to its local counterpart.
In a distributed application, there are two possible methods for one application component to obtain access to an object in another process. One method is known as passing by reference, illustrated in Figure 2.4. In this method, the first process, Process A, passes an object reference to the second process, Process B. When Process B invokes a method on that object, the method is executed by Process A because that process owns the object. (The object exists in the memory and process space of Process A.) Process B only has visibility to the object (through the object reference), and thus can only request that Process A execute methods on Process B's behalf. Passing an object by reference means that a process grants visibility of one of its objects to another process while retaining ownership of that object.
New Term: When an object is passed by reference, the object itself
remains "in place" while an object reference for that object is passed.
Operations on the object through the object reference are actually processed by the
Figure 2.4. Passing an object by reference.
The second method of passing an object between application components is known as passing by value and is depicted in Figure 2.5. In this method, the actual state of the object (such as the values of its member variables) is passed to the requesting component (typically through a process known as serialization). When methods of the object are invoked by Process B, they are executed by Process B instead of Process A, where the original object resides. Furthermore, because the object is passed by value, the state of the original object is not changed; only the copy (now owned by Process B) is modified. Generally, it is the responsibility of the developer to write the code that serializes and deserializes objects (although this capability is built into some languages, such as Java).
New Term: When an object is passed by value, the object's state is copied and passed to its destination, where a new copy of the object is instantiated. Operations on that object's copy are processed by the copy, not by the original object.
Serialization refers to the encoding of an object's state into a stream,
such as a disk file or network connection. When an object is serialized, it can be
written to such a stream and subsequently read and deserialized, a process
that converts the serialized data containing the object's state back into an instance
of the object.
Figure 2.5. Passing an object by value.
One important aspect of the CORBA object model is that all objects are passed by reference. (Actually, at the time of this writing, the OMG has issued an RFP (Request for Proposals) for adding to CORBA the capability to pass objects by value, so it is likely that this capability will be added to the CORBA standard in the near future.) In order to facilitate passing objects by value in a distributed application, in addition to passing the state of the object across the network, it is also necessary to ensure that the component receiving the object has implementations for the methods supported by that object. (This is not necessary when objects are passed by reference; recall that method invocations are executed by the component that owns the actual object.) When the CORBA pass-by-value capability is specified, it will need to address these issues; readers should stay tuned to OMG announcements for updates on this development. (The OMG Web site, which makes available a great deal of CORBA-related information and specifications, is located at http://www.omg.org/.)
There are a few issues associated with passing objects by reference only. Remember that when passing by reference is the only option, methods invoked on an object are always executed by the component that owns that object (in other words, the component that has created that object); an object cannot migrate from one application component to another. (However, you can devise methods that simulate this behavior; it simply is not provided by the CORBA architecture itself at this time.) This also means that all method calls are remote method calls (unless both the calling object and called object are owned by the same application component). Obviously, if a component invokes a lengthy series of method calls on a remote object, a great deal of overhead can be consumed by the communication between the two components. For this reason, it might be more efficient to pass an object by value so the component using that object can manipulate it locally. On Day 10 you'll explore this issue in greater detail, but in the meantime, readers should be aware that CORBA's current lack of pass-by-value semantics does raise this issue.
The CORBA standard describes a number of what are called object adapters, whose primary purpose is to interface an object's implementation with its ORB. The OMG recommends that new object adapter types be created only when necessary and provides three sample object adapters: the Basic Object Adapter (BOA), which you will concentrate on, and the Library Object Adapter and Object-Oriented Database Adapter, both of which are useful for accessing objects in persistent storage. (The CORBA specification describes these object adapters in greater detail.) Again, you will concern yourself only with the Basic Object Adapter, by far the most commonly used object adapter.
The BOA provides CORBA objects with a common set of methods for accessing ORB functions. These functions range from user authentication to object activation to object persistence. The BOA is, in effect, the CORBA object's interface to the ORB. According to the CORBA specification, the BOA should be available in every ORB implementation, and this seems to be the case with most (if not all) CORBA products available.
One particularly important (and useful) feature of the BOA is its object activation and deactivation capability. The BOA supports four types of activation policies, which indicate how application components are to be initialized. These activation policies include the following:
New Term: A server activation policy indicates how that particular server is intended to be accessed; for example, if there is a single server used by all clients, or a new instance of the server should be started for each client, and so on.
This variety of activation policies allows an application architect to choose the type of behavior that makes the most sense for a particular type of server. For instance, a server requiring a length of time to initialize itself might work best as a persistent server, because the necessary initialization time would adversely affect the response time for that server. On the other hand, a server that starts up quickly upon demand might work well with the server-per-method policy.
Note:It is worth noting here that the term persistent server has nothing to do with the common use of the term persistent, which refers to the capability of an object to store its state in some sort of nonvolatile storage facility such as a database of disk files. A persistent server does not necessarily store its state in persistent storage (although it could); in this case, the term merely implies that the server runs persistently or, in other words, continuously.
Traditionally, in a client/server application, the server is the component, or components, that provides services to other components of the application. A client is a component that consumes services provided by a server or servers. The architecture of a CORBA application is no different; generally, certain components of an application provide services that are used by other components of the application. Not surprisingly, the general terms client and server refer to these components of a CORBA application. When considering a single remote method invocation, however, the roles of client and server can be temporarily reversed because a CORBA object can participate in multiple interactions simultaneously.
In a CORBA application, any component that provides an implementation for an object is considered a server, at least where that object is concerned. If a component creates an object and provides other components with visibility to that object (in other words, allows other components to obtain references to that object), that component acts as a server for that object; any requests made on that object by other components will be processed by the component that created the object. Being a CORBA server means that the component (the server) executes methods for a particular object on behalf of other components (the clients).
Frequently, an application component can provide services to other application
components while accessing services from other components. In this case, the component
is acting as a client of one component and as a server to the other components (see
Figure 2.6). In fact, two components can simultaneously act as clients and servers
to each other. To understand this situation, consider the following scenario (illustrated
in Figure 2.7): The first component, Component A, receives a reference to an object
created by a second component, Component B, and calls a method on that object. Here,
Component A acts as a client and Component B acts as a server. Now assume that as
a parameter of the method called, Component A passes a reference to an object that
it has created (and thus provides an implementation for the object). Assume further
that Component B now calls some method on that object. For this particular method
invocation, Component A acts as a server, whereas Component B acts as a client. The
two components have not changed their overall roles in the application, but they
have temporarily reversed their roles as client and server. Therefore, from this
example you see that in a CORBA application, the terms client and server
might depend on the context of the method being called and in which component that
method's object resides.
Figure 2.6. Acting as a client and a server.
One last point to consider in the terminology of clients and servers: Although an application component can function as both a client and a server, it is nevertheless typical to label such a component as one or the other (not both). In the preceding example, assume that for the most part, Component A calls methods on objects owned by Component B. As illustrated in the example, some (or even all) of these method calls can pass object references to Component B, which can then make calls through those object references back to Component A. Although Component A is acting as a server for these method calls, because the overall function of the component is to use services provided by Component B, and only provides objects as arguments to methods in Component B, you might very well refer to Component A as the client and to Component B as the server. Methods called in this way are generally referred to as client callback methods, or simply callbacks. Callbacks are especially important given CORBA's current lack of pass-by-value capability; the capability to pass objects by value, when it becomes available, will eliminate the need for many callbacks.
New Term: Client callback method, or simply callback,
is a generic term given to a method that is implemented by a client and called by
a server. Callbacks essentially make a client
Figure 2.7. A client callback method.
After a developer creates component interface definitions using IDL, he or she processes the resulting IDL files with an IDL compiler. The IDL compiler generates what are known as client stubs and server skeletons. Client stubs and server skeletons serve as a sort of "glue" that connects language-independent IDL interface specifications to language-specific implementation code. Client stubs for each interface are provided for inclusion with clients that use those interfaces. The client stub for a particular interface provides a dummy implementation for each of the methods in that interface. Rather than execute the server functionality, however, the client stub methods simply communicate with the ORB to marshal and unmarshal parameters.
New Term: A client stub, which is generated by the IDL compiler, is a small piece of code that makes a particular CORBA server interface available to a client.
A server skeleton, also generated by the IDL compiler, is a piece of code that provides the "framework" on which the server implementation code for a particular interface is built.
On the other side, you have server skeletons, providing the framework upon which
the server is built. For each method of an interface, the IDL compiler generates
an empty method in the server skeleton. The developer then provides an implementation
for each of these methods. Figure 2.8 illustrates how client stubs and server skeletons
fit into a CORBA application.
Figure 2.8. Client stubs and server skeletons.
You will study the process of building a CORBA client and server in detail on Day 4. There you will find how to use the IDL compiler, how to build a CORBA client using the client stubs generated by the IDL compiler, and how to build a CORBA server, starting from the server skeletons also generated by the IDL compiler. Eventually, you will see that you can build CORBA clients without using client stubs at all, using what is known as the Dynamic Invocation Interface (DII). Rather than being statically linked to server interfaces, such clients can discover server interfaces dynamically and use services not even conceived of at the time the clients were built. (However, using the DII significantly increases the complexity of a client application and is probably best left for a certain niche of applications.) Because the Dynamic Invocation Interface is considered an advanced topic, you won't be seeing any more of it until Day 11.
Certainly, much can be accomplished using just the basics of CORBA: using IDL to create component interfaces, then implementing those interfaces and developing clients to exploit the services provided. However, the Object Management Architecture (which you'll recall is the Object Management group's overall architecture which includes CORBA) provides much more than the basic ORB capabilities in the form of CORBAservices and CORBAfacilities. These capabilities include event management, licensing, object persistence, naming, security, transactions, user interface management, data interchange, and much more. The interfaces for using these capabilities are standardized by the OMG, meaning that their usage is (or will be) consistent across platforms and products. What's more, the interfaces for CORBAservices and CORBAfacilities are specified in IDL, meaning that applications can use these services just as they use any other CORBA objects.
You will examine the CORBAservices and CORBAfacilities, both present and future, on Day 12. For the time being, you should be aware that there is a difference between what services and facilities are specified by the OMG and what services and facilities are available in various CORBA products. Before deciding to use a particular service or facility in an application design, you should first ensure that a product actually exists that implements that functionality. Also note that in order to be considered CORBA 2.0-compliant, a product need not implement any of the CORBAservices or CORBAfacilities; only the CORBA core functionality is required.
In this chapter, you first discovered the two cornerstones of the CORBA architecture: the Object Request Broker (ORB), which manages the communication of CORBA objects with each other, and the Interface Definition Language (IDL), which defines application component interfaces upon which CORBA applications are built. You explored the CORBA object model, where you learned about inter-ORB protocols (particularly IIOP), CORBA's use of object references, and the concept of the Basic Object Adapter. You defined the terms client and server in the context of CORBA and saw that a single application component can simultaneously act as both a client and a server. You also saw how IDL definitions create client stubs and server skeletons, which in turn implement CORBA clients and servers. Finally, you were introduced to CORBAservices and CORBAfacilities, which provide additional functionality for CORBA applications.
Now that you have developed an understanding of the overall CORBA architecture, you will move on to the basics of IDL, starting with simple data types and working up to more complex IDL constructs. You will find this knowledge of IDL necessary to design and implement CORBA applications.
The following section will help you test your comprehension of the material presented today and put what you've learned into practice. You'll find the answers to the quiz in Appendix A. On most days, a few exercises will accompany the quiz; today, because no real "working knowledge" material was presented, there are no exercises.