Networking Objects with CORBA

Component objects meet client/server

Mark Betz

Mark is director of object technologies at Block Financial's technology center, where he works on distributed multimedia information systems. He can be contacted at mbetz@conductor.com.


The evolution of object and network technologies is bearing fruit in the form of broader system interconnectivity and more-modular application architectures. Central to this evolutionary process is distributed object computing (DOC), a model for application development that promises to revolutionize how systems are conceived and implemented. Much has been written about DOC over the last couple of years (see, for instance, Dr. Dobb's Special Report on Interoperable Objects, Winter 1994/95), but only recently have developers made use of DOC-based tools such as NeXT's Portable Distributed Objects (PDO), IBM's System Object Model (SOM), and Iona's Orbix.(The latter two are implementations of the Object Management Group's Common Object Request Broker Architecture--CORBA--specification.) Before long, we're told, Microsoft will make available distributed versions of its Component Object Model (COM) technology. Still, the benefits of distributed-object computing are not clearly understood. Consequently, in this article I'll present a CORBA-based architecture designed to support a typical business--a virtual bookshop. This project not only demonstrates the technologies underlying distributed objects, but also the Internet technologies with which DOC must work to reach a reasonable number of users.

Figure 1 is a model of how a business like a virtual bookshop might operate. The shop is "located" in a cluster of host machines attached to the Internet backbone through a dedicated line to a local service provider. By virtue of the Internet's IP packet-switching protocol, each of these hosts is uniquely addressable among all other connected machines worldwide. Customers "arrive" at the shop by connecting to the Internet and executing an interface application. One benefit of using DOC as the underlying architecture of the business is that your services can have various interfaces. Some clients might use an HTML browser, while others might run an application that uses distributed binary objects directly. You might even have to provide a shell interface suitable for a terminal connection. The architecture accommodates all these modes of access, as well as those that may be required in the future. How will customers pay for their purchases? There are currently a number of proposals for secure, electronic financial transactions, including "DigiCash," "CyberCash," and "NetCash." In this discussion, I'll assume that security technology exists to allow credit-card numbers to be transmitted in privacy.

A DOC Backgrounder

Before diving into the bookshop's architecture and connection model, it's important to consider just what objects are being distributed and how they support the business. Figure 2 illustrates a simple object model. Obviously, to track what you have and what you've sold, you need a Book class. This would be best derived from a more generalized inventory item class, but I'll ignore good object modeling in order to focus on the system architecture. (Modeling a distributed system in objects is a discipline that certainly deserves its own article.) In addition to Book, the domain layer of the application contains Customer, CreditAuth, Order, Service, and Session objects. The Customer and Order classes are self-descriptive. CreditAuth encapsulates a connection to a credit-authorization network to which credit-card purchases are submitted for approval. Service is a class of objects that lets clients send you e-mail, browse a company directory, and perform other customer-service-related tasks. Session is attached to a Customer object on entry to the system; it captures the state of the system for one user over one connection and can be used to allow restart in cases of disconnection, for example.

Clearly, a number of databases are also necessary, at least for inventory, customer, and order information. There will probably be an e-mail database and a short-term-storage database for session information. Whether you think of the distributed objects as interfaces to the databases or the databases as persistence for the distributed objects doesn't much matter. The important point is that the technology allows you a great deal of freedom in choosing these components. When you're done, clients won't depend on the database architecture, the networking model, the platforms used to support the implementation, or even the interfaces of the distributed objects themselves. This is the key benefit of DOC technology--it takes the core object-oriented concept of encapsulation to its logical conclusion. Where object-oriented languages allow encapsulation of state and behavior at the syntax level, DOC allows physical encapsulation. When an object is linked into an application at the binary level, it brings with it a host of dependencies that make it difficult to reuse. The monolithic applications created on a binary-linkage model become littered with these dependencies and grow more difficult to maintain and extend.

By contrast, a distributed object (the Customer class, for example) is implemented once in a running process that is developed and maintained locally, but available globally. It is available via a call-level synchronous interface in which calls to remote resources look exactly like calls to local objects and procedures. The actual implementation details are not complex from a system-programming perspective, and the end result is well worth it: Application developers can be presented with neatly packaged components that export simple interfaces to distributed services. This borders upon the self-supporting community of component developers and utilizers that has been the promise of object technology since its beginning. The immediate benefit is simpler, more-robust applications. Rather than encompassing all of the business rules within its own source, an application evolves into a controller and coordinator of distributed objects. To achieve the benefits of this model and apply it to the sample business, you need two key facilities--networking and object distribution.

TCP/IP Networking

It would be difficult to overstate the importance of TCP/IP in making the vision I've described a matter of routine system development. Originally the language of interconnection between the nation's large civilian and military research and engineering institutions, the TCP/IP protocol has exploded in usage along with the Internet. The catalyst behind this expansion has been the development of efficient serial-line protocols that carry TCP/IP traffic, enabling low-cost dial-up connections to the net. Principle among these is Point-to-Point Protocol (PPP). Major operating-system vendors (okay, Microsoft) now standardly include a TCP/IP/PPP stack in their system-software suite: Every copy of Windows 95, Windows NT, and OS/2 ships with TCP/IP. UNIX vendors have been including TCP/IP with their systems since the University of California, Berkeley added it to BSD (along with a raft of nifty utilities and the idea of a "socket") in 1983 or so. I won't go into details of TCP/IP design or operation beyond what affects the application model; suffice it to say that it is an efficient protocol for wide-area interconnectivity of a variety of small systems connected to a variety of networks.

Perhaps the most significant recent development involving TCP/IP is the stampede of major commercial online services to implement it over their backbones. Nearly every major service now operates PPP servers and allows TCP/IP traffic for their customers with dial-up accounts. CompuServe's WinCIM access software now runs on top of WinSock, the Windows sockets specification, and can be used to access CompuServe over an Internet PPP connection. Conversely, users can access Internet addresses over CompuServe dial-up nodes, all of which can now access the provider's PPP servers. Recently, Compu-Serve announced it was ditching the venerable "77777,7777" address form (based on an old octal format) for a user alias of the form user@compuserve.com. Other services such as Prodigy and America Online have reacted similarly; see Figure 3. We'll likely see a day when every online system in the world will be addressable from every other online system. Fortunately, in addition to being the lingua franca of modern networking, TCP/IP is also the preferred language of most Object Request Brokers, the central component of a distributed-object computing technology.

IP packet switching allows you to uniquely address and send packets to literally millions of machines. TCP provides a safe, stream-oriented interface which packetizes a flow of data, delivers it to IP for transport to the remote system, and there reassembles the stream, guaranteeing packet order and integrity. Pretty impressive technology, but the devil is in the details--what's in all those little packets of data? TCP/IP is a network-level protocol, actually a set of protocols called a "stack." When an application uses TCP/IP to deliver data to a remote machine, it has to decide what to put in on one end, and what it all means on the other. This constitutes a requirement for an application-level protocol. Early Internet applications were e-mail and file-copy utilities, each supported by its own protocol. E-mail and file transfer remain the Internet's primary uses, but other application protocols have emerged. A recent addition is the HyperText Transport Protocol (HTTP), the river that carries the World Wide Web. This protocol uses TCP/IP to transmit hypertext HTML documents rendered by a browser.

RPC and IDL

Application-level protocols are a bother to develop: The task is time consuming and difficult to standardize from one application to another. Early developers of network apps already used a standard protocol for communication between the various parts of every application--an API. When one part of a program needs a service, it calls a procedure located in another part of the program. In a sense, the formal argument list and return value of the procedure specify a microprotocol that the compiler and processor collaborate to implement for us. To achieve a more fully distributed application model, you simply move some of those procedures to other machines on the net, where they can be maintained by people who understand them and used by those who need them. Why not have a call-level interface that spans a network connection? This is just the effect that developers of early Remote Procedure Call (RPC) models achieved. RPC models implement another layer of abstraction on top of the network interface. When a client executes a procedure, the call is directed across the net to a server on a host, and the results are returned. As Figure 4 shows, an RPC model allows you to view the distributed call stack exactly as you would a local call stack.

Developers of RPC models also strove for platform and language independence. To be truly useful as a development paradigm, RPC mechanisms had to function in heterogeneous environments where different languages were used to implement systems on various platforms. This required two key facilities: a language-independent means of describing a procedure and a system-level facility for handling differences in data representation. The first requirement is satisfied by Interface Definition Language (IDL), a declarative syntax used to describe procedures, their formal arguments, and their return types. A compiler translates the IDL declarations into client and server source in a particular language, which might be different for each. IDL is implementation neutral and can be translated into any language that includes the concept of a call or method invocation. The second requirement is satisfied by "marshaling," a system-software process that handles seamless data representation, conversion of arguments, and results in terms of word width, endianness, and the like.

What object orientation did for procedural models of application development, distributed objects do for RPC mechanisms. Instead of describing procedures, CORBA IDL describes objects in terms of "interfaces"--named sets of attributes and methods similar in appearance and syntax to C++ classes. Unlike C++, IDL has no syntax that implies implementation--no pointers, flow-control constructs, or anything that reserves storage in any sense. An interface is exactly that--a set of method names with their formal argument lists and return types. Even IDL attributes are simply the implication of Get/Set methods for a named value. If you're switching back and forth between C++ and IDL concepts, consider interfaces and classes roughly synonymous within each context. In addition to interfaces, IDL allows the declaration of supporting types, including structs, enums, exceptions, strings, and a type of container called a sequence. IDL is translated into an implementation language by an IDL compiler which has a C-compatible preprocessing phase, so C preprocessor constructs such as comments and # directives (such as #define) are also supported. Example 1 is an IDL interface for the Book class that illustrates some of these concepts.

Once run through an IDL compiler, the interface and type declarations are translated into corresponding language elements. For the remainder of this article, I'll use C++ as an implementation language, though mappings for C and Smalltalk are also in use. C++ interfaces are translated into several classes, while enums, structs, unions, arrays, and strings are translated into their C++ counterparts. In the case of strings, the translation is to char*, though with the acceptance of a string class as part of the ANSI C++ standard, it's reasonable to expect its use in the future. In addition to the translation of the IDL classes, a fair amount of source code is generated to support various parts of the CORBA architecture. For a given interface compilation, the result is a common header file, containing the classes and supporting types, and two source files, one each for the client and server executables. By convention, the header file name has the form interface.hh. The two source files, which contain implementations of the interface classes and supporting code, are named interfaceC.cpp and interfaceS.cpp for the client and server, respectively.

The IDL class generated for use on the client side is known as a "proxy." It declares virtual functions that correspond to the methods in the IDL interface from which it was compiled. The client-side source code implements these functions as stubs that make calls to the remote server, in a model very similar to RPC mechanisms. The client proxy classes are all behavior and have no size. Clients can use these classes directly, although it is better to layer in a wrapper class on top of them for several reasons: First, while the C++ IDL mapping is now an accepted part of the CORBA standard, no available compilers conform to it 100 percent. Consequently, it is valuable to isolate client code from the specifics of a given mapping. More importantly, the life cycle of a client proxy is not exactly that of a local C++ class. Clients declare a proxy class pointer, then assign it the return value of a function called _bind, a static method of the proxy class. The binding associates the proxy with a remote-server process running on some host. Once _bind successfully returns, the client can make calls on the proxy as if it were local. In short, the wrapper class hides _bind, server and host selection, and CORBA exception handling from the client code. I've found it useful to create Windows DLLs that export a wrapper-class API. Other interfaces based on this architecture (VBX and OCX interfaces, for instance) are also possible.

Implementing Component Objects

The implementation side is more complicated.

Along with the client proxy, the IDL compilation process produces a class whose form is derived from a section of the CORBA specification that describes the Basic Object Adapter (BOA)--a set of services that connect an implementation to the ORB and help it handle requests. The BOA's most prominent feature is the implementation parent class (the BOAImpl class in Orbix, for instance), which is similar to the client proxy from which it is derived. However, each virtual function in this class is pure virtual, so the class is not instantiable. Rather, it is meant to be used as a derivation point for the implementation class. To provide an implementation of an IDL interface, you derive a class from BOAImpl and implement each of the virtual functions. These functions might simply return a value, or they might call down to an SQL-database access layer such as DBLibrary or connect to another remote resource. Because the object server is a process, it needs a main() (or WinMain(), as the case may be) in which calls can be made to initialize the server and the ORB run-time libraries linked into it.

The run-time libraries, which are different for the client and server, provide support for the proxy and implementation classes. On the client side, they marshal the parameters to a call, establish a TCP/IP connection to a server, and transmit the call and parameters. The server library receives the call, unmarshals the arguments, and uses a dispatch class to match the name of the method to an entry point in the implementation class. This entry is then called; when it returns, any return value follows a similar route back to the client.

Two other components of the system need to be visited. The first is a set of services--including server startup and termination--that form the ORB core within the CORBA specification. How they are provided is up to the implementor. Iona's Orbix provides a daemon process that runs on server and name-lookup hosts, but is not needed on client machines.

The second component is the locator facility. CORBA defines interfaces by name, just as C++ defines classes by name. Interfaces are implemented in servers that run on hosts. Hosts have unique names within a network, and servers are uniquely named within a host. The locator service takes the name of a server and returns a list of hosts that run it. It is up to the client to decide which host to request a server binding from. Clients can be hardwired to a specific host, but use of the locator facility grants clients complete independence from the back-end host/server structure.

Designing the Virtual Bookshop

You can approach the architectural design of the virtual bookshop at three levels: the distributed-object model, the composition of the objects into server processes and the client/server architecture, and the host architecture on which the servers run.

A CORBA object server may implement any number of interfaces. In this model, I'll use one server process per object type. In reality, you might group related objects into one server for efficiency. The architecture of the servers is more or less set by the CORBA implementation: a main entry point, the interface implementation classes, the ORB run-time libraries, the socket DLL, the TCP/IP stack, and potentially connection layers for other resources such as databases. On the client side, I'll use proxy wrapper layering--one proxy and one wrapper for each object in the model. The client will also contain user-interface code and controlling logic. Figure 5 shows the client/server architecture for this example. The HTTP server for the Web interface is simply a special-case client of the ORB.

Realizing this architecture would be a matter of implementing each of the servers, as well as the client libraries. There is a lot of potential and attendant flexibility for the design of supporting services that rely on the ORB but are outside the scope of its specification. Servers will benefit from object garbage collection, remote-event reporting and server control, centralized security, and so on. These services can be provided at the ORB interface level, without resorting to specialized network programming. The clients offer perhaps the greatest potential and challenge for achieving object independence. The layering concept can be expanded to allow clients to load object definitions dynamically, and objects can supply their own user-interface constructs, which helps tremendously when dealing with versioning issues. Once the clients and servers have been designed, it remains only to outline a strategy for structuring the physical resources on the back end.

That strategy involves answering many questions:

Each question may be answered differently for a given app, but you can determine which back-end components are necessary and propose an organization for this example.

The bookshop will need one object server for each type of object in the model. Each server will run on a separate host with a daemon to start it, stop it, manage it, and provide information about it to clients. Initially (until the customers roll in) you'll have one host per type. If business booms and the Order server host is overwhelmed, you can roll out another by simply adding a machine to the net, installing the ORB and server files, and adding a line to the locator database. You'll also need at least one fat database server. My guess is that you could get a very good start using SQL Server on a fast Pentium and grow from there. The other components needed are a name host and a Web server. The name host is a specialized host that runs the ORB daemon and maintains the locator database. It is the only host that clients need to know the name of. At run time, a client will specify this host as the lookup server for bind requests. It will see a lot of traffic, but each connection will last just a few milliseconds under normal circumstances. The Web server is a specialized client of the ORB, which queries distributed objects and formats the results into HTML documents for return to a browser. I won't go into detail about the HTML component. HTTP is sufficiently flexible to accept input from the user and to query objects. Figure 6 shows the completed system architecture for the book shop.

Conclusion

If you build a virtual business, will customers come? Well, that's not my area of expertise, but a lot of money is being bet on it. More to the point, if customers do arrive, the architecture presented here will scale nearly infinitely through the addition of more hosts and servers, additional lookup-server capability, faster database servers, and the like. As your business grows, you can change the platforms on which the servers are implemented, replace database systems, radically redesign data schemas, or make any other changes to the back end that you wish--and all without affecting a single client. If the World Wide Web is ultimately replaced with a different interface, you can accommodate it, as long as it provides a means of calling a library on whatever it uses for a server.

What I've described here carries distributed-object computing to its extreme--objects implemented on many platforms, for many operating systems, collaborating on a worldwide basis over the global Internet. But these technologies are just as useful when objects are implemented only one departmental workgroup away. Either way, the technologies of distributed-object computing will provide simple solutions to complex client/server development tasks.

Figure 1: A virtual bookshop on the global TCP/IP Internet.

Figure 2: Simple object model for the virtual-bookshop application.

Figure 3: Convergence of major TCP/IP networks with commercial and ad hoc providers.

Figure 4: Local versus distributed call stack

Figure 5: Partial client/server architecture for the virtual-bookshop application.

Figure 6: Physical architecture of the virtual bookshop.

Example 1: Sample IDL Book interface and supporting types.

/* IDL declarations for the Book interface and supporting types */
typedef sequence<string> Authors;
enum MediaCode
{
    mcPaper,
    mcHard,
    mcCassette,
    mcQuality
};
struct PubInfo
{
    string House;
    string Date;
};
interface Book
{
    Authors GetAuthors();
    string Title();
    float Price();
    float Discount();
    PubInfo GetPubInfo();
    string ISBNCode();
    MediaCode Media();
};

Copyright © 1995, Dr. Dobb's Journal