Towards an Algebra of Architectural Connectors: a Case Study on

2 downloads 0 Views 79KB Size Report
Towards an Algebra of Architectural Connectors: a Case Study on. Synchronization ..... 2g oo. Figure 5. Transient action subsumption with a fixed diagram ual identities. .... the interaction requirements of a large spectrum of software systems.
Towards an Algebra of Architectural Connectors: a Case Study on Synchronization for Mobility Michel Wermelinger

Jos´e Luiz Fiadeiro

Departamento de Inform´atica Universidade Nova de Lisboa 2825 Monte da Caparica Portugal E-mail: [email protected]

Departamento de Inform´atica Universidade de Lisboa Campo Grande, 1700 Lisboa Portugal E-mail: [email protected]

Abstract To cope with the flexibility and extensibility needed for the specification of the architecture of evolving software systems, it is useful to have a set of primitive connectors from which new ones can be created in a systematic way as needs arise and to connect components only when they are required to interact. This could be achieved with a connector algebra whose constants are given primitive connectors and whose operations allow the (transient) creation of new connectors. This paper is a step towards that goal. We put forward an application-independent connector for partial action synchronization and three generic operations. Applied to the basic connector, they provide applicationspecific connectors for inhibition and full synchronization of actions. Moreover, we associate to each connector a condition stating when it should be applied to components.

1. Introduction Software Architecture has put forward the concept of connector to express complex relationships between system components, thus facilitating the separation of coordination from computation. This is especially important when a system has to evolve, since changes at both levels can be independent. For example, the system components may remain the same, but their interactions may vary continuously (as in mobile computing). On the other extreme, components change but their interactions with the environment remain the same (as when different clients access the same server using the same protocol). It is of course also possible that  This work was partially supported by JNICT through contracts PRAXIS XXI 2/2.1/MAT/46/94 (ESCOLA) and PRAXIS XXI 2/2.1/TIT/1662/95 (SARA) and by EQUITEL SA through project ARTS.

changes at the computation level imply changes at the coordination level (e.g., when new components, or new features to existing ones, are added). Even in this case, separate specification makes it easier to handle the problem. Thus, to cope properly with system dynamics at the software architecture level it is necessary for connectors to be as flexible as components. This means that it should be possible to easily create, remove, and change both connectors and components. Moreover, in order to have a grip on the complexity of change, connectors should be built in a systematic way, as has been done for components (using concepts such as structured programming and code reutilization). Garlan [6] also argues for principled ways of modifying connectors. His position is briefly summarized as follows. It is not always possible to adapt components to work with the existing connectors. Even in those cases where it is feasible, a better alternative might be to modify the connectors because usually there are fewer connector types than components types. Moreover, most Architecture Description Languages either provide a fixed set of connectors or only allow the creation of new ones from scratch, hence requiring from the designer a deep knowledge of the particular formalism and tools at hand. Conceptually, operations on connectors allow one to factor out common properties for reuse and to better understand the relationships between different connector types. The notation and semantics of such connector operators are of of course among the main issues to be dealt with. We feel that Category Theory is well suited to represent connectors and their construction at an abstract level independent of the formalism used to write specific connectors, thus revealing their fundamental properties. Capitalising on our previous work [3, 2, 13], we present three connector transformation operations. More precisely, they operate on

roles since those form the interface of a connector, i.e., they are the only parts of the connector available to the user. This means that the user can form new connectors even from those to which he has no access to the implementation. We also present a connection creation/removal operation. It applies a connector to some components only when the condition associated to the connector is met. We illustrate the operations with a generic action synchronization connector written in COMMUNITY [4].

2. Preliminaries In this section we present the basic notions of Category Theory, COMMUNITY, and connectors. Due to space limitations, we focus only on those aspects essential to understand the rest of the paper. Moreover, all technical details are omitted. The interested reader should consult the references for a complete and formal treatment of the subjects. This section also introduces the basic connector and the example application in which it will be used.

2.1. Category Theory Category Theory [10] is the mathematical discipline that studies in a general and abstract way the relationships between arbitrary entities. A category is a collection of objects (to represent entities) together with a collection of morphisms between pairs of objects (to represent the relationships) and a morphism composition operator . A morphism f with source object a and target object b is writ-

ten f : a ! b or a ! b. If f : a ! b and g : b ! c then g  f : a ! c. Diagrams are directed graphs—where nodes denote objects and arcs represent morphisms—and represent “complex” objects as configurations of smaller ones. The object thus configured can be retrieved through an operation on the diagram called colimit. Informally, the colimit of a diagram is the “minimal” object such that there is a morphism from every object in the diagram to it. f

The classical and trivial example is the category whose objects are sets, morphisms are total functions, and the composition operator is the usual function composition. In this case, between each pair of sets there are as many morphisms as total functions between those sets. Other morphisms are possible. For instance, the subset relation. In this case there is at most one morphism between a pair of sets, and the composition operator just states that the relation is transitive. The colimit of a diagram in this category is the minimal superset (i.e., the union) of the sets appearing in the diagram.

2.2. Community COMMUNITY is a UNITY [1] and IP [5].

program design language based on In this paper we only consider a subset of the full language. For our purposes a COMMUNITY program has the following structure: P var V init I do name : [guard [] ...

! assignment k assignment k . . . ]

where V is a set of typed variables and I is a boolean expression to be satisfied by the initial values of the variables. Action names act as rendez-vous points for program synchronization. At each step, one of the actions whose guards are true is selected and its assignments are executed simultaneously. The empty set of assignments is denoted by skip. A morphism from a program P to a program P0 states that P is a component of P0 and, as shown in [4], captures the notion of program superposition [1, 5]. Mathematically, the morphism maps each variable of P into a variable of P0 of the same type, and it maps each action name g of P into a set of action names fg01 ; : : : ; g0n g of P0 1 . The intuition is that those actions correspond to the behaviour of g when synchronizing with other actions of other components of P0 . Thus each action gi must preserve the functionality of g, possibly adding more things. In particular, the guard of g0i must not be weaker than the guard of g, and the assignments of g must be contained in g0i . It can be proved that COMMUNITY programs and their morphisms form a category in which every finite diagram has a colimit, which, by definition, is the minimal program that contains all programs in the diagram. Thus the diagram specifies the architecture and the colimit represents the resulting system. Since the proof of the existence of a colimit is constructive, the architecture can be “compiled” into a single program that simulates the execution of the overall system.

2.3. The Example Our example is inspired in the luggage distribution system also used to illustrate Mobile UNITY [12]. One or more carts move on a N units long track with the shape ? ! ? ?   "  ? ?  ? A cart advances one unit at each step in the direction shown by the arrows. The i-th cart starts from unit i. 1 Due to technical reasons, the mapping between actions of P and sets of actions of P0 is formalised as a partial function from P0 to P. See [13] for details.

Carti  var l : int init l = i do move: [true ! l := (l + 1) mod N]

3. The Operations 3.1. Role Specialization

According to this program, a cart is continuously moving around. The movement of the various carts must be synchronized in such a way that no collisions occur at the crossing. We henceforth omit the “mod N” operation.

2.4. Connectors A n-ary connector consists of n roles Ri and one glue G stating the interaction between the roles. These act as “formal parameters”, restricting which components may be linked together through the connector. Thus, the roles may contain variables and actions which are not used for the interaction specification. In Category Theory, all relationships between objects must be made explicit through morphisms. In the particular case of COMMUNITY programs, it means for example that two variables (or actions) of two unrelated programs are different, even if they have the same name. To state that variable (or action) x1 of program P1 is the same as variable (resp. action) x2 of P2 , even if x1 and x2 are different names, one needs a third, “mediating” program C—the channel— containing just a variable x and two morphisms σi : C ! Pi that map x to xi . Stating that two actions are the same means to synchronize them. In general, a channel contains the features that are shared between the two programs it is linked to, thus establishing a symmetrical and partial relationship between the vocabularies of those programs. Applying these ideas to connectors, for each role Ri there must be a channel Ci together with morphisms γi : Ci ! G and ρi : Ci ! Ri stating which variables and actions of Ri are used in the interaction specification, i.e., the glue. The categorical framework also allows one to make precise when an n-ary connector can be applied to components P1;:::;n , namely when morphisms ιi : Ri ! Pi exist. This corresponds to the intuition that the “actual arguments” (i.e., the components) must instantiate the “formal parameters” (i.e., the roles). As an illustration, an instantiated binary connector has the diagram P1 o

ι1

R1 o

ρ1

C1

γ1

/Go

γ2

C2

ρ2

/ R2

ι2

/ P2

As said above, sharing two actions (through a channel) means to synchronize them. However, the primitive notion used in this paper is action subsumption. Action a subsumes action b if b executes whenever a does. This can be seen as a partial synchronization: a is synchronized with b, but b can still execute freely. The connector is given in Figure 1. To make diagrams more compact, we abbreviate “a: [true ! skip]” simply as “a”.

In the connector just presented, the roles are the same as the channels because it is a general-purpose connector. To tailor it for a specific application, it is necessary to replace the generic roles by specialized ones that can effectively act as “formal parameters” for the application at hand. Role replacement is done in the same way as applying a connector to components: there must be a morphism from the generic role to the specialized one. The old role is cancelled, and the new morphism from the channel to the new role is the composition of the specialization morphism and the morphism from the channel to the old role. As an example, a unary connector ρ γ /R Go C

can be specialized with R0 ρ γ / R σ / R0 Go C becoming the new connector γ σρ / R0 Go C Let us apply this operation to the basic synchronization connector. Assume that two carts are approaching the crossing and one of them is nearer to it. To avoid a collision it is sufficient to force the nearest cart to move whenever the most distant one does. That can be achieved using our basic connector. However, in the full example, a cart will perform other actions (like loading and unloading bags) besides moving. It is therefore essential that the connector synchronizes only the “move” actions of two carts. Each role of the basic connector is thus specialized as shown in Figure 2. Notice that since the roles omit the initialization condition, they can be instantiated with any particular cart. Figure 3 shows the application of the new connector to the first and second carts and the resulting colimit.

3.2. Role Encapsulation A role may be replaced by a more specialized one as often as necessary, but once a role is instantiated with a component, it is by definition no longer a role and thus cannot be further specialized. The second operation we consider decreases the “arity” of a connector by instantiating some of its roles and making the result part of the glue. In categorical terms, the glue of the new connector is the colimit of the diagram consisting of the old glue plus the instantiated roles and the channels and components connected to them. As an illustration, encapsulating the instantiated second role of a binary connector R1 o

ρ1

C1

γ1

/Go

γ2

C2

ρ2

/ R2

ι

/P

C1  do a: [true ! skip]

a7!ab

G do ab: [true ! skip] [] b: [true ! skip] /

b7!fab;bg

o

C2  do b: [true ! skip]

a7!a

b7!b



 R2  do b: [true ! skip]

R1  do a: [true ! skip] Figure 1. Action subsumption R1  do a o

C1  do a

a7!a

a7!ab

/ G  do ab, bb7! o fab;bgC2  do b

/ R2  do b

b7!b

a7!move

b7!move





Cart  var l : int do move: [true ! l:=l+1] Cart  . . . o

Cart  var l : int do move: [true ! l:=l+1]

+ C1  do a

a7!move

a7!ab

/ G  do ab, bb7! o fab;bgC2  do b

b7!move

/ Cart  . . .

Figure 2. Role specialization results in a unary connector ρ1 R1 o C1

become a ternary connector σγ1

/ G

where σ is the morphism from G to G , the colimit of the diagram in the dotted rectangle. Returning to our example, a collision can also be avoided simply by stopping one of the carts until the other one has passed the crossing. In other words, the movement has to be inhibited, i.e., the guard of the “move” action has to become false. Using role encapsulation it is possible to obtain the generic inhibition connector given in Figure 4. Notice that action “i” of the new glue is not connected to any action of the role and is thus redundant.

3.3. Role Overlay The third operation allows to combine several connectors into a single one if they have some roles in common, i.e., if there is an isomorphism between those roles. The new connector is obtained as follows. First, take the diagrams of the original connectors and overlay them on the common roles. Remove the other roles and the corresponding channels. The glue of the new connector is the colimit of the remaining diagram. The new channel between the new glue and a common role is the role itself. As an illustration, two binary connectors with one common role R1 o

ρ1

C1

γ1

/Go

γ2

C2

ρ2

/ R2 o

ρ02

C20

γ02

/ G0 o

γ01

C10

ρ01

/ R0

1

R1 o

ρ1

C1

γ1

γ0

ρ01

/ G o 1 C0 eL L L 1 LL σ L R2

id

/ R0 1 / R2

where G is the colimit of the dotted diagram and id is the identity morphism. We could have taken as the new channel the colimit of the old channels, but this way it is simpler. Using this operation we can obtain full synchronization of actions a and b by making a subsume b and vice-versa. This is achieved by overlaying two copies of the basic connector in a symmetric way: the first (resp. second) role of one copy corresponds to the second (resp. first) role of the other copy. The diagram is / G  do ab,b o C1  do a C2  do b a7!ab

b7!fab;bg

a7!x

b7!y

R2  do y O 

R1  O do x 

a7!y

b7!x

/ G  do ab,b o C1  do a C2  do b  with colimit G  do xy. The new connector is therefore C1  do x x7!xy / G  do xy o y7!xy C2  do y b7!fab;bg

a7!ab

y7!y

x7!x

R1  do x 



R2  do y

C1  do a

/ G  do ab, b o

a7!ab

C2  do b

b7!fab;bg

a7!move

b7!move

 Cart  . . .



Cart  . . . l 7!l move7!move

 Cart1  . . .

move7!move l 7!l

l 7!l1 move7!m1 m2

Carts  var l1 , l2 : int  l 7!l2 / init l1 = 1 ^ l2 = 2 o Cart2  . . . move7!fm1 m2 ;m2 g do m1 m2 : [true ! l1 :=l1 +1 k l2 :=l2 +1] [] m2 : [true ! l2 :=l2 +1] Figure 3. Role instantiation

R1  do a o

a7!a

C1  do a

a7!ab

/ G  do ab,b bo 7!fab;bg C2  do b

+

R1  do a o

a7!a

C1  do a

a7!ai

b7!b

/ R2  do b

b7!i

/ Inhibit  do i: [false ! skip]

G  / do ai: [false ! skip] [] i: [false ! skip]

Figure 4. Role encapsulation

3.4. Transient Connectors The movement of two carts only has to be coordinated when they approach the crossing. Assume that track units 7 and 28 cross and that coordination should start when both carts are at most 3 units away from the crossing. Therefore the synchronization connector of Figure 3, which assumes that Cart2 is nearer to the crossing, only has to be applied when 0  7 ? l2 < 28 ? l1  3 _ 0  28 ? l2 < 7 ? l1  3 Let us abbreviate this interaction condition as I. In our previous work [13] the architecture of the system was given by a fixed diagram showing all possible connections between the existing components, and the interaction condition associated to a connector was coded into the guards of the glue’s actions in order to show explicitly when they could be executed. Returning to our example, action “move” of Cart1 executes freely when I is false, otherwise it is synchronized with the movement action of Cart2 . The glue of the action subsumption connector has to be changed accordingly: G var l1 , l2 : int G do ab0 : [I ! skip] do ab: [true ! skip] =) [] b0 : [I ! skip] [] b: [true ! skip] [] a: [:I ! skip] [] b00 : [:I ! skip] 0 00 Actions b and b correspond to the same action b of the

second role and since the disjunction of their guards is always true, they could be written as a single action “b: [true ! skip]”. But as presented, the glue makes explicit that the interaction only occurs when I is true, otherwise each of the roles’ actions executes freely as usual. Furthermore, since the interaction condition is used by the glue’s actions, the variables occuring in that condition must be declared by the glue. By definition, the state of the roles determines when the interaction takes place. Thus, the channels of the connector must show how the variables of I correspond to the variables of the roles. Making all these changes, Figure 3 becomes Figure 5. Notice that the interaction condition I is the same for whatever two carts are approaching the crossing. Thus, the diagram for an architecture with c carts includes c2 ? c copies of the connector, one for each pair hCarti ; Cart j i with i 6= j. If a cart is added to the system, c new copies of the diagram must be added, connecting the new cart to the existing ones. Similarly, if it is decided to avoid collisions using the movement inhibition method, then all the copies of the action subsumption connector must be replaced by copies of an inhibition connector, again one copy for each pair of carts. Such changes to an architecture can be described by an approach based on explicit reconfiguration commands [7, 8] or graph rewriting [9]. In this paper we take another view, based on the observation that the type of interaction depends often only on the type of the involved components, not on their individ-

C1  var l : int do a: [true ! skip]

a7!fab0 ;ag l 7!l1

l 7!l a7!move



Cart  . . .

l 7!l move7!move

 Cart1  . . .

l 7!l1 move7!fm1 m02 ;m1 g

G var l1 , l2 : int do ab0 : [I ! skip] / [] b0 : [I ! skip] [] a: [:I ! skip] [] b00 : [:I ! skip] o

Carts  var l1 , l2 : int init l1 = 1 ^ l2 = 2 0 / do m1 m2 : [I o ! l1 :=l1 +1 k l2 :=l2 +1] [] m02 : [I ! l2 :=l2 +1] [] m1 : [:I ! l1 :=l1 +1] [] m002 : [:I ! l2 :=l2 +1]

b7!fab0 ;b0 ;b00 g l 7!l2

C2  var l : int do b: [true ! skip] b7!move l 7!l

 Cart  . . .

move7!move l 7!l l 7!l2 move7!fm1 m02 ;m02 ;m002 g

 Cart2  . . .

Figure 5. Transient action subsumption with a fixed diagram ual identities. Instead of having a diagram that has to include a priori all possible interactions, we connect components only when needed. Thus the diagram is continuously changing, reflecting the current state of the system. This also implies that the addition or removal of connectors or components is not treated differently from changes due to the mobility of components. To be more precise, our proposal consists of the definition of transient connectors and a rule for applying them. A transient connector is defined as a connector T with an associated boolean expression I, called interaction condition, over the variables of its roles. A system is no longer specified as a diagram of interconnected components but instead as two sets, one of transient connectors, the other of components. At any point in time, the diagram is constructed by the application rule as follows. For every n-ary transient connector hI ; T i and for every n components Pi , if there are morphisms from T ’s roles to the components such that the values of the variables of the Pi corresponding to the variables of the roles make I true, then the components Pi are connected by a new copy of T . There are several things to be noted by this approach. First, the specification of a system becomes more compact. For our example, instead of a big diagram with c components and c2 ? c connectors, there is only one transient connector with T as given in Section 3.1 and I as given in the beginning of this section, where l1 and l2 now refer to the location variables of the first and second role, respectively. Second, since the application of a connector is based on its roles and condition, the designer can precisely constrain which connectors are applied to which components. In our example, the interaction condition cannot be satisfied if l1 =

l2 and therefore, as in the static approach, the connector’s roles will never be instantiated with the same component. Third, because the diagram construction operation is performed on the currently existing sets of components and connectors, any change to those sets will be immediately reflected in the diagrams constructed after that change. In our example, to change the collision avoidance policy, it is sufficient to change the connector while keeping the interaction condition. If the set of components and connectors is not changed it is possible to inspect the system for any state S (given by a proposition on the variables of all components): apply only those connectors whose condition is implied by S and compute the colimit of the resulting diagram. Furthermore, this approach subsumes our previous one because any transient connector hI ; T i can be systematically translated into a static connector S. Channels and roles remain the same, while the glue of S contains all the actions of the glue of T , with the guards strengthened by I, together with all the actions of all the roles, with their guards strengthened by :I. The new glue must of course declare all variables that occur in I and the channels must show from which role each variable comes. Moreover, the morphisms between the channels and the glue must be extended to include the roles’ actions. An example of such a transformation has already been shown (Figure 3 vs. Figure 5). As a further example, the synchronization connector of Section 3.3 becomes the one of Figure 6 (also shown in [13]), where Vi are the variables of role Ri and VI  V1 [ V2 are the variables that occur in the interaction condition.

R1  var V1 o do x

x7!x V1 \VI 7!V1

C1  var V1 \ VI do x

x7!fxy;xg V1 \VI 7!VI

G  var VI / do xy: [I ! skip] [] x: [:I ! skip] [] y: [:I ! skip] o

y7!fxy;yg V2 \VI 7!VI

C2  var V2 \ VI do y

y7!y V2 \VI 7!V2

R2  / var V2 do y

Figure 6. Transient action synchronization with a fixed diagram

4. Concluding Remarks The specification of evolving software architectures should be flexible enough to cater for transient interactions and it should be easily extensible to provide new connector and component types as needs arise. To fulfill the first goal we propose a specification consisting just of two separate sets (components and transient connectors), and an architecture construction operation stating that a connector is applied in every possible way to any set of components that instantiate its roles and satisfy the interaction condition. Through a judicious use of the condition and the roles, the combinatorics of the possible connections between components can be restricted in a precise way. As for the second objective, we presented three generic operations on roles (specialization, encapsulation, and overlay) taking one or two diagrams as arguments. In the latter case, the diagrams are connected through morphisms into a single diagram. In any case, the glue of the new connector is the colimit of (part of) the diagram. The operations were performed on an action subsumption connector, obtaining two other fundamental connectors: inhibition and synchronization. Although these results are only preliminary, we believe our approach has several advantages. The practical ones are as follows.

   

Describing and understanding connector operations and system architecture is made easier by the graphical nature of the categorical diagrams used for that purpose. The specification is more compact and simpler than a single, fixed diagram showing how components are (statically) interconnected [13]. The vocabulary of the system (i.e., the set of components and connectors) may be changed dynamically. If the vocabulary is fixed, the architecture can be inspected for a given state before run-time.

The perceived conceptual benefits of the proposed methodology are the following ones.



It deals both with elements and form [11]: there are operations to create new connectors and to create the architecture from the given vocabulary.

 

 

It subsumes the previous approach [13]. It is intuitive, because it models directly transient interactions between mobile components. Connections are generated only when needed and thus the diagram is continuously changing, reflecting the effects that the evolution of the computation had on the evolution of the connections. It provides an abstract framework to describe operations on connectors, independent of the language used to write glues and roles. It has a uniform, well-founded mathematical semantics: categorical diagrams and their colimits. Diagrams describe connectors and architectures, colimits are used to obtain new connectors and the program that describes the execution of the overall system, respectively.

Further experience is needed to see which primitive connectors and which operations are necessary to cope with the interaction requirements of a large spectrum of software systems. For the moment, such operations are used “statically”: the set of connectors needed is (incrementally) built in advance and only their application to components is dynamic. Our future research will address the problem of dynamic connector creation. Ideally, each mobile component states its requirements for each possible kind of interaction it may be part of. Such requirements could be seen as a connector pattern. When a set of components is about to interact, the algebra states how the actual connector is built from the patterns. This approach allows the flexible specification of connectors tailored for particular interactions between particular components.

References [1] K. M. Chandy and J. Misra. Parallel Program Design—A Foundation. Addison-Wesley, 1988. [2] J. L. Fiadeiro and A. Lopes. Semantics of architectural connectors. In M. Bidoit and M. Dauchet, editors, Proceedings of TAPSOFT’97, volume 1214 of LNCS, pages 505– 519. Springer-Verlag, 1997. [3] J. L. Fiadeiro and T. Maibaum. A mathematical toolbox for the software architect. In J. Kramer and A. Wolf, editors,

[4]

[5] [6] [7]

[8]

[9]

[10] [11]

[12]

[13]

Proceedings of the 8th International Workshop on Software Specification and Design, pages 46–55. IEEE Computer Society Press, 1996. J. L. Fiadeiro and T. Maibaum. Categorial semantics of parallel program design. Science of Computer Programming, 28:111–138, 1997. N. Francez and I. Forman. Interacting Processes. AddisonWesley, 1996. D. Garlan. Higher-order connectors. Presented at the Workshop on Compositional Software Architectures, Jan. 1998. J. Kramer and J. Magee. The evolving philosophers problem: Dynamic change management. IEEE Transactions on Software Engineering, 16(11):1293–1306, Nov. 1990. N. Medvidovic. ADLs and dynamic architecture changes. In L. Vidal, A. Finkelstein, G. Spanoudakis, and A. L. Wolf, editors, Joint Proceedings of the SIGSOFT’96 Workshops, pages 24–27. ACM Press, 1996. D. L. M´etayer. Software architecture styles as graph grammars. In D. Garlan, editor, Proceedings of the Fourth ACM SIGSOFT Symposium on the Foundations of Software Engineering, pages 15–23. ACM Press, 1996. B. C. Peirce. Basic Category Theory for Computer Scientists. The MIT Press, 1991. D. E. Perry and A. L. Wolf. Foundations for the study of software architecture. ACM SIGSOFT Software Engineering Notes, 17(4):40–52, Oct. 1992. G.-C. Roman, P. J. McCann, and J. Y. Plun. Mobile UNITY: Reasoning and specification in mobile computing. ACM TOSEM, 6(3):250–282, July 1997. M. Wermelinger and J. Fiadeiro. Connectors for mobile programs. Technical Report DI/FCUL TR-98-1, Department of Computer Science, University of Lisbon, Jan. 1998.