CCM-tutorial-Schmidt-Vinoski-CUJ

advertisement
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/.
Download