The CORBA Component Model: Part 1, Evolving Towards Component Middleware C/C++ Users Journal February 2004 Douglas C. Schmidt and Steve Vinoski In the early days of computing, software was developed from scratch to achieve a particular goal on a specific hardware platform. Since computers were then much more expensive than the cost to program them, scant attention was paid to systematic software reuse and composition of applications from existing software artifacts. Over the past four decades, the following two trends have spurred the transition from hardware-centric to software-centric development paradigms: Economic factors. Due to advances in VLSI and the commoditization of hardware, most computers are now much less expensive than the cost to program them. Technological advances. With the advent of software development technologies, such as object-oriented programming languages and distributed object computing middleware, it has become easier to develop software with more capabilities and features. A common theme underlying the evolution of software development paradigms is the desire for reuse; for instance, to compose and customize applications from preexisting software building blocks. Modern software development paradigms, such as object-oriented, component-based, and generative technologies, aim to achieve this common goal but differ in the type(s) and granularity of building blocks that form the core of each paradigm. The development and the evolution of middleware technologies also follow a similar goal of capturing and reusing design information learned in the past, within various layers of software. This column is the first in a series that focuses on the CORBA Component Model (CCM). We first outline the evolution of programming abstractions from subroutines to modules, objects, and components. We then describe how this evolution has largely been mirrored in middleware, from message passing to remote procedure calls and from distributed objects to component middleware. We discuss the limitations of distributed object computing (DOC) middleware that motivate the need for component middleware in general and the CORBA Component Model (CCM). The Evolution of Programming Abstractions Composing software from reusable building blocks has been a goal of software researchers for over three decades. For example, in 1968 Doug McIlroy motivated the need for software "integrated circuits" (ICs) and their mass-production and examined: (1) the types of variability needed in software ICs; and (2) the types of ICs that can be standardized usefully [1]. Since McIlroy's work predated the widespread adaptation of objects and components, he envisioned a software IC to be built from a standard catalog of subroutines, classified by precision, robustness, time-space performance, size limits, and binding time of parameters. The earliest work in systems software, including operating systems and compilers for higher-level languages, also emphasized the use of prebuilt software. This software was often included in the OS or compiler runtime itself and was usually organized around functions that accomplished some common computing support capability ranging from online file systems to mathematical manipulation libraries. Application developers manually weaved these off-the-shelf functional capabilities into new applications. Subsequent efforts to realize the vision of software ICs resulted in information hiding and data abstraction techniques that placed more emphasis on the organization of data than the design of procedures [2].Data abstraction resulted in formalizing the concept of modules as a set of related procedures and the data that they manipulate, resulting in partitioning of programs so that data is hidden inside the modules. Information hiding and data abstraction techniques were embodied in programming languages, such as Clu, Modula 2, and Ada. These languages provided modules as a fundamental construct apart from explicit control of the scopes of names (import/export), a module initialization mechanism, and a set of generally known and accepted styles of usage of the features outlined above. Although these languages allowed programmers to create and apply user-defined types, it was hard to extend these types to new usage scenarios without modifying their interface definitions and implementations. The next major advance in programming paradigms came from object-oriented design techniques, such as the OMT and Booch notations/methods (which eventually morphed into UML), and object-oriented programming languages, such as C++ and Java. Object-oriented techniques focus on decomposing software applications into classes and objects that have crisply defined interfaces and are related via derivation, aggregation, and composition. A key advantage of object-oriented techniques is their direct support for the distinction between a class's general properties and its specific properties. Expressing this distinction and taking advantage of it programmatically was simplified by object-oriented language support for inheritance, which allows the commonality in class behavior to be explicit, and polymorphism, which allows customization of base class behavior by allowing overriding of dynamically bound methods in subclasses. Although the object-oriented paradigm enhanced previous programming paradigms, it also still had deficiencies. For example, object-oriented languages generally assume that different entities in a software system have interfaces that are: (1) amenable to inheritance and aggregation; and (2) written in the same programming language. Many applications today must run in multilingual and even multiparadigm environments, which necessitates a higher level of abstraction than can be provided by a single programming language or design paradigm. The use of object-oriented techniques also indirectly led to many small relatively homogeneous parts that needed to be connected together by developers using detailed (and thus often error-prone and tedious) coding conventions and styles. This approach therefore required significant work beyond the existence of the primitive objects and even some supporting services, when developing large systems. What was needed was a way to package together larger units that were comprised of collections of the (sometimes heterogeneous) off-the-shelf parts needed to provide higher-level capabilities, including their support attributes. For example, a typical capability might have the need for all of a variety of types of object invocations, events that trigger certain responses, transactions that manage the ordering and acceptance of state changes, and fault tolerance behavior that responds to failures. While each of these may individually be off-the-shelf, they needed to be separately and carefully knitted together in each instance to form a complete module handling some particular part of an overall application. The conditions outlined above motivated the need for component-based software development techniques [3]. A component is an encapsulated part of a software system that implements a specific service or set of services. A component model defines: (1) the properties of components, such as the version of each component, dependencies between components, access modes, component identifiers, and operational policies; (2) the set of interfaces that components export; and (3) the infrastructure needed to support the packing, assembly, deployment and runtime management of component instances. Although components share many properties with objects (such as the separation of interface from implementation), they differ from objects in the following ways: 1. Multiple views per component and transparent navigation. An object typically implements a single class interface, which may be related to other classes by inheritance. In contrast, a component can implement many interfaces, which need not be related by inheritance. Moreover, components provide transparent "navigation" operations; for instance, moving between the different functional views of a component's supported interfaces. A single component can therefore appear to provide varying levels of functionality to its clients. Conversely, navigation in objects is limited to moving up or down an inheritance tree of objects via downcasting or "narrowing" operations. It is also not possible to provide different views of the same object since all clients are granted the same level of access to the object's interfaces and state. 2. Extensibility. Since components are described at a higher level of abstraction than objects, inheritance is less crucial as a means to achieve polymorphism. Objects are units of instantiation, and encapsulate types, interfaces and behavior that model the (possibly physical) entities of the problem domain in which they are used. They are typically implemented in a particular language and have some requirements on the layout that each inter-operating object must satisfy. In contrast, a component need not be represented as a class, be implemented in a particular language, nor share binary compatibility with other components (though it may do so in practice). Components can therefore be viewed as providers of functionality that can be replaced with equivalent components written in another language. This extensibility is facilitated via the Extension Interface design pattern, which defines a standard protocol for creating, composing, and evolving groups of interacting components. 3. Higher-level execution environment. Component models define a run-time execution environment—a "container"—that operates at a higher level of abstraction than access via ordinary objects. The container execution environment provides additional levels of control for defining and enforcing policies on components at runtime. In contrast, the direct dependency on the underlying infrastructure when using objects can constrain the evolution of component functionality separately from the evolution of the underlying runtime infrastructure. The Evolution of Middleware The emergence and rapid growth of the Internet, beginning in the 1970s, brought forth the need for distributed applications. For years, however, these applications were hard to develop due to a paucity of methods, tools, and platforms. Various technologies have emerged over the past 20+ years to alleviate complexities associated with developing software for distributed applications and to provide an advanced software infrastructure to support it. Early milestones included the advent of the Internet protocols, message-passing architectures based on interprocess communication (IPC) mechanisms, micro-kernel architectures, and Sun's Remote Procedure Call (RPC) model. The next generation of advances included OSF's Distributed Computing Environment (DCE), IBM's MQ Series, CORBA, and DCOM. More recently, middleware technologies have evolved to support distributed real-time and embedded (DRE) applications (for instance, Realtime CORBA), as well as to provide higher-level abstractions, such as component models (for example, CCM and J2EE), web-services (such as SOAP), and model driven middleware (Cadena and CoSMIC, for instance). The success of the middleware technologies outlined above has added the middleware paradigm to the familiar operating system, programming language, networking, and database offerings used by previous generations of software developers. By decoupling application-specific functionality and logic from the accidental complexities inherent in a distributed infrastructure, middleware enables application developers to concentrate on programming application-specific functionality, rather than wrestling repeatedly with lower-level infrastructure challenges. One of the watershed events during the past 20 years was the emergence of distributed object computing (DOC) middleware in the late 1980s/early 1990s [4]. DOC middleware represented the confluence of two major areas of information technology: distributed computing systems and object-oriented design and programming. Techniques for developing distributed systems focus on integrating multiple computers to act as a unified scalable computational resource. Likewise, techniques for developing object-oriented systems focus on reducing complexity by creating reusable frameworks and components that reify successful patterns and software architectures. DOC middleware therefore uses object-oriented techniques to distribute reusable services and applications efficiently, flexibly, and robustly over multiple, often heterogeneous, computing and networking elements. The Object Management Architecture (OMA) in the CORBA 2.x specification [5] defines an advanced DOC middleware standard for building portable distributed applications. The CORBA 2.x specification focuses on interfaces, which are essentially contracts between clients and servers that define how clients view and access object services provided by a server. Despite its advanced capabilities, however, the CORBA 2.x standard has the following limitations: 1. Lack of functional boundaries. The CORBA 2.x object model treats all interfaces as client/server contracts. This object model does not, however, provide standard assembly mechanisms to decouple dependencies among collaborating object implementations. For example, objects whose implementations depend on other objects need to discover and connect to those objects explicitly. To build complex distributed applications, therefore, application developers must explicitly program the connections among interdependent services and object interfaces, which is extra work that can yield brittle and non-reusable implementations. 2. Lack of generic application server standards. CORBA 2.x does not specify a generic application server framework to perform common server configuration work, including initializing a server and its QoS policies, providing common services (such as notification or naming services), and managing the runtime environment of each component. Although CORBA 2.x standardized the interactions between object implementations and object request brokers (ORBs), server developers must still determine how: (1) object implementations are installed in an ORB; and (2) the ORB and object implementations interact. The lack of a generic component server standard yields tightly coupled, ad-hoc server implementations, which increase the complexity of software upgrades and reduce the reusability and flexibility of CORBA-based applications. 3. Lack of software configuration and deployment standards. There is no standard way to distribute and start up object implementations remotely in CORBA 2.x specifications. Application administrators must therefore resort to in-house scripts and procedures to deliver software implementations to target machines, configure the target machine and software implementations for execution, and then instantiate software implementations to make them ready for clients. Moreover, software implementations are often modified to accommodate such ad hoc deployment mechanisms. The need of most reusable software implementations to interact with other software implementations and services further aggravates the problem. The lack of higher-level software management standards results in systems that are harder to maintain and software component implementations that are much harder to reuse. An Overview of Component Middleware and the CORBA Component Model Component middleware is a class of middleware that enables reusable services to be composed, configured, and installed to create applications rapidly and robustly [6]. During the past several years component middleware has evolved to address the limitations of DOC middleware described above by Creating a virtual boundary around larger application component implementations that interact with each other only through well-defined interfaces. Defining standard container mechanisms needed to execute components in generic component servers. Specifying the infrastructure to assemble, package, and deploy components throughout a distributed environment. The CORBA Component Model (CCM) [7] is a current example of component middleware that addresses limitations with earlier generations of DOC middleware. The CCM specification extends the CORBA object model to support the concept of components and establishes standards for implementing, packaging, assembling, and deploying component implementations. From a client perspective, a CCM component is an extended CORBA object that encapsulates various interaction models via different interfaces and connection operations. From a server perspective, components are units of implementation that can be installed and instantiated independently in standard application server runtime environments stipulated by the CCM specification. Components are larger building blocks than objects, with more of their interactions managed to simplify and automate key aspects of construction, composition, and configuration into applications. A component is an implementation entity that exposes a set of ports, which are named interfaces and connection points that components use to collaborate with each other. Ports include the following interfaces and connection points: Facets, which define a named interface that services method invocations from other components synchronously. Receptacles, which provide named connection points to synchronous facets provided by other components. Event sources/sinks, which indicate a willingness to exchange event messages with other components asynchronously. Components can also have attributes that specify named parameters that can be configured later via metadata specified in component property files. A container provides the server runtime environment for component implementations called executors. It contains various pre-defined hooks and operations that give components access to strategies and services, such as persistence, event notification, transaction, replication, load balancing, and security. Each container defines a collection of runtime strategies and policies, such as an event delivery strategy and component usage categories, and is responsible for initializing and providing runtime contexts for the managed components. Component implementations have associated metadata written in XML that specify the required container strategies and policies. In addition to the building blocks outlined above, the CCM specification also standardizes various aspects of stages in the application development lifecycle, notably component implementation, packaging, assembly, and deployment, where each stage of the lifecycle adds information pertaining to these aspects. The CCM Component Implementation Framework (CIF) automatically generates component implementation skeletons and persistent state management mechanisms using the Component Implementation Definition Language (CIDL). CCM packaging tools bundle implementations of a component with related XML-based component metadata. CCM assembly tools use XML-based metadata to describe component compositions, including component locations and interconnections among components, needed to form an assembled application. Finally, CCM deployment tools use the component assemblies and composition metadata to deploy and initialize applications. The tools and mechanisms defined by CCM collaborate to address the limitations with DOC middleware described earlier. The CCM programming paradigm separates the concerns of composing and provisioning reusable software components into the following development roles within the application lifecycle: Component designers, who define the component features by specifying what each component does and how components collaborate with each other and with their clients. Component designers determine the various types of ports that components offer and/or require. Component implementers, who develop component implementations and specify the runtime support a component requires via metadata called component descriptors. Component packagers, who bundle component implementations with metadata giving their default properties and their component descriptors into component packages. Component assemblers, who configure applications by selecting component implementations, specifying component instantiation constraints, and connecting ports of component instances via metadata called assembly descriptors. System deployers, who analyze the runtime resource requirements of assembly descriptors and prepare and deploy required resources where component assemblies can be realized. The CCM specification has recently been finalized by the OMG and is in the process of being incorporated into the core CORBA specification. The CORBA 3.0 specification released by the OMG only includes changes in IDL definition and Interface Repository changes from the Component specification [7]. CCM implementations are now available based on the recently adopted specification, including OpenCCM by the Universite des Sciences et Technologies de Lille, France, K2 Containers by iCMG, MicoCCM by FPX, Qedo by Fokus, and CIAO by the DOC groups at Washington University in St. Louis and the Institute for Software Integrated Systems (ISIS) at Vanderbilt University. The architectural patterns used in CCM are also used in other popular component middleware technologies, such as J2EE and .NET. Concluding Remarks In this column, we've provided a brief history of middleware and explained how it's evolved towards components. Component middleware represents a new generation of middleware that overcomes many of its predecessors' shortcomings. For example, by providing models that capture frequently used middleware patterns, such as interface navigation and event handling, and also by encapsulating common execution functions within generic component server containers, component middleware promises to ease the burden of developing distributed applications. The fact that component middleware also addresses assembly, packaging, and deployment concerns means that it enhances and simplifies the traditionally hard process of deploying distributed applications into production environments. Finally, we introduced the CORBA Component Model (CCM), which builds on the well established and widely deployed CORBA base to provide a practical component model for real world distributed applications. References [1] Doug McIlroy, "Mass Produced Software Components," Proceedings of the NATO Software Engineering Conference, October 1968. [2] David L. Parnas, "On the Criteria To Be Used in Decomposing Systems into Modules," Communications of the ACM, vol 15, no 12, December 1972. [3] Clemens Szyperski, Component Software: Beyond Object-Oriented Programming, Addison-Wesley, 1998. [4] Richard E. Schantz and Douglas C. Schmidt, "Middleware for Distributed Systems: Evolving the Common Structure for Network-centric Applications," Encyclopedia of Software Engineering, Wiley, 2002. [5] "The Common Object Request Broker: Architecture and Specification Revision 2.6, OMG Technical Document formal/05-09-02", May 2002. [6] Nanbor Wang, Douglas C. Schmidt, and Carlos O'Ryan, "An Overview of the CORBA Component Model," Component-Based Software Engineering, AddisonWesley, 2000. [7] "CORBA Components," OMG Document formal/2002-06-65, Jun 2002. The CORBA Component Model: Part 2, Defining Components with the IDL 3.x Types Douglas C. Schmidt and Steve Vinoski Our previous column [Schmidt04] described the evolution of programming language and middleware technologies that have culminated in the CORBA Component Model (CCM), which is a key part of the CORBA 3.x specification [CORBA3]. CCM is component middleware that addresses limitations with earlier generations of CORBA 2.x Distributed Object Computing (DOC) middleware. Some of the limitations with DOC middleware overcome by CCM include lack of standards for: Interface relationships other than inheritance. Common patterns of using POA policies. Integrating CORBA services (such as notification, lifecycle, and persistence) with distributed applications. Generic application server mechanisms. Software deployment and configuration tools. In this column, we further our discussion by illustrating a hybrid publisher/subscriber and request/response distribution architecture that uses CCM features to implement our familiar stock-quoter example, which has been the driving application in our columns for years. As discussed in the previous column, the CCM specification extends the CORBA object model to support the concept of components, and establishes standards for implementing, packaging, assembling, and deploying component implementations. From a client perspective, a CCM component is an extended CORBA object that encapsulates various interaction models via different interfaces and connection operations. From a server perspective, components are implementation units that can be installed and instantiated independently in standard application server runtime environments stipulated by the CCM specification. In general, components are larger building blocks than objects, with more of their interactions managed by containers to simplify and automate key aspects of construction, composition, and configuration into applications. Components often need to collaborate with different types of components/applications, which may understand different interface types. Components can therefore express their intention to collaborate with other components/applications by defining ports, which expose different views of their capabilities to clients. There are several types of ports in CCM, including: Facets, which provide interfaces that implement point-to-point operations invoked from other components, and receptacles, which indicate a uses dependency on an operation interface provided by another component. Facets are an instantiation of the Extension Interface pattern [POSA2], which allows each component to export multiple interfaces to prevent breaking of client code and bloating of interfaces when developers extend or modify component functionality. Receptacles define a way to connect a required interface to a component. Facets and receptacles are typically joined together by configuration and deployment tools. Their interconnection can be illustrated by the standard icon in Figure 1(a). Event sources and event sinks, which indicate a willingness to exchange typed event messages with one or more components. An event source is a named connection point for event production. There are two styles of event sources: publishers that can have multiple subscribers, and emitters that can have only one subscriber. An event sink is a named interface to which events can be pushed by event sources. An event sink can subscribe to multiple event sources. Event sources and event sinks are also typically joined together by configuration and deployment tools. Their interconnection can be illustrated by the standard icon in Figure 1(b). In IDL 3.x (which defines component-oriented extensions to IDL 2.x), facets are designated by the provides keyword, receptacles are designated by the uses keyword, and event sinks are designated by the consumes keyword. The two styles of event sources (emitters and publishers) are designated by the emits and publishes keywords, respectively. Components can also include attribute ports, which specify named parameters that can be configured later via metadata specified in component property files. All the IDL 3.x features can be mapped to equivalent IDL 2.x features, as we illustrate in our stock-quoter example. In general, when you develop a CCM application, you perform the following steps: 1. Define your interfaces using IDL 2.x features; that is, use the familiar CORBA types (such as struct, sequence, long, Object, interface, raises, and so on) to define your interfaces and exceptions. 2. Define your component types using IDL 3.x features; for example, use the new CCM keywords (such as component, provides, uses, publishes, emits, and consumes) to group the IDL 2.x types together to form components. 3. Use IDL 3.x features to manage the lifecycle of the component types; for example, use the new CCM keyword home to define factories that create and destroy component instances. 4. Implement your components; that is, using C++ or Java and the Component Implementation Definition Language (CIDL), which generates the component implementation executors and associated metadata. 5. Assemble your components; for instance, group related components together and characterize their metadata that describes the components present in the assembly. 6. Deploy your components and run your application; that is, move the component assemblies to the appropriate nodes in the distributed system and invoke operations on components to perform the application logic. In this column, we show how to use CCM features (facets, for instance), receptacles, event sources, event sinks, and attributes defined using IDL 3.x to perform the first three steps just outlined for a component-based version of our stock-quoter example. In subsequent columns, we will examine how to perform the latter three steps that stitch all the components together and run them in application servers. The Stock Quoter System Architecture Many examples in our recent columns have focused on request/response communication, where operation requests flow from client to server and responses flow back from server to client. For example, we've often shown examples where a stock broker client obtains information about a particular stock name by invoking the get_quote() operation on a Stock::Quoter interface implemented by a remote server. While this "polling" approach is fairly common, it can also saturate the server and the network by making many requests, even if the value of the stock hasn't changed! One way to avoid the problems with request/response polling is to employ a variant of the Publisher/Subscriber architectural pattern [POSA1]. In this pattern, publishers generate events that are transmitted to one or more subscribers, who can then take further action depending on the events they receive and their internal state. The overall flow of information in our latest incarnation of the stock-quoter system works as follows: 1. Stock broker clients subscribe with a stock distributor server to receive notification events whenever a stock value of interest to them changes. 2. The stock distributor server monitors a real-time stock feed database. 3. Whenever the value of a stock changes, the distributor publishes an event to interested stock brokers. 4. If stock brokers are interested in learning more details about a stock whose value has changed, they can invoke an operation on the stock distributor to receive more information. By employing the Publisher/Subscriber pattern in our stock quoter example, we can alleviate the key drawbacks with polling-based request/response design—stock brokers only contact the stock distributor server via request/response operations when the value of a stock changes, rather than polling them repeatedly to see if the value has changed. Figure 2 illustrates how we can design this type of system using CCM. We define a StockDistributor component that publishes events to indicate that a particular stock's value has changed. This component will monitor the real-time stock database and, when the values of particular stocks change, will push a CCM eventtype containing the stock name via a CCM event source to the corresponding CCM event sink of one or more StockBroker components. The StockBroker components that consume this event will then examine the stock name stored in the event. If they are interested in the stock, they can invoke a request/response operation via their CCM receptacle on a CCM facet exported by the StockDistributor component to obtain more information about the stock. The remainder of this column describes each component in Figure 2, focusing on how to program them using CCM features and IDL 3.x. To clarify the behavior of these components "under the hood," we also describe the equivalent IDL 2.x corresponding to the IDL 3.x types. An IDL 3.x compiler typically doesn't generate the IDL 2.x code directly (although it could). However, the C++ or Java mappings that it generates are equivalent to this IDL 2.x code, which can be helpful if you already understand IDL 2.x and you're learning CCM and IDL 3.x. It's important to note, however, that all the equivalent IDL 2.x code we show in this article is not written by application developers, which is one of the key benefits of CCM and CORBA 3.x relative to CORBA 2.x! Step 1: Defining the Stock-Quoter Interfaces Using IDL 2.x Types We'll start by defining some interfaces and other types using IDL 2.x features. All of our types (including the IDL 3.x types) will be defined in a module called Stock: module Stock { The remaining IDL 2.x and 3.x types are defined within the Stock module. When stock brokers want to learn more information about a particular stock whose value has changed recently, they can invoke the get_stock_info() operation on the following StockQuoter interface: interface StockQuoter { StockInfo get_stock_info (in string stock_name); }; This interface returns the following StockInfo struct: struct StockInfo { string name; long high; long low; long last; // ... }; StockInfo contains information about the high and low trading values of the stock during the trading thus far today, along with the most recent value. It also includes the stock name so that each StockInfo instance is self-identifying and is thus easily trackable and usable in collections. The stock distributor itself runs as a daemon that can be started and stopped by a system administrator. The Trigger interface instructs the stock distributor to perform these control operations: interface Trigger { void start (); void stop (); } When an administrator calls start(), the stock distributor begins to monitor the realtime stock database until the stop() operation is called. Step 2: Defining the Stock-Quoter Components Using IDL 3.x Types Now that we've illustrated the IDL 2.x types in our stock-quoter system, we'll show how they are combined using IDL 3.x component types. We start with the eventtype data type that components can use to communicate using CCM's publisher/subscriber event mechanism. Whenever a stock value changes, the stock distributor publishes the following eventtype containing the name of the stock: eventtype StockName { public string name; }; Internally, an IDL 3.x eventtype is implemented using an IDL 2.x valuetype. Unlike CORBA objects (which are passed by reference), instances of CORBA eventtype and valuetype are always passed by value. Like structs, they can contain state in the form of fields. Unlike structs, however, they can have user-defined operations and support inheritance. The equivalent IDL 2.x for the eventtype is: valuetype StockName : Components::EventBase { public string name; }; interface StockNameConsumer : Components::EventConsumerBase { void push_StockName (in StockName the_stockname); }; Note how equivalent IDL 2.x code maps the StockName eventtype to a valuetype that inherits from Components::EventBase, which is an abstract valuetype defined in the standard CCM Components module: module Components { // ... abstract valuetype EventBase {}; interface EventConsumerBase { void push_event (in EventBase evt); }; }; This mapping gives you a greater range of options because it enables publishers to push names of stocks to subscribers as either: Concrete StockName valuetypes via StockNameConsumer::push_StockName(), which is statically typed (and hence less error-prone), but less flexible, or Generic EventBase valuetypes via EventConsumerBase::push_event(), which is more flexible, but dynamically typed (and hence potentially more errorprone). Now that we've defined our StockName eventtype, we can combine it with the IDL 2.x StockQuoter interface defined earlier to create our first CCM component, called StockBroker, whose ports are in Figure 3. The IDL 3.x description for StockBroker is: component StockBroker { consumes StockName notifier_in; uses StockQuoter quoter_info_in; }; Although the StockBroker component doesn't inherit from anything explicitly, its equivalent IDL 2.x code below shows how it inherits implicitly from Components::CCMObject: interface StockBroker : Components::CCMObject { // ... }; Components::CCMObject is the base interface for all component types in CCM. By inheriting from this interface, StockBroker obtains operations that manage its event sink and receptacle. It also inherits operations that enable discovery of its ports via navigation by component-aware clients. The StockBroker component contains two ports that correspond to the two roles it plays. First, it is a subscriber that consumes a StockName eventtype called notifier_in that's published by the StockDistributor when the value of a stock changes. Second, it is a user of the StockQuoter interface we defined earlier to provide additional information about a stock. This dependency is indicated explicitly in IDL 3.x via a CCM receptacle called quoter_info_in that indicates the uses relationship on the StockQuoter interface. The equivalent IDL 2.x for StockBroker's IDL 3.x event sink designator: consumes StockName notifier_in; is StockNameConsumer get_consumer_notifier_in (); which defines a factory operation that returns an object reference to the StockNameConsumer equivalent IDL 2.x interface shown earlier. When our stockquoter system is initialized, standard CCM deployment and configuration tools [D&C] will use this factory operation to connect publishers (such as the StockDistributor component) with StockBroker subscribers. StockBroker's IDL 3.x receptacle designator uses StockQuoter quoter_info_in; maps to the following group of equivalent IDL 2.x types and operations: void connect_quoter_info_in (in StockQuoter c); StockQuoter disconnect_quote_info_in (); StockQuoter get_connection_quoter_info_in (); These operations are also used by standard CCM deployment and configuration tools to connect the quoter_info_in receptacle with the StockQuoter facet, which is provided by the StockDistributor component whose ports are in Figure 4. The StockDistributor component has the following IDL 3.x description: component StockDistributor supports Trigger { publishes StockName notifier_out; provides StockQuoter quoter_info_out; attribute long notification_rate; }; This CCM component supports (also known as "inherits from") the Trigger interface defined earlier, which enables a system administrator application to start() and stop() instances of StockDistributor. The equivalent IDL 2.x for StockDistributor for supports is: interface StockDistributor : Components::CCMObject, Trigger { // ... }; The supports keyword is useful for components such as StockDistributor that have a "primary" interface, which alleviates the need to go through extra steps just to access the operations of that interface. If the interface were specified as a facet via the provides keyword instead, applications would have to call an operation to get a reference to the facet, and then invoke the desired operation. Instead, the supports keyword lets administrator applications invoke the start() and stop() operation directly on the component reference since the Trigger interface is a parent of StockDistributor. StockDistributor also publishes a StockName eventtype called notifier_out that is pushed to the StockBroker subscriber components when a stock value changes. The equivalent IDL 2.x for the publishes StockName notifier_out; IDL 3.x event source designator is defined as: Components::Cookie subscribe_notifier_out (in Stock::StockNameConsumer c); Stock::StockNameConsumer unsubscribe_notifier_out (in Components::Cookie ck); This pair of operations is used by standard CCM deployment and configuration tools to subscribe/unsubscribe components that consume the StockName events published by the StockDistributor. For example, assuming there were stockBroker and stockDistributor object references to the respective StockBroker and StockDistributor components, these tools could connect automatically the stock distributor publisher to a stock broker subscriber using these steps: Stock::StockNameConsumer_var consumer = stockBroker->get_consumer_notifier_in (); stockDistributor->subscribe_notifier_out (consumer.in ()); The CCM deployment and configuration tools we've mentioned several times provide an important benefit to CCM and CORBA 3.x relative to the earlier CORBA 2.x Standard because they alleviate the need for developers of application components to perform connection "plumbing" programmatically. Moreover, CCM containers that provide the runtime environment for the components will mediate access to CosNotification event channels or other mechanisms used to deliver the events, further simplifying the task of application component developers. StockDistributor also defines a StockQuoter facet called quoter_info_out. The IDL 3.x facet designator provides StockQuoter quoter_info_out; in StockDistributor maps to the following equivalent IDL 2.x type: StockQuoter provide_quoter_info_out (); which defines a factory operation that returns object references that clients (such as StockBroker components) can use to obtain more information about a particular stock. As before, the CCM deployment and configuration tools can use this factory operation in conjunction with the StockBroker's connect_quoter_info_in() receptacle operation to perform connection plumbing automatically, as follows: Stock::StockQuoter_var quoter = stockDistributor->provide_quoter_info_out (); stockBroker->connect_quoter_info_in (quoter.in ()); Finally, in addition to the inherited Trigger functions, system administrators can use the notification_rate attribute to control the rate at which the StockDistributor component checks the stock-quote database and pushes changes to StockBroker subscribers. Attributes in CCM are primarily used for component configuration; for example, to define optional behaviors, modality, resource hints, and so on. The mapping for attributes in IDL 3.x is the same as for attributes in IDL 2.x; that is, they are represented as a pair of accessor/mutator methods in C++. The only semantic difference is that they can now raise user-defined exceptions, which was not allowed in IDL 2.x. Step 3: Use IDL 3.x Features to Manage the Lifecycle of the Component Types Instances of the StockBroker and StockDistributor components must be created by the CCM runtime system. One well-recognized problem with CORBA 2.x was its lack of a standard way to manage component lifecycles. To rectify this problem, CCM integrates lifecycle management into component definitions via the concept of homes, which are factories that create and destroy component instances. In CCM, home is a new IDL keyword that's used to define a factory that manages one type of component. A component instance is managed by one home instance. Since a home has an interface, it's identified via an object reference. By default, a home has standard lifecycle operations; for instance, create(), through which users can define operations with arbitrary parameter lists if the defaults don't suffice. For our stockquoter example we can use the defaults, so our homes are defined as follows: home StockBrokerHome manages StockBroker {}; home StockDistributorHome manages StockDistributor {}; The equivalent IDL 2.x mapping for these IDL 3.x homes are identical: interface StockBrokerHomeImplicit : Components::KeylessCCMHome[jp17] { StockBroker create (); }; interface StockDistributorHomeImplicit : Components::KeylessCCMHome { StockDistributor create (); }; Homes also typically support operations to find components, and the CCM specification also provides a HomeFinder interface that an application can use to find a particular home. An application can get a HomeFinder by passing the string "ComponentHomeFinder" to the ORB's resolve_initial_references() operation. These operations reduce the bookkeeping that CORBA applications must do to manage their objects and factories. Concluding Remarks This column illustrated the basics of using the IDL 3.x features specified by the CORBA Component Model (CCM) to define the key components and relationships of our stock-quoter example. The extra features of IDL 3.x make relationships between components and interfaces much clearer than IDL 2.x allows, though as you can see from our examples, there's an equivalent mapping from IDL 3.x to IDL 2.x semantics. IDL 3.x enables the definition of components that can manage multiple views (object instances, for instance), allows component dependencies and publishers/subscribers of events to be specified clearly, and supplies lifecycle management features. In future columns, we'll examine how to implement our stock-quoter example components using the CCM Component Implementation Framework (CIF), as well as how to apply the new OMG Deployment and Configuration specification [D&C] to stitch all the components together and run them in component servers. As always, if you have any comments on this column or any previous one, please contact us at object_connect@cs.wustl.edu. References [CORBA3] "CORBA Components," OMG Document formal/2002-06-65, June 2002. [D&C] "Deployment and Configuration of Component-based Distributed Applications Specification," OMG Document ptc/2003-07-08, July 2003. [POSA1] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal: Pattern Oriented Software Architecture: A System of Patterns, Wiley & Sons, 1996. [POSA2] D. Schmidt, H. Rohnert, M. Stal, and F. Buschmann: Pattern Oriented Software Architecture: Concurrent and Networked Objects, Wiley & Sons, 2000. [Schmidt04] D. Schmidt and S. Vinoski. "The CORBA Component Model, Part 1: Evolving Towards Component Middleware," C/C++ Users Journal, February 2004 (http://www.cuj.com/documents/s=9039/cujexp0402vinoski/). The CORBA Component Model Part 3: The CCM Container Architecture and Component Implementation Framework By Bala Natarajan, Douglas C. Schmidt, and Steve Vinoski Introduction Our previous column [Schmidt04b] illustrated portions of a hybrid publisher/subscriber and request/response distribution architecture that used CORBA Component Model (CCM) [CORBA3] features to enhance our familiar stock quoter example and alleviate key drawbacks with the use of polling-based request/response interactions. That column also focused on the use of IDL 3.x (which extends IDL 2.x with component-based features) to define the various CCM types used by our enhanced stock quoter example. In particular, we described the use of components, which extend CORBA objects by encapsulating various interaction models via ports, including (1) facets, which define an interface that accepts point-to-point operation invocations from other components, (2) receptacles, which indicate a dependency on a point-to-point operation interface provided by another component, and (3) event sources/sinks, which indicate a willingness to exchange typed messages with one or more components. In this column, CCM expert Bala Natarajan joins us to expand our coverage of CCM by describing the container architecture, which provides a powerful runtime environment for components, and the Component Implementation Framework (CIF), which generates a significant amount of code so it needn’t be written manually by CCM server application developers. Figure 1 illustrates the components in our stock quoter system example. The StockDistributor component monitors a real-time stock database. When the values of particular stocks change, it pushes a CCM eventtype that contains the stock’s name via a CCM event source to the corresponding CCM event sink implemented by one or more StockBroker components. If these components are interested in the stock, they can obtain more information about it by invoking a request/response operation via their CCM receptacle on a CCM facet exported by the StockDistributor component. Figure 1: CCM Architecture of the Stock Quoter System. The remainder of this column describes the CCM container architecture and Component Implementation Framework. Our next column will show how to implement the StockBroker and StockDistributor components using these CCM features. The CCM Container Architecture As discussed in [Schmidt04a], CCM components provide more powerful building blocks than CORBA objects. With CCM, more of the interactions between components are managed by middleware platform mechanisms and tools, thereby simplifying and automating key aspects of component-based applications. Specifically, CCM provides elements that assist with the construction, composition, configuration, and deployment of components into applications, and these elements are based on common patterns learned during the building and deploying of CORBA object systems over the previous decade. A key CCM element is the container, which is a framework that provides the runtime environment for one or more component implementations called executors, which are where components are actually implemented by server application developers. Containers provide the following capabilities for component application developers: They ease the task of building and deploying CCM application components by shielding component developers from low-level details of the underlying middleware. Though the CCM container framework doesn’t prevent direct access to the underlying middleware framework, it’s designed to provide a richer set of interfaces suitable for a broad range of applications. They are responsible for working in conjunction with other CCM features and tools to locate and create component instances, interconnect components, and enforce component policies associated with common services, such as lifecycle, security, and persistence. Developers select and configure the container runtime environment and its associated policies using programming artifacts provided by the Component Implementation Framework (CIF), which automates much of the component implementation “glue” code. The glue code generated from CIF and the executors developed by component developers interact with the container through a set of standard interfaces to provide a powerful component runtime environment. Each container and the components it manages can be grouped together in an executable program known as a component server within which containers and the components they manage are instantiated. To maximize flexibility, glue code and component implementations are normally housed in separate dynamic linked libraries (DLLs), and are loaded into the component server’s process at deployment time. With the addition of extra tools beyond those in the CCM specification, it’s also possible to configure executors within component servers statically, which is useful for real-time and embedded systems. [CIAOSTAT] is an example of one such tool that can be used to statically configure CCM components. Figure 2 shows the contents of a container and its relationship to the component executor(s) it manages. Figure 2: Structure of Components and Containers in CCM. As shown in Figure 2, a container defines various operations that enable the component executors to access common middleware services (such as persistence, event notification, transaction, replication, load balancing, and security) and runtime strategies and policies (such as POA policies, event delivery strategies, and component usage categories). Each container is involved in initializing and providing the runtime context for the component executors it manages, including their connections to ports of other components and to common middleware services. Below, we describe the different types of interfaces and functionality provided by a container to realize a powerful runtime environment. External APIs are interfaces the CCM container framework provides to clients of the component. There are two types of external APIs – home interfaces and application interfaces– that specify the contract between component developers and component clients. Home interfaces define operations that allow clients to obtain references to one of the application interfaces that the component implements. From the client’s perspective, two design patterns [SCP] are supported by home interfaces: (1) factories for creating new component instances (i.e., executors) and (2) finder interfaces for locating existing components. In our stock quoter application, the external APIs include the following home definitions: home StockBrokerHome manages StockBroker {}; home StockDistributorHome manages StockDistributor{}; and the following component definitions: component StockDistributor supports Trigger {}; component StockBroker {}; all of which were explained in [Schmidt04b]. Our next column will show how the CIF uses the IDL 3.x definitions shown above along with other definitions (such as CIDL composition definitions) to generate C++ or Java code that implements the contract between developers of component clients and the components themselves. Container APIs within the CCM container framework consist of internal interfaces and callback interfaces that are used by component developers to build applications. Component developers use internal interfaces to access container facilities and implement component business logic. The following SessionContext is an example of an internal interface: local interface CCMContext { Principal get_caller_principal (); CCMHome get_CCM_home (); boolean get_rollback_only () raises (IllegalState); Transaction::UserTransaction get_user_transaction () raises (IllegalState); boolean is_caller_in_role (in string role); void set_rollback_only () raises (IllegalState); }; local interface SessionContext : CCMContext { Object get_CCM_object () raises (IllegalState); }; An internal interface like SessionContext serves as bootstrapper, offering executors access to services provided by the container. In contrast, callback interfaces are implemented by component developers within executors and are used by a container to call into the executor to achieve the required functionality. The following SessionComponent is an example of a callback interface: local interface EnterpriseComponent { }; local interface SessionComponent : EnterpriseComponent { void set_session_context ( in SessionContext ctx) raises (CCMException); void ccm_activate () raises (CCMException); void ccm_passivate () raises (CCMException); void ccm_remove () raises (CCMException); }; The SessionComponent is a type of EnterpriseComponent that is housed in a container and provides operations for associating context with a component (i.e., via the set_session_context () operation) and managing the component’s lifetime (i.e., via ccm_activate(), ccm_passivate(), and ccm_remove() operations). Our next column will illustrate how component developers can implement their executors by inheriting from the internal and callback interfaces shown above. The container API specifies the contract between a component and the container that manages it. As shown above, these APIs are locality constrained; i.e., they use the local interface keyword in their IDL definitions. The local interface keyword informs the IDL compiler to suppress the generation of stubs and skeletons and instead generate language-specific mappings for locality constrained interfaces. Operations defined on local interfaces are therefore dispatched to process collocated local language-specific objects, rather than the standard mechanisms used to dispatch calls to servants registered within a POA. The CCM specification defines two types of containers: (1) session containers, which define a framework for components using transient object references and (2) entity containers, which define a framework for components using persistent object references. These container types are analogous to the session and entity bean types in Enterprise Java Beans [EJB]. In CCM, developers can use different component types that are obtained by associating the above container types with different memory management policies and CORBA usage models. Below, we describe the various characteristics of containers supported by CCM. Component category, which is the combination of the container API type (i.e., the component view) and the external API types (i.e., the client view). Table 1 illustrates the four different types of component categories supported by CCM. Table 1. Component Categories Supported by CCM. Key features of the four component categories shown in Table 1 are explained below: The service component category is characterized by no state, no identity, and a behavior implemented as operations defined on the facet of the component. The lifespan of this category is equivalent to the lifetime of a single operation request and is therefore useful for operations, such as command objects, that have no duration beyond the lifetime of a single client interaction with them. The service component category provides a simple way to wrap existing procedural applications. The session component category is characterized by transient state, a nonpersistent identity (i.e., the identity could change if the same component is activated multiple times), and a behavior implemented as operations defined on the facet of the component. The lifespan of this category is specified using the lifetime policies, which are explained further below. A session component is useful for implementing our stock broker and stock distributor components, which require transient state for the lifetime of the interaction, but have no persistent state. The entity component category is characterized by persistent state, which is visible to the client and is managed by the implementation of entity container, a persistent identity that is architecturally visible to its clients through a primary key declaration in the home declaration, and behavior that may be transactional. As a fundamental part of the CCM architecture, entity components expose their persistent state to the client as a result of declaring a primary key value on their home declaration. An entity component can be used to implement an EJB entity bean. The process component category is characterized by persistent state that is not visible to the client (i.e., it’s managed by the implementation of process container), a persistent identity that is visible to its clients only through userdefined operations on the home definitions, and a behavior that may be transactional. The process component is intended to model objects that represent business processes (e.g., applying for a credit card or creating an order) rather than entities (e.g., stock distributor or accounts). The main difference between process components and entity components is that process components do not expose their persistent identity to clients (except through user-defined operations). The desired component category can be selected at component development time by using the appropriate keyword in the Component Implementation Definition Language (CIDL) file, as discussed further below. Details of the contents and format of CIDL will be explained in our next column. CORBA usage model, which defines the interactions between the container and other CORBA capabilities, such as the ORB Core, POA, and common object services. The following are the three CORBA usage models specified in CCM, based on the type of interaction between servants and POAs: Stateless, where the servants are activated in a POA that has a lifespan policy set to TRANSIENT and an identity uniqueness policy set to MULTIPLE_ID, allowing a servant to be shared among all requests from different clients for the same interface. This usage model requires that objects do not have any identity of their own and are stateless. Conversational, where the servants are activated in a POA that has a lifespan policy set to TRANSIENT and an identity uniqueness policy set to UNIQUE_ID. This usage model is useful for a wide range of systems, including our stock broker and stock distributor components. Durable, where the servants are activated in a POA that has a lifespan policy set to PERSISTENT and an identity uniqueness policy set to UNIQUE_ID. Persistent objects support either the factory design pattern or the finder design pattern [SCP], depending on the component category. Persistent objects support self-managed or container-managed persistence. Persistent objects can be used with the CORBA Persistent State Service [PSS] or a user-defined persistence mechanism. When the CORBA PSS is used, component lifetime management is aligned with the PersistentId defined by the CORBA persistent state service and the container supports the transformation of an ObjectId to and from a PersistentId. A PersistentId provides a persistent handle for a class of objects whose permanent state resides in a persistent store, such as a database or file. The desired CORBA usage model can be selected by choosing the appropriate component category from Table 1 in a CIDL file. The relationship of the CORBA usage model with a component category implies a particular CORBA usage model. Component lifetime management, which includes different policies for managing the memory associated with components. Each component is associated with a programming language entity called a "servant," which represents the component within the POA and delegates certain operations to an executor, as described earlier. The lifetime of a component is therefore controlled via the lifetime of its servant within the POA. The following are the four lifetime policies associated with servants, and therefore executors: The method lifetime policy causes the container to activate the component upon every operation request and to passivate the component when an operation has completed. This policy limits memory consumption to the duration of an operation request, but incurs a greater cost of activation and passivation since the component is activated and passivated for every operation request. Moreover, there can be no state stored within the component, so this policy is mainly useful with the stateless CORBA usage model. The container lifetime policy causes the container to activate the component upon the first operation request and to leave it active until the container passivates it (possibly after thousands of requests are processed). Memory remains allocated until the container decides to reclaim it. This policy is useful with the conversational and durable CORBA usage models. We use this policy for the components in our stock broker application. Subsequent columns in this series will show how to select this CORBA usage model to develop our stock broker and stock distributor components. The component lifetime policy causes the container to activate the component on the first operation request and to leave it active until the component implementation requests it to be passivated. After the operation that requests the passivation completes, the container will passivate the component. Memory remains allocated until explicit application request, and is useful when component applications want to control the lifetime of the components themselves. This policy is commonly used with the conversational and durable CORBA usage models. The transaction lifetime policy causes the container to activate the component on the first operation request within a transaction and to leave it active until the transaction completes, at which point the component will be passivated. Memory remains allocated for the duration of the transaction. This policy is useful with the conversational and durable CORBA usage models, and is particularly useful for transaction systems, as its name implies. The desired component lifetime management policy can be selected declaratively during the application deployment and configuration phase. We show an example of how this can be done via XML later below. Context interface. In CCM, a context is an internal interface that provides a component executor with access to the common services provided by a container, such as access to the references of receptacles on the component, operations to publish events, access to the security credentials of the caller, and access to the UserTransaction interface that manages user-specified transactions as specified in the transaction service [CTS]. A session component can access its context via the SessionContext interface and an entity component can access its context via the EntityContext interface to gain access to the container and the underlying services it provides. The SessionContext and the EntityContext interfaces inherit from the CCMContext interface, as shown earlier. The context also serves as a “bootstrap” to the various services the container provides for the component. For example, in our StockBroker and StockDistributor components, the context will be used to push events to all the consumers and invoke operations on the receptacles, as we’ll show in our next column. The CCM Component Implementation Framework CORBA 2.x helped simplify the life of application developers by shielding them from many complexities associated with distributed computing. For example, the left side of Figure 3 illustrates how an IDL 2.x compiler generates stub and skeleton code that automates marshaling and demarshaling tasks. The constructs supported by an IDL compiler are relatively limited, however. Developers of CORBA 2.x server applications therefore must still manually write a lot of code, e.g., defining the IDL interfaces themselves, implementing their servants, and writing all the code required to bootstrap and run the server. Some of this work is clearly unavoidable, i.e., someone has to implement the IDL interfaces and business logic of IDL operations. Conversely, other tasks performed by server application developers are unnecessarily tedious and error prone in CORBA 2.x. Figure 3: Automated Code Generation with IDL 2.x vs. IDL 3.x. For example, server initialization and event loop code must be handwritten in CORBA 2.x, along with any support for introspection and navigation of object interfaces, which allows client applications to discover and traverse the operations and types defined in object interfaces. Likewise, CORBA 2.x server application developers must explicitly (1) keep track of dependencies their objects have on other objects and (2) manage the policies used to configure their POAs and manage object lifecycles. As a result, many CORBA 2.x server programs suffer from ad hoc design, code bloat, and inadequate reuse. To address these problems, CCM defines the Component Implementation Framework (CIF), which consists of patterns, languages, and tools that simplify and automate the development of component implementations, which are called executor implementations in CCM lingo. The CIF framework can be viewed as an extension of the CORBA 2.x POA framework that shields server application developers from many complexities associated with programming POAs, particularly servant registration, servant activation and deactivation, and setting a consistent set of policies on the POAs. The capabilities provided by the CCM CIF can be implemented as a wrapper facade [POSA2] around the CORBA 2.x POA framework, without exposing the inherent complexities of the POA framework to component server application developers. A key element of the CIF is the Component Implementation Definition Language (CIDL), which is a text-based declarative language that constitutes a key part of the CCM strategy for managing complex component-based applications by providing the capabilities described below. 1. Helps separate concerns by allowing developers to declaratively specify the component category and persistence details (as in the case of entity and process components) from the actual component executor implementations. The CIDL description of a component implementation is an aggregate entity, of which the component itself may be a relatively small part. The term composition is used in a CIDL file to denote the set of artifacts that constitute the unit of component definition and implementation. Based on the composition definitions within a CIDL file, a CIDL compiler can work in conjunction with an IDL 3.x compiler to generate the skeleton glue code that associates the implementations of components and component homes with the containers that host them. A CIDL compiler also generates infrastructure skeleton glue code that can be extended by component developers and used by the CIF to (1) activate objects within the component server’s POA, (2) manage the interconnection of a component’s ports to the ports of other components, (3) provide implementations for operations that allow navigation of component facets, and (4) intercept invocations on executors to transparently enact various policies, such as component activation, security, transactions, load balancing, and persistence. 2. Increases the ratio of generated code to handwritten code. The right-hand side of Figure 3 illustrates how the CIF can help increase the ratio of generated to handwritten code by automatically synthesizing the following CCM-related entities: Servants, which unlike CORBA 2.x are not implemented by component developers in CCM. In CORBA 2.x, servants are implemented by server application developers, whereas in CCM servants are synthesized automatically by a CIDL compiler. These generated servants implement CCM introspection and navigation capabilities and hide low-level details of the ORB core and POA from the executors. Servants delegate certain requests (such as operations defined on component’s supported interfaces and get_*() operations for facets) to the executors that are implemented by component developers. Executor IDL, which provides declarations of executors and container contexts as locality constrained interfaces with feature-specific operations, such as push_*() operations for events, get_*() operations for facets and receptacles, and accessors and mutators for attributes. The stubs generated from the executor IDL is inherited by executor classes that implement the component’s business logic. Context interfaces, which extend container-specific interfaces as localityconstrained interfaces to aid component executor access and bootstrap the various container-specific features, such as access to references of the connected receptacles, access to operations that can publish events, and access to the security credentials of clients. XML component descriptors, which are used by the standard OMG Deployment and Configuration [D&C] specification to configure components together when an application is launched. The remainder of this column shows examples of how each of these generated entities can be used in our stock quoter applications. An example of a servant generated by a CIDL compiler for the StockBroker component is shown below (exception specifications have been omitted): class StockBroker_Servant : public virtual POA_StockBroker, public virtual PortableServer::RefCountServantBase { public: // Operation for the ÒconsumesÓ port. StockNameConsumer_ptr get_consumer_notifier_in (); // Operation for the ÒusesÓ port. void connect_quoter_info_in (StockQuoter_ptr c); StockQuoter_ptr disconnect_quoter_info_in (); StockQuoter_ptr get_connection_quoter_info_in (); // Operations for Navigation interface. CORBA::Object_ptr provide_facet (const char *name); Components::FacetDescriptions get_all_facets (); Components::FacetDescriptions get_named_facets (const ::Components::NameList &); // Operations for Receptacles interface. Components::Cookie *connect (const char *name, CORBA::Object_ptr connection); CORBA::Object_ptr disconnect (const char *name, ::Components::Cookie *ck); Components::ReceptacleDescriptions *get_all_receptacles (); // Operations for Events interface. Components::EventConsumerBase_ptr get_consumer (const char *sink_name); Components::Cookie *subscribe (const char *publisher_name, ::Components::EventConsumerBase_ptr subscriber); // more operations.. }; The generated StockBroker_Servant servant class shown above has operations for connecting/disconnecting receptacles with facets, subscribing/unsubscribing to published events, and introspecting/navigating components. A CIDL compiler will generate both the interface of the servant and implementations for all the methods shown above. A sample of the executor IDL generated by a CIDL compiler for the stock broker is as follows: local interface CCM_StockBroker : ::Components::EnterpriseComponent { void push_notifier_in (in ::StockName e); }; local interface CCM_StockBrokerHomeImplicit { Components::EnterpriseComponent create (); }; local interface CCM_StockBrokerHomeExplicit : Components::HomeExecutorBase {}; local interface CCM_StockBrokerHome : CCM_StockBrokerHomeExplicit, CCM_StockBrokerHomeImplicit {}; local interface StockBroker_Exec : CCM_StockBroker, Components::SessionComponent {}; local interface StockBrokerHome_Exec : ::CCM_StockBrokerHome {}; The executor IDL shown above provides component-specific (i.e., stock broker component-specific) internal and callback interfaces that must be passed through an IDL compiler to generate language-specific executor classes, such as StockBroker_Exec and StockBrokerHome_Exec. Component developers then inherit from these executor classes to implement their business logic. Our next column will show C++ examples of how to extend and implement the operations on the executor interfaces. An example of the context interfaces generated by a CIDL compiler for our stock broker context is: local interface CCM_StockBroker_Context : ::Components::SessionContext { StockQuoter get_connection_quoter_info_in (); }; The locality constrained context interface above shows the new context generated for our stock broker component, which contains an operation to access the reference to a connected receptacle. Our next column will show how executors can use the operations on the context interfaces to access container-specific features, such as obtaining references to connected receptacles and using them to invoke operations (such as StockQuoter::get_stock_info()) and using the push_*() operations to publish stock names whose values have changed recently. <corbacomponent> <corbaversion>3.0 <componentrepid repid="IDL:StockBroker:1.0"/> <homerepid repid="IDL:StockBrokerHome:1.0"/> <componentkind> <session> <servant lifetime="container"/> </session> </componentkind> <threading policy="multithread"/> <configurationcomplete set="true"/> <homefeatures name="StockBrokerHome" repid="IDL:StockBrokerHome:1.0"> </homefeatures> <componentfeatures name="StockBroker" repid="IDL:StockBroker:1.0"> <ports> <consumes consumesname="notifier_in" eventtype="IDL:StockName:1.0"> <eventpolicy policy="normal"/> </consumes> <uses usesname="quoter_info_in" repid="IDL:StockQuoter:1.0"> </ports> </componentfeatures> <interface name="StockQuoter" repid="IDL:StockQuoter:1.0"> </corbacomponent> This XML component descriptor specifies StockBroker component characteristics during design and deployment time. In addition to describing the component features, such as the port names and the corresponding repository IDs for the ports, the XML descriptor specifies default values for component category, lifetime management policy, event policy, and threading policy (see [CORBA3] for more details on these policies). In CORBA 2.x applications, all the C++, IDL, and XML code shown above had to be written manually within the application to achieve the same level of functionality, which is tedious and error-prone. In CCM, much of this code is generated automatically and manipulated by various compilers and tools, which helps separate concerns and reduce development effort. Concluding Remarks This column describes the CCM container architecture and Component Implementation Framework (CIF), focusing on how these capabilities can be applied to our stock quoter example. We showed how the CCM container and the CIF support common CORBA patterns for building, configuring, and deploying distributed applications. For example, with CORBA 2.x, the management of object and servant lifecycles is a common source of errors. We explained how the CCM container architecture and CIF together support common patterns of object and servant lifecycle management. As a result, application developers simply select the desired management patterns declaratively via configuration options and tools, and the component middleware platform handles all the gory details. The result of the separation of concerns and automation provided by CCM is an overall simplification of the development, customization, and deployment of component-based applications. As we mentioned throughout this column, our next column will illustrate in detail how to apply all the CCM capabilities we discussed thus far to implement our stock quoter example using C++. As always, if you have any comments on this column or any previous one, please contact us at mailto:object_connect@cs.wustl.edu. Acknowledgments Thanks very much to Gan Deng, Tao Lou, and Jeff Parsons for their comments and improvements to this column. References [CORBA3] “CORBA Components,” OMG Document formal/2002-06-65, June 2002. [C++NPv2] D. Schmidt and S. Huston, “C++ Network Programming: Systematic Reuse with ACE and Frameworks,” Addison-Wesley, 2003. [CIAOSTAT] “CIAO Static Configuration Support for Real-Time Platforms”, http://www.cs.wustl.edu/~schmidt/ACE_wrappers/TAO/CIAO/docs/static_ciao_index. html. [CTS] “CORBA Object Transaction Service,” OMG Document formal/2003-09-02 [D&C] “Deployment and Configuration of Component-based Distributed Applications Specification,” OMG Document ptc/2003-07-08, July 2003. [EJB] “Enterprise Java Beans Technology,” http://java.sun.com/products/ejb/. [POSA1] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal: Pattern Oriented Software Architecture: A System of Patterns, Wiley & Sons, 1996. [POSA2] D. Schmidt, H. Rohnert, M. Stal, and F. Buschmann: Pattern-Oriented Software Architecture: Concurrent and Networked Objects, Wiley & Sons, 2000. [PSDL] “Persistent State Definition Language,” OMG Document formal/2002-06-65, June 2002. [PSS] Persistent State Service 2.0 Specification, OMG Document orbos/99-07-07, July 1999. [SCP] Markus Volter, Alexander Schmid, Eberhard Wolff: Server Component Patterns: Component Infrastructures Illustrated with EJB, John Wiley & Sons, Nov. 2002. [Schmidt04a] D. Schmidt and S. Vinoski. “The CORBA Component Model, Part 1: Evolving Towards Component Middleware,” C/C++ Users Journal, Feb. 2004, http://www.cuj.com/documents/s=9039/cujexp0402vinoski/. [Schmidt04b] D. Schmidt and S. Vinoski. “Object Interconnections: Defining Components with the IDL 3.x Types,” C/C++ Users Journal, April 2004, http://www.cuj.com/documents/s=9152/cujexp0404vinoski/.