An Architectural Approach to Auto-Adaptive Systems L.Andrade1 and J.L.Fiadeiro1,2 [email protected]
, [email protected]
1 ATX Software SA, Alameda António Sérgio 7, 1C, 2795-023 Linda-a-Velha, PORTUGAL 2 Dep. Informatics, Fac. Sciences, Univ. Lisbon, Campo Grande, 1749-016 Lisboa, PORTUGAL Abstract We propose a layered architecture based on the separation of two concerns – computation and coordination – as a means of achieving higher levels of auto-adaptability. This separation makes it possible for adaptation to be enforced through the reconfiguration of the system in terms of the mechanisms that coordinate interactions, superposing connectors among components of the system without intruding on the way the computations that they perform locally are implemented.
1. Introduction Our aim in this paper is to show how, by adopting an architectural approach, we can achieve higher levels of auto-adaptability in systems that are required to operate in environments that are “business time critical”, which includes those that make use of Web Services, B2B, P2P, or otherwise operate in what is known as “internet-time”. More and more, organisations require systems that can adapt themselves to the environment in which they operate. This may be seen as a general response to the need for controlling complexity: monolithic, rigid systems built under the assumption that all use cases have been foreseen are, normally, too costly to build, impossible to verify against requirements, and slow in responding to failure. Not to mention that, as experience has shown, our capacity to anticipate how the application domain will evolve is, usually, rather poor… On the other hand, this tendency is also a reaction to the current need for systems that offer services that can match the use context, for instance user profiles and preferences. Such contexts are, typically, very dynamic, prompting the need for increased levels of agility and flexibility in the way systems can be evolved to match new requirements, including user-driven customisations. Even more challenging is the fact that many organisations now look at their information systems as instruments of their own business strategies, namely when it is important to have an aggressive presence in the market. In such cases, it is not only the capacity to be
evolved that is determinant but the ability for the system to react autonomously, in run-time, without interruption of service, by adapting itself to the new circumstances. We claim that the development of such a new breed of systems can be enhanced by adopting an architectural approach through which two different layers can be recognised in system configurations. On the one hand, a lower layer in which components reside that correspond to core business entities that are relatively stable. These components encapsulate basic services through computations that they perform locally and autonomously in the sense that there are no implicit interactions among them. Instead, the interconnections between components that are necessary for the global properties that are required of the system as a whole to emerge from these computations are externalised as first-class citizens. We call such connectors “coordination contracts”. They reside in the second architectural layer, which we call the “coordination layer“, and they can be dynamically superposed over groups of components present in the lower level to coordinate their joint behaviour, or over individual components to regulate or monitor their behaviour. The layering is strict in the sense that the components do not interact directly with the contracts (connectors): components cannot even know whether or how they are being coordinated. Instead, they are “orchestrated” by putting in place among them the contracts that coordinate their behaviour and ensure the emergence of required system properties. The separation between these two concerns – computations and coordination mechanisms – builds upon research on Coordination Languages and Models (e.g. ), Parallel Program Design (e.g. ), Software Architectures (e.g. ) and Reconfigurable Distributed Systems (e.g. ). It is not language-specific in the sense that it can be enforced, more or less directly, over most platforms for component-based development through micro-architectures  that, basically, introduce an eventbased intermediate layer. Event-based middleware represents an advantage in that it directly supports a service-oriented approach to interconnection but, as demonstrated in , the externalisation of the
coordination mechanisms can be achieved indirectly through design patterns for which there is hardly any penalty that needs to be paid in terms of computational performance, especially when compared with the gains that are obtained in agility. Hence, in this paper, we shall build over event-based interaction mechanisms and concern ourselves only with the presentation of the modelling primitives that we are proposing for operating over computation/coordination layered architectures. To be more generic, we shall not address the computation layer specifically: we will assume the general notion of component that has been popularised in , by which we mean “a unit of composition with contractually specified interfaces and explicit context dependencies only”. Indeed, the primitives that we use for modelling coordination are independent of the nature of the components to which they apply. As we shall see, they rely on abstract coordination interfaces and concrete adaptation mechanisms through which specific components can be bound to these interfaces. The primitives that we propose for the coordination layer – coordination laws, interfaces and contracts – have already been presented in other publications, namely as extensions to the UML , and applied to different business domains (see www.atxsoftware.com). Therefore, in the paper, we shall limit ourselves to a short introduction to their intuitive semantics and pragmatics in so far as they are needed for understanding the selfadaptation aspects. For these, we will introduce what we call “coordination contexts” – primitives through which the system can evolve through dynamic reconfiguration. These create an additional third architectural layer in which the ability for auto-adaptation can be enforced and fine-tuned.
2. Coordination Primitives As already motivated, our architectural approach is based on a methodological and technological separation between the computations performed by system components and the mechanisms that coordinate their interaction. Components encapsulate collections of services that determine specific system functionalities that are made available through computations performed on local states. Components correspond to “core”, “stable” entities in the sense that auto-adaptation should not be intrusive on their implementation but, instead, make different uses of the services that they provide or adapt them to new circumstances through the superposition of coordination mechanisms. These are modelled explicitly and outside the components in the form of what we have named “coordination contracts”. A coordination contract makes available the expressive power of a connector in the terminology of software architectures . It consists of a prescription of coordination effects (the glue of the connector) that are
superposed on a collection of partners (system components) when the occurrence of given triggers is detected. Coordination contracts ensure that given global properties of the system will emerge from the functionalities of the components and the interaction established between them through the contracts. They are the means through which we propose that auto-adaptation be conceived in the sense that, in order to adapt itself to a new use context, the system should be able to reconfigure itself by removing existing contracts and plugging in new contracts from which different behaviour can emerge. Hence, it is important that we make it possible for the system to be reconfigured in terms of revising which contracts apply to which components without interfering in the way components are implemented. The microarchitecture that we developed for implementing coordination contracts in platforms for component-based development  supports, precisely, this degree of dynamic, non-intrusive reconfiguration. Coordination contracts are instances of coordination laws for specific implementation environments. This is why we abstain from providing a concrete syntax for them and, instead, will analyse and discuss, in more detail, the specification of the laws from which they derive. The purpose of a law is to describe a generic pattern of coordination that has a direct meaning in terms of the application domain. A typical correspondence is with business rules in the sense of capturing forms of orchestrating the core services made available in the computation layer to satisfy specific and current business requirements. In the description of a law, the partners that are subject to coordination are not identified as specific components of a specific system but in terms of a number of coordination interfaces (the roles of the connector) that act as types that can be instantiated with components of the system when a contract that instantiates the law is activated on a particular configuration. The trigger/reaction mode of coordination that we have in mind requires that each coordination interface identifies which events produced during system execution are required to be detected as triggers for contracts to react, and which services must be made available for the reaction to superpose the required effects. The degree of dynamic reconfigurability that can be achieved through coordination depends directly on the ability of the execution environment to make available interactions as recognisable triggers. For instance, distributed systems and object-based environments are paradigmatic examples in which collections of triggers are explicitly made available. In an object-oriented environment, typical events that constitute triggers are calls for operations/methods of instance objects, and typical services are the operations that are made public by the object class. In such cases, coordination interfaces can be identified with abstractions already made available in object-oriented programming through mechanisms such
as class interfaces in Java, pure virtual classes in C++, and abstract classes in Eiffel. We are going to illustrate our approach with a very simple example from banking, a domain in which we have developed several applications using architectural techniques. One can think that, as a core concept of banking, an account offers two basic functionalities – credits and debits. The methodology that we are building around the architectural separation between coordination and coordination suggests that, in order to model specific business activities that involve bank accounts, we should superpose whatever coordination mechanisms are required as external entities, rather than specialise the class with new, “hard-wired” features each time a new business rule, or changes to existing business rules, come into play. For instance, restrictions on debits should not be “hard-wired” as pre-conditions on the basic method that performs the debit but, rather, as contracts that coordinate specific interactions that involve the method. Hence, in order to model the business rules that apply to withdrawals as performed by customers, one should consider a coordination interface with accounts that only assumes the basic functionality of debits: coordination interface account-debit type-id account services debit(a:Money); balance():Money; properties balance() after debit(a) is balance()-a end
The name under type-id designates the family of components that exhibit an interface that is compatible with the requirements that are being specified in terms of services, triggers, and properties thereon. The inclusion of properties in the interface provides means for requirements to be specified on the components that are allowed as instances of the partners (actual parameters of a contract). In this example, we are stating minimal properties on the functional behaviour of the services included in the interface, namely that debits interact with observations of the balance as expected. We shall not expand on the language that can be used for writing such properties except to say that it should lend itself to some means of formal (ideally mechanical) verification through which instantiations can be deemed to be correct or not. In the context of object-based environments, instances of partners correspond to objects whose classes exhibit the required interface. In this case, type-id can designate any class in the inheritance hierarchy that is compatible with the corresponding coordination interface. In a more general, component-based environment, we need to be more explicit on the notion of compatibility between component interfaces and coordination interfaces, which depends on the nature of the language in which the components are developed. Indeed, we are aiming for heterogeneous environments in which components of arbitrary nature need to be coordinated,
which means that the instantiation mechanism for contract partners has to rely on explicit rules that establish when the interface of a given component complies with the corresponding coordination interface. As a business activity, a withdrawal by a given customer is an operation that involves both an account and a customer, regardless of the way that the customer is represented in the system, i.e. of whether it is a software component in the information system, an interface object that captures an external agent of the system, etc. The corresponding coordination interface is agnostic with respect to any specific form of representation and allows them to evolve as new decisions are made during the lifetime of the system. coordination interface customerwithdrawal type-id customer services owns(a:account):Boolean events withdrawal(n:Money; a:account) end
The distinction between services and events is as discussed before. For the proposed coordination, we are required to detect as triggers events that consist of customers performing withdrawals and be provided with services that query about the account ownership relation. Notice that, at the level of accounts, we did not require the ability to detect any events because the trigger is purely on the side of the customer. In the traditional, object-oriented way, withdrawals are modelled by direct calls to the debit operation of the corresponding account. That is to say, as part of the design of the system, withdrawal(n,a) establishes a call a.debit(n). Because the system operation that is actually performed is the debit, restrictions that derive from the business activity turn out coded in the account operation. The alternative way that we are proposing through coordination contracts consists in providing an explicit representation of the business activity that does not require changes to be performed on the implementations of the business entities that are involved but, rather, is “superposed” on the partners of the contract without their “knowledge”. More concretely, superposition imposes synchronisation points in the lives of the components involved in the contract: each triggerreaction clause in a contract identifies a point of “rendezvous” in which the components are brought together to synchronise their lives. In execution terms, the “rendezvous” is seen as an indivisible, atomic action. Each partner has its own “view” of the action and participates in it accordingly. This decoupling of roles in an interaction is essential for supporting non-intrusive changes by letting the partners evolve independently. The law that models the standard coordination mechanism that regulates withdrawals can be defined as follows:
coordination law standard-withdrawal partners a:account-debit, c:customerwithdrawal coordination when c.withdrawal(n,a) with a.balance() ≥ n and c.owns(a) do a.debit(n); end law
Instances of this very simple law are contracts that establish interconnections between the instances of the corresponding partners to which they are applied. Such interconnections establish how customers and bank accounts interact through the triggers identified under “coordination”. More specifically, each such coordination rule identifies, under the “when” clause, a trigger to which the contract will react – a request by the customer for a withdrawal in the case at hand. The reaction to be performed to occurrences of the trigger is identified under the “do” clause – a debit for the amount and on the account identified in the trigger. Under the “with” clause, we include conditions that should be observed for the reaction to be performed. If any of the conditions fails, the reaction is not performed and the occurrence of the trigger fails. Failure is taken to be handled by whatever mechanisms are provided by the language used for the deployment of the actual components that instantiate the partners involved in the trigger. In the example, this means that any withdrawal for which the requested amount is strictly greater than the balance or the customer does not own the account generates a failure in the customer and is not executed. In what concerns the implementation of the coordination rule, the whole interaction is handled as a single transaction, i.e. it consists of an atomic event in the sense that the trigger reports a success only if all the operations identified in the reaction execute successfully and the conditions identified under the “with” clause are satisfied. Notice that the “with”-conditions are superposed over the guards that apply to the operations that perform the reaction to the trigger. Hence, the reaction may fail even if the “with”-clause is satisfied. In order to illustrate how laws can be used for supporting changes in the domain, consider the situation in which the bank decides to make a VIP-package available to selected customers through which they can use a credit-facility to overdraw designated accounts. The law that models VIP-withdrawals can be defined as follows: coordination law VIP-withdrawal partners a:account-debit, c:customerwithdrawal attributes credit:money coordination when c.withdrawal(n,a) with a.balance()+credit≥n and c.owns(a) do a.debit(n); end law
In this contract, we have included a local attribute – credit – that takes the value that has been negotiated between the customer and the bank for overdrafts on the specific account over which the contract is superposed. This means that the same customer may subscribe different contracts for different accounts and that different customers that own the same account can subscribe different contracts as well. In fact, we have not included in the model any information on the conditions under which a given customer can subscribe a VIP-withdrawal for a given account. This is left for the “coordination contexts” through which the management of the configuration of the system, in terms of establishing, at each state, which contracts apply to which components, is performed. The definition of such coordination contexts is, precisely, the subject of this paper as discussed in the next section.
3. Contexts for Auto-Adaptation The main purpose of coordination laws and contracts is to model the collaborations that need to be put in place among components to ensure that the global properties that are required of the system in any given state can emerge from the interactions and the computations that are being performed locally within the components that are present in the configuration of the system, at that same state. In broad terms, a configuration of a system consists of a collection of components that deploy core entities of the business domain and a collection of instances of contracts that coordinate the interactions between them. An example of a configuration in the banking domain that we have been discussing is given below in terms of a diagrammatic notation that should be self-explaining: two customers, mary and john, are joint owners of account aaa/bbb; mary uses a VIP-withdrawal package with credit 3000 and john a standard-withdrawal package; mary is the owner of a second account ccc/ddd with a standardwithdrawal contract. john: Customer
aaa/bbb: Account Balance:1000
The use of coordination contracts for enforcing business rules leads to an approach to the evolution process that is based on reconfiguration techniques as known from Configurable Distributed Systems . At each moment of the life of the system, its configuration can be
identified with the collection of components that have been created (but not destroyed), interconnected through the contracts that will have been superposed among them according to the laws that determine how business is conducted. As part of the evolution process, components may be added, removed, and replaced, new contracts may be superposed, existing contracts can be removed or replaced, and so on. All these operations rewrite the configuration. The new configuration will dictate how the system will behave from then on, computationally speaking, through the revised set of components and interactions among them. On the other hand, as a result of the computations performed by the components, or the interactions that are maintained with the environment, the global state of the system changes, which may require a reconfiguration to adapt it to the new circumstances. Having mechanisms for evolving systems is not the same as prescribing when and how these mechanisms should be applied. Evolution is a process that needs to be subject to rules that aim at enforcing given policies of organisations over the way they wish or are required, e.g. through legislation, to see their businesses conducted. For this purpose, we provide a modelling primitive – coordination contexts – through which the reconfiguration capabilities of the system can be automated, both in terms of ad-hoc services that can be invoked by authorised users, and programmed reconfigurations that allow systems to react to well identified triggers and, hence, adapt themselves to changes brought about on their state or configuration. For instance, in the banking domain that we have been using as an example, a coordination context normally exists for each customer. The purpose of this context is to manage the relationships that the customer may hold with its various accounts according to the packages that the bank offers. Such contexts are made available to bank managers each time the customer goes to a branch, or to the customer itself through the Internet or ATMs. The syntax that we are developing for contexts can be illustrated around this example as follows: coordination context customer(c:customer) workspace component types account, customer contract types standard-withdrawal, VIPwithdrawal, pensioner-package, homeowner-package constants min-VIP: money attributes avg-balance = … services subscribe_VIP(a:account,V:money): pre:
c.owns(a) and avg-balance≥min-VIP and not exists home-owner-package(c,a) post: exists’ VIP-withdrawal(c,a) and VIP-withdrawal(c,a)’.credit=V
subscribe_home(a:account): pre: not exists pensioner-package(c) post: c.owns(a)’ and exists’ home-owner-package(c,a)
not exists pensioner-package(c) and not exists home-owner-package(c,a)
post: exists’ pensioner-package(c)
rules VIP-to-std: when exists VIP-withdrawal(c,a) and avg-balance < min-VIP post not exists’ VIP-withdrawal(c,a) and exists’ standard-withdrawal(c,a)
Besides the laws that we introduced in the previous sections for managing withdrawals, we have added the names of a few other ones just to make the example more “interesting”. Each instance of a coordination context is “anchored” to a component or set of components. In the example, the anchor is a customer instance, referred to as c in the definition of the context (type). Under “workspace” we identify the component types and laws that are made available for evolving the way the anchor interacts with the rest of the system. Configuration services correspond to operations for adhoc reconfiguration, i.e. they are performed on demand from users of the system. Notice that we include in this category operations that, in traditional OO modelling, are assigned to classes like object creation. The rationale is that, by interfering with the population of the system, such operations address the evolution of its configuration and, hence, their use should be regulated in the scope of a coordination context. Configuration services involve both components and contracts. In the example above, besides the creation of new accounts, three other services are provided for each of the laws that model a financial package that can be offered to the customer. These services have pre-conditions through which policies are enforced. For instance, VIP-withdrawals are not available on accounts that support a home-owner package. Pensioners are not allowed to subscribe home-owner packages. Configuration rules correspond to different ways of programmed reconfiguration, i.e. to the ability of the system to reconfigure itself in reaction to external events or internal state changes. In the example above, a VIPpackage is replaced by a standard one when the average balance of the customer falls below the minimum value set up for being a VIP. Typically, the programmed configuration rules capture more dynamic properties that require specific actions to be taken in reaction to certain state changes, for instance to restore consistency with respect to policies like the ones that regulate VIP-status for customers. Notice the use of a post-condition in the configuration rule instead of a specific (trans)action to be performed as a reaction. Together with the use of pre/post-conditions in the definition of services, this allows us to separate context interfaces from their implementations, which adds to flexibility by allowing the choice of the actual reconfiguration operations to depend on “lower level” issues like the physical distribution topology. The pre/post-conditions capture higher level properties such as business policies that should be elicited during analysis: for instance, eligibility conditions as illustrated
in the example, or dependencies that regulate the subscription of different business products, or legislation that becomes applicable, etc. They can also be used for establishing the mechanisms through which the system can adapt itself to changes related to the circumstances in which it is operating, for instance properties of the network over which interconnections are established, which is an essential feature for addressing mobility. Contexts should not be treated as "normal" components in the sense that they are not used in configurations to add new functionalities to the system. That is to say, they are not defined in order to contribute to the functional properties that the system can exhibit but only to manage the way the system is allowed to evolve. For instance, coordination contexts can be used, among others, for modelling actors as in use cases, i.e. the mechanisms through which "users" (regardless of whether they are human, physical, software, etc) have access to the system, except that, now, such users can interfere with the configuration of the system, not just with its state. Different contexts may even make use of different implementations for the same operations, for instance reflecting the fact that access to the system may be provided through different channels. Hence, for instance, the withdrawal service of customer is typically offered in a coordination context that models access over the counter at the local branch, but not if the access is via the Internet, whereas the amount that can be requested will be limited for accesses via an ATM. This is the sort of flexibility that is required for autoadaptation, namely for taking into account changes that occur at the level of the circumstances in which the system is being used, which include changes of business profiles as well as more technological aspects such as changes in the channels through which the system is used. For instance, as already mentioned, auto-adaptation in this sense is an essential mechanism for addressing mobility. The proposed separation of concerns – computation/coordination – allows, precisely, for the auto-adaptation mechanisms to operate at the coordination level without interruption of the services deployed at the computational layer thus ensuring continuity in run-time. Another important application area is fault-treatment, leading to self-healing capabilities, an avenue that we are currently exploring in collaboration with the Universities of Kent, London (King’s College), and Florence. Finally, we should point out that a coordination context may have a state of its own in order to represent information that only makes sense in that business context, e.g. “attributes” of the subsystem identified by the context. For instance, properties like “average balance”, where the average is taken over all the accounts that a given customer owns, require a context in which all these accounts are present.
4. Concluding remarks A new approach to system construction and evolution is being developed which is based on the externalisation of the mechanisms that coordinate interactions between components from the code that implements the computational requirements of the services they provide. When this externalisation is followed by the explicit representation of the coordinating units as “first class” entities”, it becomes possible to make components and coordinators (contracts) to evolve independently and nonintrusively on each other’s implementations. As a result, systems may evolve in ways that are compositional with respect to the application domain, which is a key ingredient for being able to model auto-adaptation at the higher-levels of abstraction in which the causes that drive adaptation can be perceived and reasoned about. This is what we have aimed to show in this paper through the definition of a new semantic primitive – coordination context – for structuring the evolution of systems, namely the one that results from the need for auto-adaptation. A coordination context defines a set of services and rules through which the system can be reconfigured, for instance reflecting a specific business activity within the organisation or a specific mechanism for the system to adapt itself to changes occurring in the communication network. The invocation of these services and the activation of these rules are subject to a number of constraints that impose requirements and restrictions on the way the system can evolve. The methodology that we have been developing around the notion of “coordination contract” is based on a solid mathematical semantics that is independent of the language or implementation platform in which system are designed and deployed. This independence is achieved, basically, through the use of Category Theory, which also provides for the semantics of the reconfiguration process through algebraic graph rewriting .
5. References 1. 2.
4. 5. 6.
R.Allen and D.Garlan, "A Formal Basis for Architectural Connectors", ACM TOSEM, 6(3), 1997, 213-249. 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. 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. G.Booch, J.Rumbaugh and I.Jacobson, The Unified Modeling Language User Guide, Addison-Wesley 1998 E.Gamma, R.Helm, R.Johnson and J.Vlissides, Design Patterns: Elements of Reusable Object Oriented Software, Addison-Wesley 1995 D.Gelernter and N.Carriero, "Coordination Languages and their Significance", Communications ACM 35, 2, pp. 97107, 1992.
8. 9. 10. 11.
J.Gouveia, G.Koutsoukos, L.Andrade and J.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. S.Katz, "A Superimposition Control Construct for Distributed Systems", ACM TOPLAS 15(2), 1993, 337356.. J.Magee and J.Kramer, "Dynamic Structure in Software Architectures", in 4th Symp. on Foundations of Software Engineering, ACM Press 1996, 3-14. C. Szyperski, Component Software: Beyond ObjectOriented Programming, Addison Wesley 1998. M.Wermelinger and J.L.Fiadeiro, "A Graph Transformation Approach to Software Architecture Reconfiguration", Science of Computer Programming, in print.