Coordination Patterns for Component-Based

0 downloads 0 Views 58KB Size Report
B(g) is a proposition over the set of variables – its guard. ... available the number of times the cart has gone through the control point. ..... concrete needs for supporting development work in highly volatile business domains, we had to workout a ...
Coordination Patterns for Component-Based Systems * L.F.Andrade1,2 , J.L.Fiadeiro 2,3 , J.Gouveia 1, G.Koutsoukos1, A.Lopes 3 and M.Wermelinger4 1 OBLOG Software S.A. Alameda António Sérgio 7 2795 Linda-a-Velha, Portugal {landrade,jgouveia,gkoutsoukos}@oblog.pt 2 ATX Software S.A. Alameda António Sérgio 7 2795 Linda-a-Velha, Portugal 3

Dept. of Informatics Faculty of Sciences, University of Lisbon Campo Grande, 1700 Lisboa, Portugal [email protected], [email protected] 4

Dept. of Informatics Faculty of Sciences and Technology, New University of Lisbon Quinta da Torre, 2825 Monte da Caparica, Portugal [email protected]

A b s t r a c t . The separation between computation and coordination offers the possibility for systems to be evolved through the addition, deletion or substitution of the connectors that coordinate the interaction between their components, without interfering with the computations that are performed locally. Ideally, this form of evolution should be applicable regardless of the languages in which these computations are programmed, thus allowing for the integration of third-party, closed components, like legacy systems. In order to establish universal principles that support this level of generality, we propose a mathematical pattern that clarifies essential mechanisms of coordination that support evolution, and a design pattern that allows for such mechanisms to be implemented in platforms for component-based system development like CORBA, EJB and COM.

1

Introduction

The separation between computation and coordination, as proposed by recent languages and models [18], has opened important new perspectives for supporting extendibility of systems, i.e. the possibility of adapting software systems to changes in requirements in an easy way. The evolutionary model that we have been developing, as a genuine, and fruitful, collaboration between academia and industry, is based on the representation of the more volatile aspects of the application domain (e.g. business rules, adapters, monitors) as connectors whose purpose is to coordinate the interaction among core, more stable, components. The idea is that, in this way, evolution can be made to be compositional over the changes that occur in the application domain through the addition, deletion or substitution of connectors, without interfering with the services provided by the core objects of the system. The applicability of this particular approach to evolution has been demonstrated in the field through the development of software systems in a number of domains using the OBLOG tool [www.oblog.com]. OBLOG provides a language and family of tools for supporting object-oriented development. One of the features that distinguish it from other products in the market is its collection of primitives for modelling the behavioural aspects of systems. These include the notion of coordination contract [3], a semantic primitive corresponding to connectors as used in Software Architectures [1], which we brought into OO modelling as a means of supporting the coordination layer required by the approach that we described above. Ideally, this method for evolving systems should be applicable regardless of the languages in which the core objects are programmed, thus allowing for the integration of third-party, closed components, like legacy systems. For such a general support to be possible, we need to abstract away from the specific coordination languages and *

This paper is an extended and updated version of [6]. This work was partially supported by Fundação para a Ciência e Tecnologia under contract POSI/32717/CHS/2000.

models that have been proposed in the literature, universal principles that can be made available for extending existing languages, methods and tools with evolutionary properties. Our purpose in this paper is to relate the progress that we have made so far into that endeavour. In section 2, we present a "mathematical pattern" that identifies essential mechanisms of coordination which are present in languages for parallel program design and architecture description. This pattern was used in [3] for giving semantics to coordination contracts. In section 3, we present a design pattern that allows for such mechanisms to be implemented in platforms for component-based system development like CORBA, EJB and COM. This pattern was used for implementing contracts in OBLOG and is now the basis for a Coordination Development Tool that we are building for supporting the use of coordination contracts in more general frameworks [19]. A concrete instance of this tool is now available for coordinating JAVA programs.

2

A categorical pattern for coordination

In order to motivate the mathematical formalisation of some essential principles of coordination, we start with an example. Although we have acquired experience in applying this technology in a number of business domains – including banking [5], stock-trading [22] and telecommunications [21] – the example we chose is closer to what can be typically found in Software Architectures and Coordination Languages. For simplicity, the example will be developed in a toy language – CommUnity [13] – that encapsulates the coordination mechanisms that we are making available in the CDT tool [19]. CommUnity is similar to Unity [12] and Interacting Processes [16], except that it replaces the shared-variables model of interaction by an object-based coordination model centred around private state and shared actions. A program in CommUnity is of the following form: prog P is out V in R [] do g: [B(g) g

a D(g)

a: F(g,a)]

where • V R is the set of variables. Variables can be declared as input (R) or output (V) and they correspond to communication channels that are used by the program. Input variables are used for receiving data from the environment of the program. They provide a simplified model for data transmission usually associated with input parameters of methods. Output variables provide for both observations of the state of the program, and the output parameters usually associated with methods. They are not used as representations of the state of the object but, rather, as abstractions of the state that are convenient for the specification of interactions with the rest of the system. • is the set of action names; each action name has an associated guarded command. Actions represent interactions between the program and the environment. Hence, their execution is also under the control of the environment in the sense that their occurrence may require synchronisation with actions of other components of the system. Each action g is typed with a set D(g) consisting of the output variables whose values may change as a result of the occurrence of action g. For every variable a, we also denote by D(a) the set of actions whose occurrence can change a. • For every action g , B(g) is a proposition over the set of variables – its guard. When B(g) is false, g cannot be executed. • For every action g and output variable a D(g), F(g,a) denotes the set of values that a may take after the execution of g. That is, we work with non-deterministic assignments. When the assignment is deterministic, i.e. when F(g,a) denotes a singleton, we use the standard notation – a:=F(g,a). As an example, consider the typical airport luggage delivery system in which carts move along a track and stop at designated locations for loading and unloading baggage. As usual, we model locations in the track through natural numbers modulo the length of the circuit. Pieces of luggage are also modelled through natural numbers, zero being reserved to model the situation in which a cart is empty. Given this, a CommUnity program that models the behaviour of a cart can be given as follows:

prog out in do [] []

cart i s bag, loc, dest: nat nbag, ndest: nat move: [loc dest loc:=loc+1] load: [loc=dest bag=0 bag:=nbag dest:=Dest(nbag)] unload: [loc=dest bag 0 bag:=0 dest:=ndest]

That is to say, a cart is able to move, load and unload. It moves by incrementing the variable loc, while it has not reached its destination. The current destination is available locally in dest and is computed from the bag each time the cart stops to load, using a function Dest that we assume is provided in the data type, or from the environment, when unloading, using the input variable ndest. Loading and unloading take place only when the cart has reached its destination. Consider now a typical situation of evolution. Assume that we need to monitor the behaviour of the cart by observing how many times it visits a given location in the circuit. The typical way of doing so would be to extend the program as follows: prog out in do [] [] []

monitored_cart i s bag, loc, dest: nat; c o u n t : n a t nbag, ndest: nat; c p o i n t : n a t j&move: [loc dest l o c c p o i n t c&move: [loc dest l o c = c p o i n t load: [loc=dest bag=0 bag:=nbag unload: [loc=dest bag 0 bag:=0

loc:=loc+1] loc:=loc+1 c o u n t : = c o u n t + 1 ] dest:=Dest(nbag)] dest:=ndest]

The extension (highlighted in bold) of the original program includes a new output parameter that makes available the number of times the cart has gone through the control point. The control point itself is an input variable and, hence, can be dynamically changed by the environment. The move action is now split in two: j&mov accounts for movements that do not go through the control point, and c&move for those that do (thus incrementing count). This sort of extension is supported in languages for parallel program design like Unity through the notion of superposition. The program monitored_cart was obtained from cart by superposing additional behaviour accounting for the required monitoring activity. Extension by superposition follows certain rules that make sure that the original program is "protected" in a very precise sense that can be captured through the following notion of morphism: Given programs P1= and P2= a superposition morphism :P1 P2 is a pair < :V 1 R 1 V 2 R , : 2 1> of functions such that, • For every a V 1 R 1, sort(a)=sort( (a)). • For every a V 1, (a) V 2. • For every a∈V1, D 1(a)=σ γ(D2(σα(a))). • For all g2 2 st (g2) is defined, a1 D1( (g2)), (F2(g2, (a1)) F1( (g2),a1))) • For every g2 2 st (g2) is defined, (B 2(g2) (B1( (g2)))). where means validity in the first-order sense taken over some axiomatisation of the underlying data types. Programs and superposition morphisms constitute a category PRO. The functions account for the extension. Notice that input variables may become output variables through superposition in the sense that the extension may account for the part of the environment from which values were being read. The morphisms on actions are partial and contravariant to account for the fact that, on the one hand, superposition may unfold actions of the original program (as in the example) and, on the other hand, new actions may be added. The restrictions are the usual ones for superposition: the guards of the actions can be strengthened and the assignments can be made more deterministic. New actions are also restricted to leave the old attributes out of their scope (encapsulation). Superposition morphisms do not correspond to the mathematical pattern that we wish to provide for supporting the approach to evolution that we outlined in the introduction. This is because extensions performed in this way require changes to be carried out on the way components have been implemented, e.g. changing the guards of given actions. The way we want to support the evolution of the system is by interconnecting an explicit component to the original program in a way that ensures the required monitoring activity. The required component for doing the monitoring can be given by the following program:

prog out in do []

monitor i s count:nat loc,cpoint:nat inc: [loc=cpoint skip: [loc cpoint

count:=count+1] ]

This is a component that is able to read two values from its environment (through the channels loc and cpoint) and perform two actions: one that increments an output variable count when the two input channels hold the same value, and another that just "skips" (does "nothing", computationally speaking) when these values are different. In order for this component to monitor the behaviour of the cart as intended, we need to interconnect it to cart. The required interconnection is expressed through the following diagram channel ↔

loc move

cart



•← ←

loc inc skip

monitor

where program channel i s in a do g: [ ]

The channel is defined as a program itself but as one that does not perform any computation. It provides a pure coordination function by establishing the means for the two components to communicate, just like an ideal, neutral "cable". In the case of the example above, we need a "cable" for the monitor to read the location of the cart, and another cable for the move action of the cart to synchronise with those of the monitor. The interconnection itself is established through the arrows. The left arrow maps a to loc and move to g, and the right arrow maps a to loc and both inc and skip to g. Because there are no computations involved in the channel, the arrows are, in fact, superposition morphisms. Hence, the diagram is a categorical entity i.e. a mathematical object which, in this case, expresses a given system configuration. Such a configuration diagram establishes input/output relations, as in the case of loc, as well as synchronisation sets as in the cases of {move,inc} and {move,skip}. This semantics of the configuration is the one that corresponds to taking the colimit of the diagram. The colimit returns a program whose behaviour corresponds to the joint behaviour of the components thus interconnected. In the case above, it is easy to prove that the colimit returns, up to isomorphism, the program monitored_cart. It is not possible to provide herein the full explanation of how colimits in the category of programs work. In the case of interconnections such as the one above, and besides establishing these input/output relations and synchronisation sets as illustrated, the colimit assigns to each synchronisation set an action whose guard is the conjunction of the guards of the actions in the set, and whose assignments are given, for every output variable, by the intersection of the assignments performed locally by the actions in the set. This operation captures what in IP [16] is actually called superposition – a generalised parallel composition operator based on a rendez-vous synchronisation mechanism. In this case, channels can be seen to provide the places in which the rendez-vous take place. It is this pattern of extension that we wish to support the method of evolution that we proposed: components are not extended in the sense that we rewrite whatever features we want to change (even if subject to certain rules), but superposed with other components that regulate their behaviour. In the general case, the superposition may be performed over more than one component using what in Software Architectures are called connectors [1]. Such connectors coordinate the behaviour of the given components (roles or partners) via a mediator (the glue). See [14] for a semantics of architectural connectors using Category Theory as above and [4] for their application to OO modelling through coordination contracts. There is, however, one further ingredient in our approach to evolution that needs to be accounted for. Precisely the one that corresponds to coordination. How can we capture the fact that, through the interconnections expressed above, we do not have to change the way the components are programmed? That is to say, how can we express that computation has actually been separated from coordination? In the case of CommUnity, this separation is captured by distinguishing two different parts of programs: signatures, or interfaces, and bodies. Signatures consist of the features that are used to perform the interconnections – the input and output variables, and the action names, together with their types. Hence, they provide the model

of coordination that is built into the design language. The bodies consist of the features through which each program ensures a given functionality – the guards and assignments performed by the actions. A program signature is a triple where • V and R are S-indexed families of sets where S is the set of sorts. • is a 2 V -indexed family of sets. We denote by D(g) the type of each g in . Given signatures 1= and 2=, a signature morphism from 1 to 2 is a pair < :V 1 R 1 V 2 R , : 2 > of functions such that, 1 • For every a V 1 R 1, sort(a)=sort( (a)). • For every a V 1, (a) V 2. • For every a V 1, D 1(a) (D2( (a))). Program signatures and morphisms constitute a category SIG. The categories of programs and of signatures are related through a functor s i g :PRO SIG that forgets the computational aspects of programs. The fact that the computational side does not play any role in the interconnections is captured by the following properties of this functor: • s i g is faithful; • s i g lifts colimits; • s i g has discrete structures. The fact that s i g is faithful (injective over each hom-set), means that morphisms of programs cannot induce more relationships between programs than between their underlying signatures (interfaces). That is to say, by taking into consideration the computational part, we may not get additional observational power over the external behaviour of programs. The second property means that, given any configuration diagram dia:I PRO over programs and colimit (s i g (S i )→C)i:I of (dia;s i g ), there exists a colimit (S i →S) i:I of dia such that s i g (S i →S)=(s i g (S i )→C). In other words, if we interconnect system components through a diagram, then any colimit of the underlying diagram of interfaces establishes a signature for which a computational part exists that captures the joint behaviour of the interconnected programs. This is a "correctness" result in the sense that it states that, by interconnecting the signatures of programs, we are interconnecting the programs themselves. The corresponding "completeness" result – that all interconnections can be established via signatures – is given by the third property. The fact that s i g has discrete structures means that, for every interface C:SIG there exists a program s(C):PRO such that, for every signature morphism f:C s i g (S), there is a program morphism g:s(C) S such that s i g (g)=f. That is to say, every signature C has a “realisation” (a discrete lift) as a program s(C) in the sense that, using C to interconnect a component S, which is achieved through a morphism f:C s i g (S), is tantamount to using s(C) through any g:s(C) S such that int(g)=f. In the case of CommUnity, the discrete lift of a signature is the "empty program" over that signature: every action has a true guard and performs a completely underdetermined assignment. This is exactly how the channel in the example above looked like, which justifies that channels are, essentially, just signatures. Hence, the interconnections are established via signatures, independently of the computations performed locally by the components. These three properties of a functor int:S Y S INT constitute the definitional characteristics of what in [15] we called a coordinated functor or, equivalently, for a given category S Y S of systems to be coordinated over a category INT of interfaces. In [15], we provided more examples of coordinated functors, including concurrency models, specification logics, and Gamma-like programs [8], which supports the view that this is, indeed, an abstraction of typical properties of coordination that hold across a range of formalisms (and not just CommUnity). Therein, we further show how a number of properties hold of coordinated formalisms that are useful for software development, including the ability of synthesising interconnections between programs from interconnections established at the level of their specifications.

3

A design pattern for coordination

The fact that a mathematical pattern exists for justifying our principle of supporting evolution through the superposition of connectors (coordination contracts) does not mean that it can be put into practice directly over the technology that is available today. In this section we show that, even if none of the standards for componentbased software development that have emerged in the last few years (e.g. CORBA, EJB and COM) can provide a convenient and abstract way of supporting superposition as a first-class mechanism as proposed in this paper, an implementation can be given that is based on a design pattern that exploits some widely available properties of object-oriented programming languages such as polymorphism and subtyping.

When defining an implementation, we need to have in mind that, as motivated in the introduction, we should be able to superpose regulators (coordination contracts) on given components of a system in order to coordinate their joint behaviour without having to modify the way these components are implemented. This degree of flexibility is absolutely necessary when the implementation of these components is not available or cannot be modified, as in legacy systems. It is also a distinguishing factor of coordination contracts when compared with existing mechanisms for modelling object interaction, and one that makes coordination contracts particularly suited for business domains where the ability to support the definition and dynamic application of new forms of coordination is a significant market advantage. Before we discuss the design pattern that we developed for coordination, we must point our that the level at which its "correctness" with respect to the mathematical pattern discussed in the previous section can be established is not a mathematical one. This is because neither the mathematical semantics nor mathematical abstractions of these platforms for component-based system development are available. Hence, we will argue for, rather than prove, its correctness. The correspondence between the two patterns will not be established at the level of the categorical concepts either. That is to say, we did not take it as a task to "implement" the notions of object, morphism, colimit, etc. Instead, we have tried to make sure that the "spirit" of the mathematical solution was captured by the implementation pattern. The evidence that we have gathered from using this pattern in our development work indicates that we have succeeded in capturing this spirit. The "spirit" that we have been mentioning corresponds to the characteristics that we distilled at the end of the previous section. We need to provide autonomous existence to signatures (interfaces), use these as a means for interconnecting programs, and ensure that interconnections are not lost by restricting them to the interfaces. The class diagram below depicts the proposed pattern, based on well-known design patterns, namely the Proxy or Surrogate [17]. Its "correctness" relies on two main mechanisms. On the one hand, provision of a specific interface (SubjectInterface), as an abstract class, for every component. This interface is related to the real program (Subject) through a dynamically reconfigurable proxy reference. On the other hand, support for dynamic reconfiguration of the code executed upon requests for operations (including requests by self as in active objects), is achieved through the proxy. Reconfiguration of a predefined component (such as adapting the component for a specific use) or coordination of various components (such as behaviour synchronisation) is achieved by making the proxy reference a polymorphic entity. On the one hand, this proxy is provided with a static type at compile-time – the type with which this entity is declared (ISubjectProxy) – that complies with the interface of the component. On the other hand, the type of its values may vary at run-time through ISubjectPartner as contracts (connectors) are superposed or removed from the configuration of the system. These types, the ones that can be dynamically superposed, become the entity's dynamic type (dynamic binding), one for each contract through Ct_I_SubjectConnector. The notion of dynamic binding means that, at run-time, such a proxy assumes different values of different types. However, when a request is made for services of the component, it is the dynamic binding mechanism of the underlying object-oriented language (e.g. C++, Java) that makes the choice of the operation implementation to execute (Subject or ISubjectPartner) based on the type to which the proxy belongs. Relying on this mechanism of later binding, the reconfiguration by superposition is implemented by (1) introducing the intended coordinated behaviour of the parties, as obtained through the colimit, on Contracti objects, and (2) connecting them to the parties using the Ct_I_SubjectConnectorobjects as explained below. The synchronous semantics of the interconnections established through the channels is enforced by letting the contract coordinate the execution of the operations involved in the synchronisation set defined by each interaction (as given through the colimit). This coordinated execution of the synchronisation set is performed atomically as far as the execution of other operations of the parties involved is concerned. The Coordination Contract Design Pattern consists of two parts: the component part and the coordination part. The former consists of the features that have to be provided for each component so that it can become coordinated by a contract. The latter concerns the mechanisms that allow for coordination through the contract to take place. The classes that participate in the proposed pattern are shown below. Thick black-line boxes represent components that are tool-generated in the Coordination Development Environment that we have already mentioned [19]. Component Part • SubjectInterface. This is an abstract class (type) that defines the operations under potential coordination. In fact, it is the common interface of services provided by SubjectToProxyAdapter and ISubjectProxy. • Subject. This is the real component, the one that provides the concrete implementation of the various services and inherits from SubjectToProxyAdapter.

Coordination Part

Component Part

SubjectInterface

ISubjectProxy

Chain of responsibility

SubjectToProxyAdapter _operation ( ) operation ( )

0 …* Subject

ISubjectPartner

_operation( )



Ct_1_SubjectConnector

Ct_2_SubjectConnector

1 1

Contract1



1 1

: UML implements

: UML extends

Contract2

SubjectToProxyAdapter. Defines the ability to use a proxy or, alternatively, internal methods for the implementation of a given Subject interface. It is a concrete class that allows, at run time and using the polymorphic entity proxy, to delegate requests to ISubjectProxy in the case in which Subject is under coordination. Such requests are then delegated to ISubjectPartner which links the subject to the contracts that coordinate it. If no contract is involved SubjectToProxyAdapter may forward requests directly to Subject. This requires that (1) Subject inherits from SubjectToProxyAdapter and, (2) the operations of Subject are renamed such that the operations having the initial names can be moved to SubjectToProxyAdapter as concrete operations and the new operations as abstract operations. For instance, Subject’s operation( ) exists now as operation() in SubjectToProxyAdapter and as _operation( ) in Subject. Moreover, _operation() is also declared as an abstract operation in SubjectToProxyAdapter. Requests for operation() are made to Subject. However, due to renaming, the operation does not, in fact, exist in Subject but in SubjectToProxyAdapter from which Subject inherits. In the case that there is no contract (no proxy) involved, operation() in SubjectToProxyAdapter forwards the request to the corresponding real implementation, _operation(), in Subject. Otherwise, as already stated above, it delegates the request to ISubjectProxy.



ISubjectProxy. It represents an object with the capability of implementing the Subject interface. It is an abstract class that defines the common interface of Subject and ISubjectPartner. The interface is inherited from SubjectInterface to guarantee that all these classes offer the same interface as Subject with which real subject clients have to interact. Coordination Part • ISubjectPartner. Defines the general abilities of an entity to be under coordination. It maintains the connection between the real object (Subject) and the contracts in place for it. The class is responsible for delegating received requests to CtSubjectConnectors according to a chain of responsibility. The class contains operations for managing the chain of responsibility. Alternatively, the required management operations can be included in an abstract class, ContractPartner, from which ISubjectPartner inherits. However, this is a rather “low-level” design issue and therefore such a class is not presented in the pattern. • Ct_i_SubjectConnector. A partner that represents the specificities of Subject coordination for a given contract in which Subject is a participant. For each pair contract/participant there is exactly one Ct_i_SubjectConnector. The class implementation may be responsible for the execution of the rules defined in the coordination part of the contract and for ensuring satisfaction of the contracts’ semantics. • Contract-i. A coordination object that defines the rules that will be superimposed on Subject. This is an object that subsumes the coordination specified through the configuration diagrams defined in the previous sections (colimit semantics); it is notified and takes decisions whenever a request is invoked on a real subject. In this sense, objects of this class implement more than the behaviour being superposed by the connector on the parties; they are required to coordinate the execution of the whole synchronisation sets as required by the colimit semantics developed in section 2. This difference relative to the mathematical pattern is the price that needs to be paid for having to implement superposition over platforms that do not support it directly, namely when interaction is only available as method calling. These functionalities can be summarised as follows: Component Part SubjectInterface Subject SubjectToProxyAdapter

ISubjectProxy

An abstract class that defines the operations under potential coordination. The real component that is candidate for coordination. Defines the ability to alternative use a proxy or internal methods for the implementation of a given Subject interface. It represents an object with the capability of implementing the Subject interface.

Coordination Part ISubjectPartner Ct_i_SubjectConnector

Contract_i

Defines the general abilities of a concept to be under coordination. A partner that represents the specificities of Subject coordination for a given contract in which Subject is a participant. A coordination object that defines the rules that will be superimposed on Subject.

It should be noted that, in case of inheritance between objects that are under coordination, some additional classes and some new associations may be necessary. Due to space limitations, these are not presented in the pattern and will not be discussed herein. In what follows, we discuss some “lessons learned” from our experience in implementing the pattern in Java and C++. Clearly, a variety of different “low-level” design decisions can be made for implementing the general pattern as described above. However, such decisions should always take into account the requirements presented in the beginning of this section. In this context, there are four issues we wish to discuss: • The first issue that should be considered is related to the implementation of Subject in Java. As explained above, Subject inherits from SubjectToProxyAdapter. The question is, what if Subject has to inherit from another class, for instance SuperSubject, since Java does not support multiple inheritance? In that case, a good solution is for Subject and SubjectToProxyAdapter to be merged into a single class, Subject, that

provides the functionality of both the previous classes. It should be noted that having SubjectToProxyAdapter as a Java interface would not provide a solution because the dynamics of the pattern rely on call delegations that are defined in the method implementations of SubjectToProxyAdapter. Contrary to Java, multiple inheritance is supported in C++ and, therefore, a C++ implementation is not concerned with such an issue. • Contracts should be independently and explicitly created so that they can be added and deleted in a “plug and play” mode. A good solution is to explicitly create each contract parameterising its constructor with the contract’s participants (Subjects). The contract is then responsible for creating its CtSubjectConnector passing as argument itself and the participant to which the CtSubjectConnector is associated. The CtSubjectConnector simply checks whether it is the first partner in the chain of delegation. If it is the first partner it sets the subject proxy to point to itself, otherwise it adds itself as the last partner. • In cases of inheritance, implementations of the pattern maintaining a single chain of responsibility will encounter the problem of having a “non-uniform” chain of responsibility. By “non uniform” we mean that the chain can contain a mixed sequence of the different connectors of the base and the subclasses objects. The management of the chain of delegation should be responsible for dealing with this situation by retrieving and delivering partners that comply with the corresponding Subject interface for the trigger being processed. Correctly retrieving the contracts’ rules and executing the contracts’ synchronisation set are additional issues that have to be considered when implementing the pattern. There are different strategies that can be adopted to deal with such issues. For instance, having a more centralised management of the execution of the contracts rules versus managing the execution in the ISubjectPartner. A typical execution of the pattern starts with a request for an operation of the object. Because of the renaming procedure that we described above, requests to Subject are forwarded to SubjectToProxyAdapter which then delegates execution on either the real object Subject or ISubjectPartner if the real object is being coordinated. In the latter case, the ISubjectPartner transfers the execution to the regulators (contracts). Each contract itself is required to execute, atomically, the synchronisation set to which the called operation belongs (colimit semantics), thus superposing whatever new forms of behaviour have been imposed. The execution of the synchronisation set may involve, among other, calling the operation of the real object, but not necessarily. For instance, the new guard imposed by the synchronisation set may refuse the execution of the operation altogether. On the other hand, as a means of allowing legacy code to be upgraded, it is also possible that the regulator executes a new implementation of the operation being called instead of calling the real object. This mechanism of upgrading code was suggested in [11] and should be subject to the satisfaction of the specification of the operation. Notice that, as part of the execution of the synchronisation set, the regulator may call operations of other objects. This is what happens, typically, with non-unary connectors, i.e. when the regulator coordinates the interaction that is required among several objects. In this case, the pattern of interface/proxy/channel is applied to each real object under coordination. As mentioned above, if there are no contracts coordinating a real subject, the pattern can be simplified and SubjectToProxyAdapter may forward requests directly to Subject, which is not such a heavy penalty. On the other hand, superposing a new connector implies only modifications to the coordination side of the pattern. Indeed, we should make clear that, through this design pattern, connectors are superposed on the partners taken as black-boxes, meaning that the components are not even aware that they are being coordinated by a third party. In the figure above, this means that requests are still put on the original components even if they will be intercepted by the connector for superposing the required coordination mechanisms. The same transparency applies to all other clients of the same supplier: no changes are required on the other interactions that involve the components being coordinated through a connector. Hence, connectors may be added, modified or deleted without any need for the partners, or their clients, to be modified as a consequence. An alternative implementation strategy for superposition can be found in [11] which is based on surrounding components with successive layers of wrappers. The idea is that, each time a component is extended through the superposition of additional behaviour, a new layer is created that wraps the current implementation and intercepts calls to the original component. It is clear that this layered approach implements the morphism-based view of superposition that we mentioned in section 2, and which we rejected because it does not provide the kind of support required by our approach to evolution. Indeed, this layered approach does not make the regulators explicit in the architecture of the system, i.e. it does not externalise the coordination mechanisms that regulate the behaviour of the components for a given configuration. As argued in section 2, this precludes a compositional approach to evolution as achieved through the colimit-view of superposition that we favour.

4

Concluding remarks

In this paper, we have presented on-going work that explores some of the features made available by coordination models for supporting a discipline of system evolution that is compositional on the evolution of the application domain itself. Essentially, the proposed discipline is based on the explicit identification, as first-class citizens, of the mechanisms that model business rules and other aspects of the application domain that require that the behaviour of its components be coordinated in order to achieve certain effects. Because, in many application domains, changes are required because of the need to introduce new or replace existing coordination mechanisms, we advocate that corresponding changes in running systems should be supported through the dynamic reconfiguration of the coordination mechanisms, without having to change the way the components being coordinated are implemented. In this way, a true "plug-and-play" approach to software development is supported, making it possible to integrate third-party, closed components, like legacy systems. The solution that we found is based on a pattern that captures what in Software Architectures are called connectors [1] and implements what in Parallel Program Design is known as superposition [20]. In fact, our work consisted in abstracting from the concrete proposals that can be found in the literature, universal principles that capture the essence of these notions as far as their application to our evolutionary approach is concerned, and make it applicable to a wider range of languages and models. This abstraction process was conducted in two directions. On the one hand, we searched for mathematical patterns that would capture the semantics of the coordination mechanisms that we wanted to make available in OBLOG. This search lead us to identify general properties that, once satisfied by a language, provide for the separation between coordination and computation. These mathematical aspects were motivated and discussed in section 2. Their properties are developed in greater detail in [15]. On the other hand, because the approach to evolution that we motivated above was developed in response to concrete needs for supporting development work in highly volatile business domains, we had to workout a way of making these coordination mechanisms available in concrete platforms for system development. As a result, a design pattern was developed that can make the proposed coordination mechanisms available over platforms for component-based system development like CORBA, EJB and COM. More specifically, support for the whole approach is being developed as a Coordination Development Tool [19] for which an instance exists already that allows for JAVA programs to be coordinated. This design pattern was discussed in section 3. More examples are available in [3], where the notion of contract was proposed as an extension to the UML [10] for representing connectors with the semantics discussed herein, and in other papers and reports that cover different areas of application – banking [5], stock-trading [22] and telecommunications [21]. We should stress that, even if current component-based technology does not provide a convenient way for coordinating components to be deployed as first-class citizens, our experience is that the benefit of having this form of coordination available as a primitive construction when specifying components and their interactions is that it avoids the burden of having to code such a pattern each time it is needed. In the meanwhile, tools that, like OBLOG, provide automatic code generation from high level specifications, must hide the implementation complexity of coordination, allowing the developer just to specify the connectors themselves. Finally, it is important to point out that there is no mathematical proof of the correctness of the design pattern wrt the categorical pattern. Given the lack of formal semantics for component-based development platforms, such a proof is, indeed, impossible. Nevertheless, and given that the design pattern was developed on the basis of the abstractions distilled in the notion of coordination formalism, we showed what correspondence there is between the two notions. In fact, we even showed that the morphism-based view of superposition (as opposed to the colimit-based view that we preferred, as discussed in section 3) can also be implemented using successive layers of wrappers as suggested in [11]. The ultimate justification for "correctness" is in the successful application that has been made of these notions through contracts . Further work is in progress, both in improving the evolutionary approach and its support, as well as in incorporating other associated mechanisms that we have developed over the proposed mathematical pattern. The former include the modelling, as contracts, of the primitives made available by the UML for capturing interactions – use cases and collaboration diagrams [10] – as a means of providing an integrated semantics for evolution. The latter, based on [23,24], include the mechanisms via which reconfiguration can be effectively performed "on-line" as well as the operations through which connectors (contracts) can be constructed.

References 1. R.Allen and D.Garlan, "A Formal Basis for Architectural Connectors", ACM TOSEM, 6(3), 1997, 213249. 2. L.F.Andrade, J.Gouveia, P.Xardone and J.Camara, "Architectural Concerns in Automating Code Generation", in Proc. TC2 First Working IFIP Conference on Software Architecture, P. Donohoe (ed), Kluwer Academic Publishers. 3. L.F.Andrade and J.L.Fiadeiro, "Interconnecting Objects via Contracts", in UML'99 – Beyond the Standard, R.France and B.Rumpe (eds), LNCS 1723, Springer Verlag 1999, 566-583. 4. L.F.Andrade and J.L.Fiadeiro, “Coordination: the Evolutionary Dimension", in Technology of ObjectOriented Languages and Systems – TOOLS 38, W.Pree (ed), IEEE Computer Society Press 2001, 136-147. 5. L.F.Andrade and J.L.Fiadeiro, “Coordination Technologies for Managing Information System Evolution", in Proc. CAISE’01, A.Geppert (ed), LNCS, Springer-Verlag 2001, in print. 6. L.F.Andrade, J.L.Fiadeiro, J.Gouveia, A.Lopes and M.Wermelinger, "Patterns for Coordination", in COORDINATION'00, G.Catalin-Roman and A.Porto (eds), LNCS 1906, Springer-Verlag 2000, 317-322 7. R.Back and R.Kurki-Suonio, "Distributed Cooperation with Action Systems", ACM TOPLAS 10(4), 1988, 513-554. 8. J.P.Banâtre and D.Le Métayer, "Programming by Multiset Transformation", Communications ACM 16(1), 1993, 55-77. 9. L. Bass, P.Clements and R.Kasman, Software Architecture in Practice, Addison Wesley 1998. 10. G.Booch, J.Rumbaugh and I.Jacobson, The Unified Modeling Language User Guide, Addison-Wesley 1998. 11. J.Bosch, "Superimposition: A Component Adaptation Technique", Information and Software Technology 1999. 12. K.Chandy and J.Misra, Parallel Program Design - A Foundation, Addison-Wesley 1988. 13. J.L.Fiadeiro and T.Maibaum, "Categorical Semantics of Parallel Program Design", Science of Computer Programming 28, 1997, 111-138. 14. J.L.Fiadeiro and A.Lopes, "Semantics of Architectural Connectors", in TAPSOFT'97, LNCS 1214, Springer-Verlag 1997, 505-519. 15. J.L.Fiadeiro and A.Lopes, "Algebraic Semantics of Coordination, or what is in a signature?", in AMAST'98, A.Haeberer (ed), Springer-Verlag 1999. 16. N.Francez and I.Forman, Interacting Processes, Addison-Wesley 1996. 17. E.Gamma, R.Helm, R.Johnson and J.Vlissides, Design Patterns: Elements of Reusable Object Oriented Software, Addison-Wesley 1995. 18. D.Gelernter and N.Carriero, "Coordination Languages and their Significance", Communications ACM 35, 2, pp. 97-107, 1992. 19. J.Gouveia, G.Koutsoukos, L.Andrade and J.L.Fiadeiro, “Tool Support for Coordination-Based Software Evolution", in Technology of Object-Oriented Languages and Systems – TOOLS 38, W.Pree (ed), IEEE Computer Society Press 2001, 184-196. 20. S.Katz, "A Superimposition Control Construct for Distributed Systems", ACM TOPLAS 15(2), 1993, 337-356. 21. G.Koutsoukos, J.Gouveia, L.Andrade and J.L.Fiadeiro, “Managing evolution in Telecommunications Systems”, submitted, accessible at www.fiadeiro.org/jose/papers. 22. G.Koutsoukos, T.Kotridis, L.Andrade, J.L.Fiadeiro, J.Gouveia and M.Wermelinger, “Coordination Technologies for Business Strategy Support: a case study in Stock Trading”, submitted, accessible at www.fiadeiro.org/jose/papers 23. M.Wermelinger and J.L.Fiadeiro, "A Graph Transformation Approach to Software Architecture Reconfiguration", Science of Computer Programming, in print. 24. M.Wermelinger and J.L.Fiadeiro, "Algebraic Software Architecture Reconfiguration" in ESEC/FSE'99, LNCS 1687, Springer-Verlag 1999, 393-409.