Coordination Contracts: Concepts and Methodology 

 

This document contains a first introduction to the use of Coordination Contracts and associated engineering methodology. It presents the various concepts and techniques in a simple, progressive way, that should be complemented by selected readings from our publications page.

A word of warning, though: do not use in the CDE the notation found in the papers!. This is because, on the one hand, the notation itself has evolved a lot and, on the other hand, in the papers we tend to use a more abstract notation in order to simplify the presentation. Consult the Contracts Reference  section for more details on how to write your Java contracts in the CDE.

 

1. Methodology - The 3Cs approach

a. What are the 3Cs

The rationale for the methodology and technology that we are building is the strict separation between three aspects of the development and deployment of any software system: the computations performed locally in its components, the coordination mechanisms through which global properties can emerge from those computations, and the configuration operations that ensure that the system will evolve according to given organisational policies.

The layering is strict and aims at supporting changes to be performed at each layer without interfering with the levels below. The Coordination Layer defines the way computational components can be interconnected for the system to behave, as a whole, according to set requirements. At each state, the interconnections in place among the population of basic components define the current configuration of the system. The Configuration Layer is responsible for managing the current configuration,i.e. for determining, at each state, which components need to be active and what interconnections need to be in place among which components.

An analogy with an orchestra is perhaps useful. The "computation layer" of the orchestra is given by the players, each of which is able to play a number of instruments. Each instrument has a certain functionality and properties (provides a certain sound) but the sound produced by the orchestra as a whole at each instant is an emergent phenomenon that results from the the instruments that are playing, the notes they are playing, and their relative positions in the orchestra. Each player follows a score that determines the order in which notes need to be played. The conductor acts as a coordination layer on top of the music instruments, ensuring that the right instruments are played at the right time at the right places. The conductor, however, does not act arbitrarily: it follows a score that is different from the one each player follows because it contains all the information that is relative to the configuration of the orchestra.

 

b. Why the 3Cs

One of the main reasons for advocating the separation of these concerns is that it facilitates the evolution of systems. Changes that do not require different computational properties can be brought about either by reconfiguring the way components interact, or adding new connectors that regulate the way existing components operate, instead of changing the components themselves. This can be achieved by superposing, dynamically, new coordination and configuration mechanisms on the components. If the interactions were coded in the components themselves, such changes, besides requiring the components to be reprogrammed, would probably have side effects on all the other components that use their services, and so on, triggering a whole cascade of changes that is difficult to control.

On the other hand, the need for an explicit configuration layer, with its own primitives and methodology, is justified by the need to control the evolution of the configuration of the system according to the business policies of the organisation or, more generally, to reflect constraints on the configurations that are admissible (configuration invariants). This layer is also responsible for the degree of self-adaptation that the system can exhibit. Reconfiguration operations can be programmed at this level that will enable the system to react to changes perceived in its environment by putting in place new components or new interconnections. In this way, the system can adapt itself to take profit of new operating conditions, or reconfigure itself to take corrective action, and so on.

 

c. How to make it happen

For this approach to be effective, both at system design and implementation, this layering must be strict. This means that each layer makes use of the services of the layer(s) below without them being even aware of the layer above: if the components have built-in dependencies on the way they will be coordinated and configured, it will be much harder to evolve the system because it is usually very difficult to anticipate which coordination and configuration mechanisms will be needed to respond to change in the application or technological domains.

Our main responsibility in proposing a new model is, therefore, to provide the means through which the proposed separation can be supported, both at the modeling and deployment phases. Coordination Contracts are the conceptual units with a precise semantics and implementation strategy  that currently support the modelling and deployment of the coordination layer of the previous general architecture.

 

2. Coordination Contracts

a. What and Why

In general terms, a coordination contract is a connection that is established between a group of objects (participants). Through the contract, rules and constraints are superposed on the behaviour of the participants. From a static point of view, a contract defines what in the UML is known as an association class. However, the way interaction is established between the participants is more powerful than what can be achieved within the UML and OO languages because it relies on a mechanism of superposition that overrides direct, explicit method invocation, and replaces it with an external trigger/reaction kind of interaction.

More precisely, in the context of what the CDE makes available for coordinating Java programs (see our publications for the more general coordination capabilities that can be made available), this form of interaction allows for calls made from a client object to a supplier object to be intercepted by a contract which then superposes specific reactions to be executed synchronously and atomically as a transaction that may involve actions from the other participants and local actions of the contract itself. In order to provide the required levels of pluggability and dynamic adaptability, neither the client, nor any other object in the system, will "know" what kind of coordination is being or will be superposed. To enable this, a contract micro-architecture allows coordination contracts to be superposed on given objects in a system to coordinate their behaviour without having to modify the way the objects are implemented (black box view).

Using an abstract notation, a coordination contract is defined as follows:

contract class <name>
participants <list of classes>
constraints <the invariants that instances of the participant classes need to satisfy to be coordinated by instances of the contract>
attributes <list of attributes private to the contract>
operations <list of operations private to the contract>
coordination <list of trigger/reaction rules, each of which establishes a point of interaction (rendez-vous) in which the participants synchronise their local behaviour>
end contract


A contract consists of a collection of role classes (that identify the types of objects that can be partners in the contract), constraints that represent invariants defining in which conditions instances from the participating classes may be related by the contract, attributes and operations private to the contract, and the prescription of the coordination effects that will be superposed on the partners. Contracts can be unary, i.e. they can apply to single object instances, e.g. to regulate or adapt the way they behave to new requirements or simply monitor their behavior without inducing any new properties. They can also involve many partners, in which case they behave as synchronization agents that coordinate the way partners interact.

Each interaction under a coordination rule is of the form:

     name: when trigger && trigger conditions
           with condition
           set of actions

The name of the rule identifies a particular form of coordination; it identifies a point of “rendez-vous" in which the participants synchronize their local behaviour. The names themselves are used for managing the interference between different contracts that may be active in the same state as discussed further below.

The condition under “when" establishes the trigger of the interaction. The trigger can be a condition on the state of the participants, a request for a particular service, or an event on one of the participants (events are not curently supported in the CDE). Several conditions, identified as "trigger conditions", can be placed in the “when" clause. If one of such conditions is not satisfied, the contract is considered as being “inactive" and, as a result, either the original code of the trigger or another contract is executed (if such a contract exist and is active). This mechanism provides the ability for controlling which of the contracts imposed on a component will be responsible for coordinating it.

The set of actions identifies the reactions to be performed, usually in terms of actions of the partners and some of the contracts own actions. When the trigger corresponds to the invocation of an operation, three types of actions may be superposed on the execution of the operation:

  1. before actions: to be performed before the operation
  2. do action: to be performed instead of the operation (alternative); the original operation is executed if the clause is omitted
  3. after actions: to be performed after the operation
In the case in which an object participates in multiple contracts with the same trigger, the sequence of execution for the before, replace and after clauses is the following: first, all the “before" actions are performed, then one “do", and finally all the “after" actions. A pictorial representation of the coordination semantics is the following:

 

 

 

 

 

 

 

 

 

 

It should be noted that the semantics of contracts allow for only one “replace" clause to be executed, thus preventing the undesirable situation of having two alternative actions for the same trigger. Furthermore, any such replacement action must adhere to whatever specification clauses apply to the operation (e.g. contracts in the sense of B.Meyer specifying pre- and post-conditions). This ensures that the functionality of the original operation, as advertised through its specification, is preserved.

The set of actions that are executed as a reaction to a trigger is called the synchronisation set associated with that trigger. The semantics of contracts requires that this set be executed atomically, guarded by the conjunction of the guards of the individual actions together with the conditions included in the "with" clause. Therefore, the “with" clause ("guard conditions") puts further constraints on the execution of the actions involved in the interaction. If any condition under the “with" clause is not satisfied, the synchronisation set fails and none of its actions is executed (including the trigger operation on the participant).

In order to illustrate the use of contracts, consider the familar world of bank accounts in which clients can, as usual, make withdrawals. The object class account is usually specified with an attribute balance and a method withdrawal with parameter amount. In a typical implementation one would assign the guard Balance>=amount restricting this method to occur in states in which the amount to be withdrawn can be covered by the balance. However assigning this guard to withdrawal operations can be seen as part of the enforcement of a business requirement and not necessarily of the functionality of a basic business entity like account. Indeed, the circumstances under which a withdrawal is accepted can change from customer to customer and, even for the same customer, from one account to another depending on its type.

Inheritance is not a good way of changing the guard in order to model these different situations. Firstly, inheritance views objects as white boxes in the sense that adaptations like changes to guards are performed on the internal structure of the objects, which from the evolution point of view is not desirable. Secondly, from the business point of view, the adaptations that make sense may be required on classes other than the ones in which the restrictions were implemented. In our example, this is the case when it is the type of client, and not the type of account, that determines the nature of the guard that applies to withdrawals. The reason the guard will end up applied to withdrawal, and the specialization to account, is that, in the traditional clientship mode of interaction, the code is placed on the supplier class.

Therefore, it makes more sense for business requirements of this sort to be modeled explicitly outside the classes that model the basic business entities, because they represent aspects of the domain that are subject to frequent changes (evolution). Our proposal is that guards like the one discussed above should be modeled as coordination contracts that can be established between clients and accounts:

contract class Standard_Withdrawal
participants x : Account; y : Customer;
constraints owns(x,y)=TRUE;
coordination
wt: when  y.calls(x.withdraw(z)) 
   
with  x.Balance() >= z;
   
do   x.withdraw(z)
end contract

The constraint means that instances of this contract can only be applied to instances of Customer that own the corresponding instance of Account. The coordination rule superposes the guard that restricts withdrawals to states in which the balance is greater than the requested amount.

Having externalized the ‚business rule“ that determines the conditions under which withdrawals can be made, we can support its evolution by defining new contracts that can replace the old ones when customers subscribe new account packages. For instance, consider the following contract that allows for relaxing the functionality of withdrawal to situations in which an account may become overdrawn up to an agreed credit limit:

contract class VIP_Withdrawal
participants x : Account; y : Customer;
constraints owns(x,y)=TRUE;
attributes Credit:Double;
coordination
wt: when  y.calls(x.withdraw(z)) 
   
with x.Balance()+Credit >= z;  
   
do   x.withdraw(z)
end contract

The situation in which a customer decides to subscribe to this new business rule is modelled by replacing, in the current configuration, the Standard_Withdrawal contract that connects him/her to the account, by a VIP_withdrawal. This substitution can be performed at run-time, without interrupting the service provided by the system, and without requiring the code that implements withdrawals to be changed. Moreover, the fact that the business rule has been externalised and made explicit in the configuration leads to a system architecture that reflects directly the business model, which makes it much easier to locate and perform changes in the system that derive from the evolution of the business domain.

 

b. How

For this approach to be effective, we need a means of translating contracts directly to the implementation level. To enable this, the contracts micro-architecture allows coordination contracts to be directly implemented using OO languages, while providing the following advantages:
  1. Components are not aware of the presence of contracts and, therefore, any number of contracts can be added/removed without having to modify the components.
  2. Contracts can be added/deleted in a "plug and play" mode, even in run time.
  3. Even existing components can be easily adapted to accept contracts without making any modifications elsewhere in the application, thus allowing for easier reengineering/evolving of existing applications.

It should, also, be noticed that the methodology and technology of coordination contacts are language independent. However, there is also no doubt that we need specific language implementations that will allow for specifying contracts in a level that is closer to the underlying language and on top of specific implementations of components. The current version of the CDE provides this functionality for Java as the target language and with components (contract participants) for which the source code is available. In future releases, the CDE will support participants as Java .class files (byte code) as well as C++ as target language.

Section Syntax Reference describes how contracts can be edited in the CDE to allow the implementation of the micro-architecture in Java. In what follows, we present how the outlined engineering methodology and contracts can be put together in practice.

 

3. Putting everything together

To put our methodology and coordination contracts together, we propose the adoption of a development strategy based on the following steps:

  1. Perform domain analysis and identify the parts of the system that are evolution critical and the parts that are computationally "stable". 
    For instance, parts that are more related to business rules such as the way accounts are credited, or network management policies, or parts that are very sensitive to technology advances, are normaly very likely to evolve. On the other hand, computationally "stable" parts are normally parts that implement algorithms or operations that are unlikely to change in the future. For instance, a credit(Amount) operation of account consists of the computation balance()+Amount and can be considered as a "stable" operation: it has no business rules embedded and experience has shown that credit() is normally a computation of the previous form. Naturally, this is not always easy to say or assume, and the more experience one has of the domain the better this evolutionary model will be.
  2. Write the models (for instance, UML ones) of the parts identified at the previous step and develop the specific implementation of the computational parts of the system; and then
  3. model the required collaborations using contracts. Normally, all the volatile elements should be implemented in contracts in order to achieve a good evolutionary model.
    In order to make an effective use of this strategy, a good knoweldge and understanding of the semantics of contracts is required. For instance, it very likely that questions such as "what sort of conditions should I choose, a constraint, a trigger condition or a with clause?",  "shall I write this action inside a "before", "after" or a "do" (replace) statement?", will arise when starting to use contracts.
  4. Use the CDE to develop the Java version of your contracts on top of the components already developed in 2 and generate the code that implements the adapted components and the contracts.
    Note that there are differences between the contracts at the modelling level and the CDE-Java specific ones since that former are abstract and the latter are superposed on top of existing specific Java components
  5. Develop your Java final application by applying instances of your contracts to the instances of your computational components (participants objects). 
    By doing so, the evolution of the behaviour of your application is feasible by simply reconfiguring the contracts and components relationships (add, delete, substitute, change contracts).

 

For examples on how to apply contracts consult the Case Studies section.





© Copyright 2001 ATX Software SA. All rights reserved.