Formal Model Driven Approach to Developing Enterprise

Formal Model Driven Approach to Developing Enterprise Systems
Risto Pitkänen
Tampere University of Technology
Institute of Software Systems
P.O. Box 553, FIN-33101 Tampere, FINLAND
[email protected]
Abstract
Developers of distributed enterprise systems face the
challenges of ever-increasing complexity and heterogeneous, constantly evolving platforms. One approach to coping with these is model driven development, in which refinement, model execution and code generation pave the
way from an abstract model to a specific implementation.
In this paper, a model driven approach built on a formal refinement-based specification method is introduced.
The approach comprises a specification language for
computation-independent and platform-independent modeling as well as model execution and code generation tools.
Its advantages include simple yet precise semantics, welldefined refinement, and clear model relationships. The approach is reflected against OMG’s Model Driven Architecture (MDA), and its use is demonstrated by developing a
non-trivial example application.
1. Introduction
Development of distributed enterprise systems has been
facing a crisis due to ever-increasing application-level complexity combined with heterogeneous and constantly evolving platforms. Many professionals agree that it is advisable to avoid the use of implementation-level concepts in
early model entities such as UML diagrams. When modeling a system that is to be implemented using Enterprise
JavaBeans [16], it may seem only natural to resort to concepts such as Remote, Local and Home interfaces, or to divide components to session and entity beans. Focusing on
platform-specific details too early, however, seriously complicates modeling and makes it hard to implement the system on some alternative platform. For example, when developing a specification model of an information system for
a public library, one should initially concentrate on concepts
such as books, customers, loans and reservations, and what
can be done with them, and not on whether a loan is an
entity bean or not and which kinds of factory methods its
Home interface provides.
The OMG Model Driven Architecture (MDA) [14] is
an effort to formalize and even automate the above advice.
MDA is essentially an approach to using models in specification, design and implementation of heterogeneous systems. Literature mentions model executability, refinement
and automatic code generation as techniques that pave the
way from an an abstract MDA Computation Independent
Model to an actual implementation [15, 3, 4]. So far, MDA
has mostly been studied from the viewpoint of using OMG
Universal Modeling Language (UML) [12], although the
MDA Guide [14] is careful not to restrict MDA to modeling
in UML. In some respects, UML is not an ideal language for
MDA. For example, it is large and complicated, precise operational semantics are not yet widespread, high-level models tend not to be executable, and model refinement is not
precisely defined.
We propose an approach to developing distributed enterprise applications that resembles MDA in many respects,
and could even be argued to be an instance of MDA. Instead of UML, we rely on an action-oriented specification
language, which allows formal refinement of models and
verification of model properties. Our models are executable
even at the very highest levels of abstraction. Safety properties are preserved by construction in model refinement, and
critical properties can be formally verified by using theorem
proving or model checking. Models can also be validated
and tested by executing them in an animation tool. There
is also an experimental code generator and plans for other
advanced tools.
To reflect our approach against the mainstream of model
driven development, we introduce OMG MDA in Section
2. Section 3 gives an overview of our approach and how it
relates to the MDA framework. Computation Independent
modeling in our approach is discussed in Section 4. Platform Independent Modeling is described in Section 5. Platform specific issues are treated in Section 6, and Section 7
concludes the paper with a summary and some discussion.
2. OMG Model Driven Architecture
OMG MDA has been described as an approach to using
models in software development [14], or using modeling
languages as programming languages [7]. It is based on the
well-known idea of separating the specification of a system
from the details of its implementation, particularly details
that arise from the platform the system operates on.
In MDA, models are not just specifications of implementations, but in fact the most important products of a software
project. Models do not become obsolete as soon as an implementation is obtained, but they are used through the entire lifecycle of a system, from initial analysis and design
to operation and maintenance. With adequate tool support,
an implementation might even be generated automatically
from a model.
MDA specifies three viewpoints to a system: a Computation Independent, a Platform Independent, and a Platform
Specific viewpoint. Models are to be written according to
each viewpoint. Figure 1 depicts the relationships of different models.
Computation
Independent
Model (CIM)
Platform
Independent
Model (PIM)
Platform
information
+
Platform
Specific
Model (PIM)
Figure 1. Model relationships in OMG MDA.
A Computation Independent Model (CIM) employs concepts and vocabulary of the problem domain, specifying high-level requirements and/or operation of the system without delving into details of structure or processing.
A CIM specifies the system within its operating context,
e.g. the business environment in which an enterprise information system is used.
A Platform Independent Model (PIM) specifies the structure and operation of a system in a way that is independent
of platform-specific details to a specified degree. A PIM
can for example be targeted towards a platform-independent
virtual machine, or to a class of platforms offering similar
kinds of basic services, such as distributed component platforms [6]. MDA does not define the difference or relationship between a PIM and a CIM very precisely; PIMs are
just more implementation-oriented and detailed, and they
are assumed to relate to CIMs in a meaningful way.
A Platform Specific Model (PSM) is obtained by combining the details arising from a specific platform with a
PIM as depicted in Figure 1. The result is (a model of) an
implementation of the system for that particular platform.
In an ideal world, the mapping from a PIM to a PSM is
done automatically by an MDA mapping tool, based on a
model of the target platform and perhaps some guidance
from the developer. The PSM might be expressed using program code, or it could be input to a code generator, which
produces an actual implementation of the system, or part of
it.
As pointed out in [3], precise semantics are a prerequisite for code generation. If the semantics of a model are
unclear, code generation can of course be attempted, but
the result is likely to be something that was not intended.
UML models do not have precise semantics, but a solution
exists in the form of Action Semantics [13], UML extension. Action Semantics as such do not define a concrete
syntax in which details about processing can be included in
a model, but such a syntax is specified in e.g. [17]. Precise
operational semantics also make models executable, which
enables model testing, early validation and model prototyping.
Mappings may also be utilized for refinement of models.
Several PIM-to-PIM transformations may be done before
the step to a PSM. Refinement is not precisely defined in
UML; it is just considered a process that produces a more
detailed model in some respect. Refinement may e.g. involve implementing a compound operation as a sequence
of simpler messages.
Using UML as a modeling language with MDA is not
without its problems. Use of Action Semantics is not
widespread yet, and its lack of standard concrete syntax is
somewhat disturbing. UML has a respectable set of powerful notations, but does not in itself provide guidelines on
how to use the notations effectively. The sheer amount of
different notations and concepts and metalevels may cause
a steep learning curve for those who want to master UML
at a level necessary for utilizing advanced MDA capabilities. In addition, many UML notations have their roots in
object-oriented programming, which means that developers
may to use them to express implementation-level concepts
even in computation-independent models, leading to unnecessary complexity [10].
3. Overview of Our Approach
Our approach is similar to OMG MDA, but we do not
employ UML for modeling. This choice is motivated by
our endeavor towards well-defined refinement and formal
semantics, simplicity, orthogonality and executability.
Thus far we have focused on one domain: enterprise systems. Our PIMs are therefore targeted towards enterprise
computing platforms such as CORBA and Enterprise JavaBeans. Our approach is summarized in Figure 2.
refinement
CIM
(DisCo)
Model
Execution
Tool
refinement
PIM
(one or more components)
(TransCo)
code
generation
PSM (generated)
(currently EJB)
External Components
sion interfaces which may be used to customize their functionality or add calls to external components.
A central part of the approach is a model execution
tool, the DisCo Animator [1], to which we also plan to
add support for TransCo and model/implementation interaction. This would enable synchronized execution of a model
and its implementation, driving an implementation from a
model, using a model as a substitute for test stubs with a
partial implementation, observing and obtaining feedback
from a running system, and other advanced activities.
This work builds on existing research on the DisCo
method, language and tools. TransCo and its EJB implementation scheme are new contributions of this paper.
4. Computation Independent Modeling
Figure 2. Our model driven approach.
Our CIMs are written in the DisCo (Distributed Cooperation) language, which will be introduced in Section
4. A model is produced incrementally using stepwise refinements, leading to hierarchical abstractions and aspectoriented views to the system and its environment. Despite
its familiar-looking syntax to programmers, DisCo has formal semantics and support for automated verification of
model properties. Informal reasoning based on its operational semantics may also be used. Furthermore, refinements preserve safety properties, which simplifies both formal and informal reasoning.
Once a sufficiently detailed CIM has been obtained, a PIM is produced using TransCo (Transactional
Components), an extension of DisCo. This model may consist of several components, which together form a (partial1 )
refinement of the DisCo CIM. TransCo could be compared
to marking a model [14], or described as a very high-level
platform-independent transactional programming language
for enterprise systems.
An experimental code generator produces Enterprise
JavaBeans implementations from TransCo PIMs. Support
for other enterprise platforms such as CORBA can be added
in the future. Depending on one’s point of view, we either
skip the PSM phase or use Java as our PSM language. Nevertheless, the PIM includes all the information needed for
implementation, and the EJB code generator includes the
EJB platform model, which corresponds to automatic model
transformation [14] in OMG MDA. To facilitate integration
with components not modeled using our approach, the generated implementation is extensible. In addition to ordinary
EJB Remote interfaces through which external components
may call services, the generated components expose exten1 Partial in the sense that environment part of the DisCo model is not
included in the TransCo model.
DisCo [8, 5] is a formal specification language designed
for reactive and distributed systems. Based on action systems [2] and Temporal Logic of Actions [11], it allows stepwise refinement by safety-preserving superposition. DisCo
is suitable for modeling of enterprise systems partly because
it is action based. Actions may be considered abstractions
of transactions, which are usually needed in enterprise applications. A recent survey article reviewing many of the
ideas behind DisCo is [9].
Our choice of using DisCo instead of UML leads to a
few methodological consequences. Our CIMs or PIMs do
not consist of separate static, dynamic or other views. There
are no numerous different notations, but just one relatively
simple specification language.
DisCo models are written using relatively fine-grained
well-defined refinements and they are structured in an
aspect-oriented manner, enabling utilizing different levels
of abstraction and separation of concerns. Even CIMs are
always executable and tool support exists for model execution. Formal verification of critical properties is also supported.
We shall introduce the basic concepts of DisCo using a
running example describing a specification of a library. The
same example is mapped to TransCo components in Section 5 and automatically generated EJB implementations of
those components are obtained using a code generation tool,
described in Section 6.
4.1. Layers
DisCo specifications consist of layers, which can be seen
as aspect-oriented units of modularity, allowing separation
of concerns in specification. Our aspects are not to be confused with aspects of aspect-oriented programming (AOP),
which are implementation-level entities. DisCo specification aspects may or may not be implemented using AOP.
Furthermore, layers enable stepwise refinement, or incremental specification development.
Our model of a library is developed using several layers.
The first layer specifies the collection management aspect
of the system:
layer collection_management is
The layer will introduce the concepts of titles and copies,
and allow adding and removing both from the library collection.
4.2. Classes
Data types are modeled using classes much like in ordinary object-oriented languages. The following specification
fragment introduces classes Title and Copy:
dynamic class Title is
isbn: String;
author: String;
title: String;
end;
dynamic class Copy is
of_which: reference Title;
id: integer;
end;
The above piece of specification is self-explanatory apart
from a few details. DisCo classes do not have methods.
Objects, instances of classes, are just collections of data.
Methods are replaced by multi-object actions. In our library, class Title represents a certain title such as “The
Art of Computer Programming, vol. I” by Donald Knuth,
while Copy represents a particular physical instance of a title indicated by a reference (of_which) to the corresponding Title.
Keyword dynamic in a class definition indicates that objects of the class may be dynamically created and deleted.
4.3. Actions
Actions are atomic units of execution consisting of action name, parameters, a guard, and a body. Parameters
can be objects, in which case they are called participants, or
plain values such as integers or character strings. A guard is
a boolean-valued expression. An action is said to be enabled
if its guard evaluates to true for some parameter combination. There is no explicit control flow; instead, execution
proceeds by nondeterministic selection of one enabled action at a time and executing its body for some parameter
combination its guard evaluates to true for. Concurrency
is thus modeled in an interleaving fashion. Allowed executions can be restricted by fairness and real-time requirements, which are, however, not used in our example.
Action add_title models adding a new title to the collection:
action add_title(t: new Title; isbn, author, ti: String) is
when not (∃ t2: Title :: t2.isbn = isbn) do
t.isbn := isbn || t.author := author || t.title := ti;
end;
Action add_title has four parameters, one of which (t:
new Title) is a participant, the rest being values of type
String. Its guard requires that there exists no other title
with the same ISBN as the title to be added. Its body is a
parallel assignment which initializes the fields of the new
title with the parameter values. Keyword new before the
type specification of participant t means that this participant
is a new dynamically created object.
We want to emphasize that actions are never explicitly
called. Execution just proceeds by nondeterministic selection of any enabled action and any legal parameter combination. In an implementation this would of course not be the
case, but the purpose of a specification is to model precisely
all allowed executions. Removing control flow and concepts such as “caller” and “callee” greatly simplifies specifications.
Action remove_title removes a title from the collection:
action remove_title(t: Title) is
when not (∃ c : Copy :: c.of_which = t) do
delete t;
end;
Note that the guard requires that no copies of the title exist,
i.e. any copies need to be removed first.
The rest of layer collection_management is specified
as follows:
action add_copy(t: Title; c: new Copy; id: integer) is
when not (∃ c2: Copy :: c2.id = id) do
c.of_which := t || c.id := id;
end;
action remove_copy(c: Copy) is
when true do
delete c;
end;
end collection_management;
Copies of any Titles in the collection can be added as long
as they are given globally unique ids. Existing copies can
be removed from the collection at any time.
4.4. Refinement
Layer structure of the library CIM is depicted in Figure 3. Each arrow depicts a refinement relation, and where
two arrow tails meet, there is an implicit composition layer
which combines two specification branches. We will explain the contents of all layers after first showing how refinement works in detail.
Layer customer_management specifies a very simple aspect of the library that is orthogonal with the aspect specified by collection_management:
layer customer_management is
dynamic class Customer is
collection_
management
customer_
management
dynamic class Loan is
customer: reference Customer;
target: reference Copy;
end;
loans
reservations
layer loans is
import collection_management, customer_management;
due_dates
complete_
library
refines
Figure 3. Layer structure of the library CIM.
id: integer;
name: String;
end;
action add_customer(c: new Customer; id: integer;
name: String) is
when not (∃ c2: Customer :: c2.id = id) do
c.id := id || c.name := name;
end;
action remove_customer(c: Customer) is
when true do
delete c;
end;
end customer_management;
There are no new language concepts in the layer, so we
proceed straight to layer loans, which refines the composition of the two layers given above. This is expressed by an
import statement in the beginning of the new layer:
layer loans is
import collection_management, customer_management;
In this case the result of composition is simple: all classes
and actions of both layers become part of the composition.
DisCo has special rules for what is allowed in refinement
and what is not. Obeying the rules guarantee that all safety
properties (of the form “something bad never happens”, e.g.
“there are never two titles with the same ISBN in the collection”) are preserved by construction. Rules required in our
example are summarized in the following:
• New classes and actions may be introduced. However,
new actions may not assign to existing variables, but
only variables introduced in the current layer.
• Existing classes can be extended by adding new variables.
• Existing actions may be refined by adding parameters,
strengthening guards by adding new conjuncts, and
adding assignments to newly introduced variables to
action bodies.
Layer loans adds class Loan and two actions to handle
instances of it:
action do_loan(cu: Customer; co: Copy; l: new Loan) is
when not (∃ l2 : Loan :: l2.target = co) do
l.customer := cu ||
l.target := co;
end;
action return_loan(c: Copy; l: Loan) is
when l.target = c do
delete l;
end;
end;
We observe that loans apply to copies, and titles are not
mentioned here. Notice that classes from both imported layers are referred to, but their variables are not assigned to in
the actions.
The above superposition step was very simple, because
existing classes or actions were not touched. Let us look at
a fragment of layer due_dates to see an example of a little
more sophisticated refinement:
extend Customer by
fees: integer;
end;
extend Loan by
due_date: time;
end;
refined do_loan(cu: Customer; co: Copy; l: new Loan) of
do_loan(cu, co, l) is
when ... cu.fees < BAN_LIMIT do
...
l.due_date := now + LOAN_TIME;
end;
Here Customer is augmented with a variable indicating the amount of overdue fees on the customer’s account,
and each Loan is extended by a field due_date indicating
when the loan has to be returned by the latest. Action
do_loan is refined by strengthening its guard (a customer
may only loan something if her overdue fees do not exceed
constant BAN_LIMIT defined elsewhere in this layer) and
adding an assignment which records now + LOAN_TIME in
the due_date field of the new loan. DisCo has facilities for
specifying real-time properties, an implicit parameter now
of type time in each action being one of them.
4.5. Rest of the Specification
The rest of the specification layers are briefly described
in the following:2
Layer due_dates includes, in addition to the fragment
shown above, definitions of a few constants, a refined
2 The complete specification is available at http://disco.cs.tut.
fi/examples/library/.
version of action return_loan which checks if the
loan time has expired, and if so, adds an overdue fee
to the customer’s account, and a new action pay_fees
which decrements the fee balance of its participating
customer by an amount specified by an integer parameter.
Layer reservations introduces class Reservation,
which contains references to a reserving customer and
a reserved title. A new action reserve is enabled
whenever all copies of the title to be reserved are
currently in loan. Action do_loan is refined by two
separate actions: an ordinary do_loan, whose guard
is strengthened to require that there are more copies
of the title available than pending reservations, and
fetch_reserved, which has a new participant of
class Reservation, and whose guard requires that
the Reservation has been made by the participating
customer and its target is a Copy of the reserved
Title.
Layer complete_library is an empty refinement of the
composition of layers reservations and due_dates.
The composition rules of DisCo cause classes and actions that have common ancestors in the specification
hierarchy to be combined, which results in a library
whose do_loan action, for example, contains the refinements given in both ancestor layers.
The complete library specification is being executed in
the DisCo Animator in Figure 4. Note that also any earlier
layer can be animated. Animator shows the current states of
specification objects and allows the user to select enabled
actions for execution. The screen has been grabbed at the
moment of execution of action return_loan. Execution
history is depicted graphically as a kind of a generalized
sequence diagram.
5. Platform Independent Modeling
Code generation directly from a DisCo model has been
attempted before, but it does not seem feasible in the general case. The gap between DisCo and program code is too
wide: code generation cannot produce realistic and efficient
implementations. The developer has to supply at least some
additional information.
The fact that direct code generation from DisCo is not
feasible also leads to a natural definition of a CIM in our
approach: any model written in plain DisCo is considered a
CIM.
One possibility for expressing the required extra information is to extend DisCo with language constructs capable
of expressing sufficient detail for code generation. This is
the approach we have chosen.
Figure 4. DisCo Animator.
The new language constructs shall not be used inside ordinary DisCo layers, but only in a refinement of the complete CIM. Thus, we add an extra level of refinement below
the most detailed DisCo specification, as depicted in Figure 2. Furthermore, certain DisCo constructs (such as actions) are too abstract to be directly implemented and are
not allowed at the PIM level. For convenience, we call the
language subset only available at the CIM level DisCo, and
the PIM subset TransCo. According to our definition, any
model written in TransCo is considered a PIM.
The most important implementability-related requirements for TransCo are summarized in the following:
Partitioning. TransCo has to enable partitioning of a
DisCo specification into system and environment parts,
and further partitioning of the system part into one or
more components with well-defined interfaces. Components are needed in order for the system to be efficiently distributable.
Parameter binding. Let us consider action return_loan
of the library example. In DisCo, any combination of
Copy and Loan satisfying the guard condition may be
nondeterministically picked, while in an implementation, the user would provide one of the participants and
the other would be computed thereof. TransCo has to
provide facilities for binding parameters by computing
them based on other parameters.
Control flow. The execution model of DisCo has been designed from the viewpoint of specification. Substituting nondeterminism for control flow is not feasible in
an implementation. In TransCo, more explicit control
flow is required.
The following subsections introduce TransCo language
constructs and discuss how the above requirements are fulfilled.
5.1. Components
The step from a DisCo CIM to a realistic and efficient
distributed implementation requires two kinds of partitioning. First, the model has to be partitioned into system and
environment parts. This is done in TransCo in a straightforward manner: whatever is not specified in the PIM is considered part of the environment. The CIM still documents
possible assumptions about environment behavior.
The system part is further partitioned into components
with well-defined interfaces. This is a process entirely up to
the developer. Often the layer structure of the model leads
to a useful partitioning, but sometimes some other way of
dividing the system into components is more appropriate.
In our library example, a suitable partitioning arises
more or less directly from the layer structure. The library
will consist of components collection_management and
customer_management, mutually orthogonal aspects easily
modularized as components, and loan_management, which
combines features from layers loans, reservations and
due_dates.
Let us begin with component collection_management.
In TransCo, we first indicate the name of the component
and the name of the DisCo layer that induces the complete
CIM:
attribute author : String;
attribute title : String;
unique key (author, title);
end;
Keys provide means of obtaining references to objects.
They are analogous to database keys, but function in an
object-oriented fashion. Keys in TransCo are conceptually
simple, declaring and using them is straightforward, and
there are no separate storage interfaces such as EJB or CCM
“Homes.”
We indicate that attribute isbn is the primary key of
class Title above. A class operation (i.e. an operation that
can be invoked without access to a specific instance of a
class) _find(String) is now automatically generated into
class Title. For example Title._find(’’123456789’’)
returns a reference to a Title object with ISBN number
123456789, or null if no such title exists. An additional
unique key consisting of fields author and title is defined, leading to automatic generation of a class operation
_find_by_author_title(String, String). Note that
keys consisting of just one field can be defined by including the key specifier in parentheses after the attribute type,
while compound keys can be defined separately after the
attributes they consist of.
Class Copy is implemented in a similar manner. In this
case we define a non-unique key as well:
class implementation Copy is
attribute of_which : reference Title (key);
attribute id : integer (primary key);
end;
A class method _find_by_of_which(Title) is generated, but as the key is not unique, its return type is set Copy
(TransCo for set of copies) instead of Copy.
component collection_management of complete_library is
In TransCo, we can define variables inside component
scope. We will implement the selection of unique id’s for
Copy objects as required in the CIM with a running index:
var next_free_id : integer := 1;
5.2. Classes, Attributes and Keys
There are two kinds of classes in TransCo: a class can
either be an implementation of a CIM class (indicated by
the keyword implementation) or a new class not part of
the CIM. The name of a class implementation must correspond to some class in the CIM, and it must implement the
variables of the corresponding CIM class as attributes or
operations. Roughly the same rules of superposition apply
to the DisCo-to-TransCo step as to DisCo-to-DisCo steps,
and safety properties are thus preserved. Classes may also
define keys as in the following:
class implementation Title is
attribute isbn : String (primary key);
5.3. Interfaces and Transactions
Components may expose one or more named interfaces.
An interface consists of transactions. Transactions are
atomic compound operations that may be called by a client.
The client is another transaction or the environment. Analogously to classes, there are two kinds of transactions,
namely the kind that are implementations of CIM actions,
and the kind that are new at the PIM level.
Component collection_management exposes one interface, default, which is a special interface name meaning
that the name of the component itself refers to this interface
in a context in which an interface name is required:
interface default is
Transactions look a lot like actions. A transaction has a
parameter list and a combination of a guard and a body:
transaction add_title(isbn: String; author: String;
title: String)
t: new Title;
of complete_library.add_title(t, isbn, author, title)
is
when Title._find(isbn) = null;
t.isbn := isbn;
t.author := author;
t.title := title;
end
A transaction may also have local definitions and an action call (complete_library.add_title(t, isbn, author, title)) as above. If an action call is present, the
transaction is considered to be an implementation of the
“called” CIM action. An action call is required in order
to bind formal parameters of an action to parameters and
local definitions of a transaction. This binding is needed in
order to define the refinement relationship between a PIM
and a CIM. An action call does not, however, add to the
functionality of a transaction.
Unlike an action, a transaction is actually called, and
the caller supplies only those parameters that are the required inputs. Compare this to a DisCo action that indicates all its participating objects and required values as its
parameters, regardless of whether they are “inputs”, “outputs” or something else. In the case of add_title, isbn,
author and title are input values, while t: new Title
is the object to be created. Therefore, only the former
three are listed as transaction parameters, and t is defined
as a local reference to a new Title object, while the action call complete_library.add_title(t, isbn, author, title) binds action formals to the parameters and
the local.
The body of transaction add_title contains a guard
statement (when. . . ) and three assignments. The body
is executed using transactional semantics: if any of
its statements fails, the whole transaction is aborted.
The TransCo expression Title._find(isbn) = null is
logically equivalent to the action guard not ∃t2: Title :: t2.isbn = isbn in DisCo. There may be several
guard statements, and their conjunction should imply the
original action guard.
Unlike action bodies in DisCo, transaction bodies are sequential to simplify implementation. Locals can be used as
auxiliary variables if the assignments of the original action
implemented by a transaction have cyclic dependencies.
Transaction remove_title is a straightforward implementation of its corresponding action:
transaction remove_title(t: Title)
of complete_library.remove_title(t)
is
when Copy._find_by_of_which(t) = {};
delete t;
end;
Again,
Copy._find_by_of_which(t) = {}
is equivalent to the original action guard,
not (∃c: Copy :: c.of_which = t).
Transaction add_copy only requires a Title object to be
passed as a parameter, while the copy to be created and its
id are implemented as locals:
transaction add_copy(t: Title)
c: new Copy;
id : integer := next_free_id;
of complete_library.add_copy(t, c, id)
is
when Copy._find(id) = null;
next_free_id := next_free_id + 1;
c.of_which := t;
c.id := id;
end;
Notice how the originally nondeterministically chosen
value id is now replaced by a running index. If we wanted,
we could omit the guard statement altogether, obtaining a
proof obligation that the expression of the guard is an invariant.
Finally, transaction remove_copy is again a straightforward implementation of the action with the same name:
transaction remove_copy(c: Copy)
of complete_library.remove_copy(c) is
delete c;
end;
This concludes the interface and the component:
end default;
end collection_management;
5.4. More on Transactions
The actions to be implemented by transactions are the
ones of the final, complete CIM. For example, transaction return_loan implements the final version of its corresponding action, i.e., it also contains code derived from
layer due_dates:
transaction return_loan(c: Copy)
l: Loan := Loan._find_by_target(c);
cu: Customer := l.customer;
of complete_library.return_loan(c, l, cu)
is
when l /= null; -- note!
if now > l.due_date then
cu.fees := cu.fees + OVERDUE_FEE;
end if;
delete l;
end;
end;
An interesting detail to notice here is that local l: Loan
is initialized as Loan._find_by_target(c). Specifying a
copy to be returned determines the loan object as well, as
only one loan per copy can exist at the same time. Furthermore, a reference to the needed customer object is obtained
from the loan object just fetched. Thus, parameters of the
original action are bound to the Copy object passed as a parameter to the transaction and the Loan and Customer objects computed thereof. Hence the guard of the original action l.target = c ∧ l.customer = cu may be simplified to l /= null, which together with the initialization
implies the original guard. Of course, to be formal, this
should be proved.
5.5. Components, Environments and Calling Transactions
Java package
TransCo component
class
interface
stateless session bean
(XDoclet format)
transaction
6. Platform Specifics
A PIM written in TransCo could be executed directly, but
the language has been designed with modeling in mind. It
has an overly restricted set of concepts to be an all-purpose
programming language, there are no libraries to speak of,
and no run-time environment or distribution middleware
that support it exist. Therefore a better choice is to use code
generators, which produce implementations (PSMs) which
utilize existing languages and middleware platforms.
Our first target platform is Enterprise JavaBeans (EJB).
We have an experimental code generator, which produces
extensible EJB implementations of TransCo components.
The basic EJB code generation strategy depicted in Figure 5 is quite simple. Each TransCo component becomes a
package containing entity beans and session beans. XDoclet is used in conjunction with the Ant build tool to simplify code generation. XDoclet is a tool that enables declarative EJB programming using a single Java source file per
bean, and removing the need for manually providing separate deployment descriptors or local, remote and home interfaces.
TransCo classes become entity beans whose persistence
is managed by the EJB container. Interfaces become stateless session beans with methods corresponding to transactions of the interface. Container-managed or bean-managed
transactionality (the latter is currently not supported by our
code generator) is used to execute each session bean method
as a distributed transaction.
method
code
Ant build files
Database creation files
(JSP form interface files)
configuration user_conf of library is
coll_m: collection_management;
cust_m: customer_management;
loan_m: loan_management;
User(coll_m, cust_m, loan_m.basic_loans, loan_m.reservations);
end;
(Note that interface default is bound if only component
is specified.) The code generator now generates a simple
WWW user interface which can be used to call transactions
in each interface bound to User. We could as well manually
implement a user interface and connect it to the extension
interface exposed by the generated library implementation.
generator
Transactions may be called by other transactions or environment external to a TransCo model. In the library example, no transaction calls other transactions. All transactions are called by the environment. A component configuration file is used to express component connections. In
this case, we connect the interfaces of all components to a
special User component:
entity bean
(XDoclet format)
Ant + XDoclet + javac
Compiled Java
component
Figure 5. EJB code generator.
6.1. Extensibility
The generated EJB implementation needs to be extensible in order to facilitate adding external components such
as user interfaces (which might be easier to model using for example UML instead of TransCo) and adding
implementation-specific functionality to existing components. We do not want to modify generated code, because
re-generation would destroy modifications and they would
have to be manually incorporated again. Therefore, each
generated entity and session bean exposes an extension interface allowing functionality to be added at certain extension points including: prior and posterior to executing a
transaction, and prior and posterior to accessing an attribute
or calling an operation. Extensibility is further enabled by
the fact that external components can call the services of the
generated components in the ordinary way.
6.2. Generating Library
An EJB implementation of the full library example has
been generated using our experimental code generator. The
code generator has also produced a simple Java ServerPages
based web interface which enables testing the generated implementation. Furthermore, scripts to create and drop the
required database tables have also been generated. As a
J2EE execution environment we currently use the Resin application server.
The generated library allows managing the collection
and customers, loaning, reserving, and using other basic library functions. All data is persistently stored in a relational
database, without the developer having had to write a single
line of SQL, EJB-QL or other query language. Consistency
of the data is ensured by transactional execution semantics.
7. Summary and Discussion
filing data.
We have described a formal model-driven approach
to the development of enterprise systems and reflected it
against OMG MDA. The approach builds on earlier research on the DisCo method, language and tools, our
new contributions being the model-driven framework, the
TransCo language for writing PIMs based on CIMs given
in DisCo, an EJB implementation scheme for TransCo components, and an experimental code generator.
Our approach differs from the mainstream of modeldriven research, because we do not employ UML for modeling. The rationale behind this is our will to keep things simple, executable and precisely defined all the way through,
from the most abstract CIM to implementation. Our definitions of CIM, PIM and PSM are clear, and relationships
between models are well-defined.
One way to evaluate the usefulness of our approach is
to examine its effect on the amount of lines produced by
a developer. The library example is 193 lines in TransCo
(counting blank lines). Stripped of details related to extensibility interfaces, the generated EJB/XDoclet Java sources
contain a total of 1176 lines of code. This does not yet include generated build files and SQL scripts. The generated
code does not radically differ from manually produced code
in this case. Running XDoclet generates some more code,
resulting in a total of 2278 lines of Java. This figure roughly
corresponds to the amount of code that would be needed if
plain EJB without the declarative facilities of XDoclet was
used.
Although the above figures of course represent only a
rough comparison based on one example, they probably
suggest something about the expressiveness of our PIM language and the usefulness of our code generation tool. Gaining in the level of abstraction leads to tradeoffs in lowlevel expressiveness. Everything that can be expressed in
Java/EJB cannot be formulated in TransCo. However, our
generated implementations expose extensibility interfaces
to enable adding of manually produced functionality and
components that have been modeled using, for example,
UML.
Efficiency of the generated implementation has not been
measured, but there is nothing that would suggest higher
inefficiency than in a typical manually written EJB application using transactions and container-managed persistence.
Our future plans include instrumenting the generated
EJB code with a DisCo Animator interface, which would
allow us to observe, test and drive an implementation using
the corresponding CIM and PIM. This would enable e.g.
tracing real executions as model animations, using model
execution scenarios directly as test cases, executing individual components with the CIM filling in as their environment
and other system components, and gathering real-time pro-
References
[1] T. Aaltonen, M. Katara, and R. Pitkänen. DisCo toolset –
the new generation. Journal of Universal Computer Science,
7(1):3–18, 2001. http://www.jucs.org.
[2] R. J. R. Back and R. Kurki-Suonio. Distributed cooperation
with action systems. ACM Transactions on Programming
Languages and Systems, 10(4):513–554, Oct. 1988.
[3] E. Breton and J. Bézivin. Towards an understanding of
model executability. In Proceedings of the international
conference on Formal Ontology in Information Systems,
pages 70–80. ACM Press, 2001.
[4] J. Bézivin and S. Gérard. A preliminary identification of
MDA components. In OOPSLA 2002 Workshop on Generative Techniques in the context of Model Driven Architecture, November 2002. http://www.softmetaware.com/
oopsla2002/mda-workshop.html.
[5] DisCo WWW site. http://disco.cs.tut.fi.
[6] W. Emmerich. Distributed component technologies and
their software engineering implications. In Proceedings of
the 24th International Conference on Software Engineering,
pages 537–546. ACM Press, 2002.
[7] D. S. Frankel. Model Driven Architecture: Applying MDA
to Enterprise Computing. Wiley Publishing, 2003.
[8] H.-M. Järvinen and R. Kurki-Suonio. DisCo specification
language: marriage of actions and objects. In Proceedings of
the 11th International Conference on Distributed Computing Systems, pages 142–151. IEEE Computer Society Press,
1991.
[9] R. Kurki-Suonio. Action systems in incremental and aspectoriented modeling. Distributed Computing, 16:201–217,
2003.
[10] R. Kurki-Suonio and T. Mikkonen. Liberating objectoriented modeling from programming-level abstractions. In
J. Bosch and S. Mitchell, editors, Object-Oriented Technology, ECOOP’97 Workshop Reader, number 1357 in Lecture Notes in Computer Science, pages 195–199. Springer–
Verlag, 1998.
[11] L. Lamport. The temporal logic of actions. ACM Transactions on Programming Languages and Systems, 16(3):872–
923, 1994.
[12] Object Management Group. Universal modeling language
specifications. http://www.omg.org/uml/.
[13] Object Management Group. Action semantics for the UML.
OMG document 2001-08-04, August 2001.
[14] Object Management Group. MDA guide version 1.0.1.
OMG Document Number omg/2003-06-01, June 2003.
http://www.omg.org/mda/specs.htm.
[15] J. Siegel and the OMG Staff Strategy Group. Developing
in OMG’s model driven architecture. OMG White Paper,
http://www.omg.org/mda/presentations.htm, November 2001.
[16] Sun Microsystems. Enterprise JavaBeans specification, version 2.0, August 2001.
[17] I. Wilkie, A. King, M. Clarke, C. Weaver, and C. Rastrick.
UML ASL Reference Guide. Kennedy Carter, asl language
level 2.5 edition, 2001. http://www.kc.com/download/.