Compiling OCL
Compiling OCL
Kurt Stirewalt
Computer Science and Engineering
Michigan State University
Spencer Rugaber
College of Computing
Georgia Institute of Technology
1 Problem Statement
Component-based software development attempts to gain productivity and quality ben-
efits by making use of existing code resources. But even if the existing components are
themselves reliable, the resulting assembly might not be. We would like to find ways to
improve our confidence in the assembly, while retaining the leveraging benefits. As-
sume that we start with a specified set of behavioral guarantees, called invariants, for
the target system. Our quality goal is ensure that the invariants are maintained through-
out execution. Moreover, we want to achieve this goal while satisfying the following
additional, non-functional properties.
• Transparency: The solution should refrain from intruding into the components
themselves. Transparency separates reasoning about invariants from the details of
the components’ implementations. Also, it reduces the need to modify the code
of the components, thereby lessening the risk of introducing defects.
• Flexibility: There are a variety of architectural approaches for combining com-
ponents. A flexible solution is one in which an architectural approach can be se-
2.
lected by the designer based on other desirable system properties. Moreover, flex-
ibility supports reuse, enabling components to be packaged in various ways.
• Economy: A goal of the composition process is to avoid additional run-time costs
over an ad hoc implementation. As a general rule, the more encapsulated and self-
contained the components are, the more complex is the composition mechanism
required to integrate them. With complexity comes run-time overhead. An eco-
nomical solution supports collaboration without additional run-time cost.
• Intentionality: In order to reason about system behavior, it should be possible to
relate the behavioral specification of a desired invariant to its implementation di-
rectly. In particular, each invariant should be traceable to the code mechanism re-
sponsible for guaranteeing it. Intentionality also supports maintainability—
changes to system functional requirements often mean altering system invariants.
Invariants implemented intentionally are easier to alter.
This paper describes a mechanism for assembling components into a system whose
behavior is guaranteed. The composition and its invariant properties are specified by a
designer using a subset of UML and OCL. The specified model is automatically com-
piled into a set of wrappers that enforce the desired invariant properties. The wrappers
make use of the metaprogramming features of C++ to achieve the non-functional goals
of transparency, flexibility, economy, and intentionality.
2 Solution Approach
2.1 Modeling
The component assembly process described in this paper is called DYNAMO, short for
Dynamic Assembly from Models. DYNAMO supports model-based specification of
component assemblies. What this means is that a designer specifies an assembly in a
high-level, declarative notation rather than operationally in a programming language.
The notation we have used is the Unified Modelling Language1 (UML) [12] including
the Object Constraint Language (OCL) [21]. Moreover, we have interpreted UML class
model constructs in terms of the vocabulary of software architecture2. (See Table 1.)
Annotations to the class model, in the form of OCL constraints, provide semantics. In
particular, handlers for external system events (stimuli) are ultimately modelled as
methods in a component. OCL pre- and post- condition constraints specify the effect of
events on the system. Invariants, initially indicated with natural language annotations,
are first translated by the designer into OCL annotations to associations. (The UML rule
restricting invariants to classifiers is relaxed for this step only.) As the architecture is
refined, associations are subsumed by DYNAMO’s layered architecture. At this point,
each constraint is assigned to the component responsible for maintaining it.
2.3 Architecture
In DYNAMO, desired system properties are expressed as invariants using OCL. When an
external stimulus perturbs the state of the system, invariants must be re-established. We
also wish the process to satisfy the non-functional constraints (transparency, flexibility,
economy, and intentionality) described above. We call this process invariant mainte-
nance. DYNAMO addresses the invariant-maintenance problem by compiling the OCL
invariants into wrappers that transparently notify dependent components when they
need to take action to re-establish an invariant. In particular, DYNAMO components are
organized into a layered, implicit-invocation architecture. The order of layers is deter-
mined by the navigation paths occurring in the OCL constraints, thereby improving in-
tentionally. Implicit invocation, because it is provided by wrappers, enhances transpar-
ency. Both improvements add to flexibility and reusability. The implementation ap-
proach described in the next section addresses the issue of economy.
A DYNAMO design comprises a layered set of components. For each component,
event-handling methods, percepts3, and OCL constraints are identified. The compila-
tion process takes these three elements as input and produces wrapper code as output.
At run-time, the wrappers detect and propagate events and update dependent compo-
nents, thereby maintaining system invariants.
3 Metaprogramming Implementation
DYNAMO implementation takes advantage of the metaprogramming features of C++.
3. A percept is a unit of presentation that communicates system state to the end user.
4.
Specifically, component wrappers are implemented as layered C++ class template in-
stantiations. A class template is a parameterized class definition, where the parameter
is usually another class. Moreover, the parameter can be used as the base class of the
template class thereby enabling components to be stacked into layers. When combined
with C++’s compile-time inlining mechanism, much run-time overhead can be avoided.
This section describes how OCL constraints are realized as generated C++ wrappers.
To do so, DYNAMO makes use of two devices—status variables and mode components.
4. There are some non-constructive constraints for which this may not be possible. They
are discussed in section 4.5.
6.
5. Note that the _Top and _Bot suffixes on template class names refer to their roles in
the layered architecture and not to their roles in the inheritance hierarchy. That is, the
_Top wrapper provides services that communicate with a component above it in the lay-
ered architecture. The relative nesting of the templates is actually in the inverse order to
their position in the layering.
7.
6. To simplify the diagram, the template classes themselves and the corresponding
«bind» dependencies are not shown.
9.
StatusVariable<int>
d : int
operator=() : int
operator int()
b
SV_B _b <int>
B _To p <B > updater1P : U pdater
BindsU pdater
operator=() : int
getValue_b() : int setU pdater1() : void
bind_b_1(in scp : int) : void
N otifies
«interface»
U pd ater
A
update1() : void
DYNAMO designs are expressed using an OCL-capable UML modeling tool such as
Rational/IBM [8] or ArgoUML [19]. These tools support the export of diagram content
11.
4 Evaluation
4.1 Transparency
What alterations to the source code of existing components are required in order to
make them into mode components? Only one change is necessary on the part of a pro-
grammer—the types of status variables must be adjusted. That is, member variables of
components upon which other components are dependent must be so designated. Two
scenarios can be imagined. In the first, the original designer of a component library is
seriously concerned with reuse. Components are developed, and potentially interesting
status is declared as such in the component code. The second scenario is the adaptation
of an existing component into a mode component. In this case, the adaptor must not only
decide what facilities of the component are required of other components, but must also
locate the definitions of these variables in the code, so that their types may be altered.
In both scenarios, the coding effort required of the developer consists of adding some
#include statements and changing the types of the status variable declarations. Any
scheme for intercomponent invariant maintenance must provide access to the constitu-
ent state. Hence, we judge the mode component approach to be adequately transparent.
4.2 Flexibility
The DYNAMO approach is flexible in several senses. First is the fact that alternative com-
ponents with the same APIs can be substituted for each other. Moreover, additional
component can be inserted to provide optimizations and other enhancements. These
added or substituted components simply amount to interpolated templates in the C++
code. DYNAMO is also flexible in a different sense. Mode components are not the only
scheme for maintaining invariants. For example, mediators [17] provide many of the
same features. More conventional approaches to invariant maintenance in C++, such as
aggregated components with embedded pointers and explicit delegation can also be
used. The DYNAMO compilation architecture has been successfully applied to these al-
ternative approaches. That is, the DYNAMO compilation approach is flexible with re-
spect to the specific mechanism for updating status to maintain invariants.
4.3 Economy
Flexibility normally leads to overhead. Typically, flexibility is achieved by using indi-
rection through pointers. Using pointers implies dereferencing, which, in turn, means
an extra operation on every access. Our approach reduces overhead by making use of
two features of C++: template classes and inlining.
Components are normally constructed independently and encapsulated in their own
classes. This reduces coupling and enhances maintainability. But, because components
12.
need to interact, they often hold pointers to each other. Another approach is to have one
component be a subclass of another. Then the subordinate can directly access the fea-
tures of the superordinate component without the pointer overhead. But such an ap-
proach is intrusive and unnatural. Mixin inheritance is an alternative to subtyping—a
mixin adds a feature to a class without requiring that the mixin be an explicit subtype.
The other C++ feature that can reduce overhead is inlining. Normally, the compila-
tion of a method call introduces significant overhead at the calling site. The C++ com-
piler can detect situations where a copy of the code for the called method can be inserted
directly at the call site without the associated overhead. This technique is particularly
applicable when the method code is short, such as obtains with instance-variable access
routines (getters and setters). In this way, components can retain their encapsulation
without engendering normal intercomponent communication overhead. Templates and
inlining enable our approach to provide low overhead invariant maintenance.
4.4 Intentionality
The overarching goal of the DYNAMO work on component assembly is to increase as-
surance. It accomplishes this by providing an invariant-maintenance mechanism. Invar-
iants are directly manifest in the code. In particular, each independent variable in each
invariant results in the generation of a status-variable wrapper to provide change noti-
fication and an update method to re-establish the invariant when one of its constituents
changes. Because this code is generated, it is possible for the designer to have confi-
dence that the specification is being met. Hence, the approach is intentional7.
4.5 Limitations
The DYNAMO approach, while satisfying the above-described non-functional goals, is
not without limitations. Some of these are described here.
• Loss of symmetry: Components nested as template mixins are inherently asym-
metric. This loss of flexibility is compensated for by the reduced overhead they
require.
• Constructiveness: Not every invariant can be expressed as a mode component
constraint. Constraints in which a single variable appears on the left hand side8
are called constructive. This is a theoretical limitation of the approach that has not
proven a problem in practice.
• Circularities: More serious are cyclically dependent constraints, as for example,
happens if variable a depends on variable b in one constraint, and variable b de-
pends on variable a in another. Run-time update of one variable can lead to an
infinite cascade of invariant re-establishments. In DYNAMO, such co-dependen-
cies can be grouped into the same mixin layer, providing a symmetric solution.
5 Related Work
There are a variety of design strategies for maintaining invariants among an assembly
of components. At one extreme, an invariant can be implemented as an explicit integra-
tion component, distinct from the components it integrates (hereafter referred to as its
integrands). Under this approach, the integration component might be a peer of its inte-
grands, as is the case with mediators [17], or it might encapsulate its integrands, as with
GenVoca layers [1]. Some designs even employ a hybrid of these approaches. For ex-
ample, Java AWT programmers define containers, which (like layers) encapsulate GUI
components but which (like mediators) listen for events from these components [7]. At
the other extreme, an invariant can be implemented as a collaboration [20], which dis-
tribute the responsibilities for maintaining the invariants among the integrands. An al-
ternative to choosing an invariant maintenance mechanism at the time when the code is
written is delaying the decision until assembly time. This has been called the flexible-
packaging problem, and an approach to providing it is described in [5].
DYNAMO makes use of the template processing mechanism of the C++ compiler to
obtain its metaprogramming functions. An alternative approach is provided by the Open
C++ project [3]. Open C++ adds the meta-object protocol to the C++ compiler. That is,
programmer have the ability to reprogram the compiler by, for example, telling it what
to do when it sees a new construct, such as a MonitoredClass. This construct might
be realized with code that counts method calls or variable updates. The metaprogram-
mer is responsible for using available features of the Open C++ API to write metapro-
grams for doing the counting. We have successfully applied this tool to generate DYNA-
MO status variable updates, so it would seem to provide a viable alternative to the tem-
plate program approach described in this paper. A survey of other work on invariant
maintenance can be found in reference [15].
On the issue of implementation, currently, the most complete OCL compiler comes
from the Dresden University of Technology and supports OCL 1.4. To support OCL
2.0, the Dresden development team is redesigning their compiler as described in refer-
ence [9]. The Dresden compiler features a MOF (Meta Object Facility) Repository that
manages models and meta-models by providing interfaces for their access. The code
generator itself is designed to take instances of the OCL metamodel as input and output
Java code without altering the state of the environment.
Acknowledgments
The PIs on this project wish to thank the following student participants: Jonathan Gda-
levich, Corinne McNeely, Terry Shikano, Patrick Yaner, and David Zook from Georgia
Tech and Reimer Behrends and AliReza Namvar from Michigan State. We also wish to
thank colleague Laura Dillon from Michigan State. This effort was sponsored by the
Defense Advanced Research Projects Agency, and the United States Air Force Re-
search Laboratory, under agreement number F30602-00-2-0618. Other support was
provided by Office of Naval Research grant N00014-01-1-0744 and by NSF grants
EIA-0000433 and CCR-9984726.
References
[1] D. Batory and S. O’Malley. “The Design and Implementation of Hierarchical Software
Systems with Reusable Components.” ACM Transactions on Software Engineering and
Methodology, 1(4):355–398, October 1992.
[2] Gilad Bracha and William Cook. “Mixin-based Inheritance.” Proceedings ECOOP/
OOPSLA '90, October 21-25, 1990, 303-311.
[3] Shigeru Chiba. OpenC++ Home Page. http://www.csg.is.titech.ac.jp/~chiba/
openc++.html.
15.