π -ADL: A Formal Description Language for Software Architectures E. Cavalcante, F. Oquendo, T. Batista Technical Report - UFRN-DIMAp-2014-102-RT September - 2014 - - Relatório Técnico Setembro The contents of this document are the sole responsibility of the authors. O conteúdo do presente documento é de única responsabilidade dos autores. Departamento de Informática e Matemática Aplicada Universidade Federal do Rio Grande do Norte www.dimap.ufrn.br π-ADL: A Formal Description Language for Software Architectures Everton Cavalcante1,2 , Flavio Oquendo2 , Thais Batista1 1 DIMAp, Federal University of Rio Grande do Norte, Natal, Brazil CNRS/Université de Bretagne-Sud, Vannes, France 2 IRISA-UMR [email protected], [email protected], [email protected] Abstract. Software architectures have played a central role in the development of software systems due to their potential for contributing with the achievement of functional and quality requirements. The specification of these architectures is an important activity in this context as it can document anticipated important design decisions, generates artifacts that pervade throughout the software development lifecycle, enables a better understanding and communication about the system, and is able to guide the evolution of the system. For this purposes, architecture description languages (ADLs) have been used as notations for representing and analyzing software architectures, thus producing models that can be used at design time and/or runtime. However, most existing ADLs: (i) are focused on structural, topological aspects of the architecture; (ii) do not provide an adequate support for representing behavioral aspects of the system; (iii) do not support an expressive description of dynamic aspects of the architectures; (iv) have limitations in terms of automated verification of constraints and properties, and; (v) are disconnected from the runtime level, thus entailing architectural mismatches and inconsistencies between architecture and implementation. In response to some of these challenges, this report introduces π-ADL, a formal language based on the π-calculus process algebra for specifying dynamic software architectures under structural and behavioral viewpoints and supporting their automated analysis with respect to functional and non-functional properties. Furthermore, π-ADL is integrated with the Go programming language (also based on π-calculus), thus enabling to automatically generate implementation code from architecture descriptions while benefiting from the capabilities provided by this language for constructing scalable distributed systems and handling multicore and networked computer architectures, as required by new generation software systems. Finally, π-ADL is endowed with a tool support for assisting software architects in the description of architectures and the generation of implementation code. In this report, a real-world flood monitoring system is used as an example of how to describe software architectures in π-ADL and then automatically generate source code in Go. Keywords: Software architectures, architecture description languages, π-ADL, Go. 1 π-ADL: A Formal Description Language for Software Architectures 1 2 Introduction The Software Architecture has attracted much attention from researchers and practitioners since the 1990’s [46]. The increasing complexity in software development and the demand for quality software systems are some of the most important issues that have increased the interest on this sub-discipline of Software Engineering. Due to their size and complexity and the frequent involvement of large teams of developers, rigorous and systematic approaches have been required to engineer software systems. Among the existing approaches, the architecture-driven engineering has been regarded as useful to successfully build software systems by considering software architectures as the central element in the development process. In this perspective, a software architecture is the main artifact for activities such as system design and analysis, model refinement towards implementation, and code generation. According to the ISO/IEC/IEEE 42010 International Standard [30], a software architecture can be defined as the fundamental conception of a system in terms of its constituent elements and their relationships with each other and the environment, as well as the principles that will guide the system design and evolution. Furthermore, software architectures have been recognized as a significant element for determining quality of software systems as decisions taken at the architectural level in an early stage can directly affect the realization of business goals and the achievement of functional and quality requirements [14, 45, 49]. Applied throughout the software lifecycle, good architectural practice has the potential to increase the understandability of a software system and the development process used to create it, to ensure that qualities of particular relevance are fulfilled, and to reduce the overall cost of software engineering [23]. Therefore, as a tool for improving design practices, architecture offers an effective way of modeling software systems while supporting reasoning and analyses about behavior before development is completed. While primarily a design-related discipline and artifact, architecture also has the potential to grant substantial benefits to the entire software development process including runtime evolution and adaptation. The specification of a software architecture is one of the main activities of an architecturedriven software development process as it enables to anticipate important decisions regarding the system design [46]. Good architectural models provide a basis for architectural analysis, understanding and communicating about a system’s design, and guiding a system’s evolution. For this purpose, formal, semi-formal, and even informal notations have been proposed to support such a specification at a high abstraction level and the communication among stakeholders (e.g., architects, developers, etc.). Among these notations, architecture description languages (ADLs) [18, 34] have become well-accepted approaches for representing and analyzing software architectures. ADLs are able to provide a systematic and disciplined way for describing architectures by using both a conceptual framework and concrete notation, thus producing models that can be used at design time and/or runtime. From a runtime perspective, software architectures are often characterized by two viewpoints, namely structural and behavioral viewpoints. The structural viewpoint is concerned with the structure of the system in terms of: (i) components, which represent functional elements of the system; (ii) connectors, which represent the interconnections among components supporting their interactions, and; (iii) configurations of components connected by connectors determining how they are composed together. In turn, the behavioral viewpoint is related to the internal operation (actions) of components and connectors and to the interaction among these elements. Most existing ADLs are focused on the structural viewpoint by providing means of describing the topological aspects of the architectures and few ADLs support the representation of behavioral aspects of the system, but such a support is rather limited. This is the case of π-ADL: A Formal Description Language for Software Architectures 3 well-known ADLs such as Wright [12], xADL [20], and ACME [22]. Dynamism is another important concern for the today’s software systems, which operate in environments that are highly dynamic and are subjected to changes. Therefore, a major challenge for ADLs is the ability of describing dynamic software architectures from both structural and behavioral viewpoints, thus including changes on structures and behaviors of the architecture itself and its components and connectors at runtime. Most of the proposed ADLs assume that architectures are static and few ADLs support an expressive description of dynamic aspects of architectures, such as Dynamic Wright [11], Rapide [31], and Darwin [32]. In software engineering, formal methods refer to mathematically based techniques for specifying, analyzing, and developing software systems. The complex and critical nature of software call for formal ADLs as means of supporting the automated verification and enforcement of constraints and properties. Furthermore, the verification of such properties becomes more important mainly when evolving software systems in order to ensure their correctness before, during, and after the evolution processes. However, to accomplish these requirements, ADLs are challenged to make a trade-off between expressiveness, user-friendship, and automated analysis capabilities enabled by the adoption of formal concerns. In response to the abovementioned challenges, the π-ADL description language [38] was proposed in early 2000’s as a formal, well-founded theoretically language for specifying software architectures under structural and behavioral viewpoints, unlike most existing ADLs. πADL is based on the π-calculus process algebra [36] and it was designed to support a comprehensive description of dynamic architectures and their automated rigorous analysis with respect to functional and non-functional properties. Therefore, the adequate support to these features as provided by π-ADL makes such a language prevail over other existing ADLs. In the last decade, the π-ADL language has been applied in several real-world scenarios and pilot projects for critical and large-scale systems. A recurrent problem of almost all ADLs (including π-ADL) is the decoupling between architecture descriptions and their respective implementations. As software architectures are typically defined independently from implementation, ADLs are often disconnected from the runtime level, thus entailing architectural mismatches and inconsistencies between architecture and implementation mainly as the architecture evolves. Therefore, even if a system is initially built to conform to its intended architecture, its implementation may become inconsistent with the original architecture over time. This problem becomes worse with the emergence of new generation programming languages because most existing ADLs do not properly address the features of this type of languages, which are characterized by their purpose of taking advantage of the modern multicore and networked computer architectures, as well providing concurrency capabilities. These features are not well supported by existing mainstream programming languages (such as C++ and Java), thus increasing the required complexity for constructing large-scale and dynamic systems, which are becoming typical in several application domains. In this context, the main goal of this report is to present an evolution of π-ADL in order to tackle some of the abovementioned issues related to the specification of software architectures and their relationship with the implementation level. The π-ADL language has undergone some modifications in its elements and syntax in order to meet the role of a deterministic implementation language while maintaining its formal features. In order to support this transition from architecture description to implementation, π-ADL was integrated with the Go programming language [7], an easy general-purpose language designed to address the construction of scalable distributed systems and to handle multicore and networked computer architectures. Go was chosen to serve as implementation language due to its basis on the π-calculus process algebra (the same of π-ADL), so that the straightforward relationship between elements of the π-ADL: A Formal Description Language for Software Architectures 4 languages has fostered such an integration. Finally, π-ADL was endowed with a tool support for assisting software architects in the description of architectures by using the π-ADL language and for automatically generating implementation code in Go. The remainder of this report is organized as follows. Section 2 introduces the π-ADL description language and its main elements. Section 3 presents the technologies used for supporting π-ADL architecture descriptions, as well as the mapping process from such descriptions to the implementation in Go. Section 4 details the π-ADL architecture description of a real-world flood monitoring system and how to generate the corresponding implementation of this system in Go. Section 5 contains final remarks and directions to future work. Finally, Appendix A presents the grammar specification of the π-ADL language. 2 The π-ADL architecture description language Process algebras (or process calculi) represent a mathematical theory for formally modeling concurrent systems and describing their communications/interactions at a high abstraction level [43]. As algebras are defined as mathematical structures composed of a set of values and a set of operations and properties on these values (such as commutativity, associativity, idempotency, and distributivity), processes in typical process algebras are values and the parallel composition of process can defined to be a commutative and associative operation on processes. Process algebras have played a role of formal underpinnings for describing software architectures as they provide a concurrency model, which expresses the existence of concurrent processes, and a communication model, which specifies how such processes can communicate with each other. By making a correspondence with software architectures and their formal specification, the concurrency model serves as a basis for specifying components that coexist in order to compose the architecture of the system. In turn, the communication model serves as a basis for specifying connectors representing the interactions between components (processes). In this perspective, π-ADL [38] was proposed as a formal language for describing dynamic software architectures under both structural and behavioral viewpoints, i.e., in terms of the elements that compose such architectures and their operation at runtime. This language has as theoretical foundation the higher-order typed π-calculus [36] (hence the name), a computationally complete (Turing-complete) process algebra that is able to provide an universal model of computation. As the classical π-calculus is not suitable to be used as an ADL because it does not provide constructs that enable architects to easily express architectures, the π-ADL language extends π-calculus by providing these constructs while achieving computational completeness and high expressiveness with a simple formal notation. In the π-calculus process algebra, communications/interactions between concurrent processes take place through channels, which are abstractions that enable the synchronization between such processes by sending and receiving values and/or names. Despite adopting the same notions of communicating process, the main feature that distinguishes π-calculus from previous process calculi such as CSP (Communicating Sequential Processes) [25] and CCS (Calculus of Communicating Systems) [35] is the name mobility, i.e., the ability of passing names over channels that can refer to processes or even channels, so that it is possible to send channels as data over other channels, for example. Therefore, it is possible to express changes in process configurations during computation, thus making π-calculus a suitable underlying model for describing dynamic software architectures, as envisioned by the π-ADL language. The following subsections present the main elements of the π-ADL description language, as well as a comprehensive example that illustrates how to make an architectural description by using this language. A complete description about the syntax of architecture descriptions in π-ADL: A Formal Description Language for Software Architectures 5 π-ADL and its main elements, as well as the operation semantics of the language can be found in [39, 41] and in Appendix A. 2.1 Architectural abstractions From the structural viewpoint, an architecture is described in π-ADL in terms of components, connectors, and their composition to form the system, i.e., the architecture itself as a configuration of components and connectors. Components represent the functional elements of the system, whereas connectors manage interactions among components as a component cannot be directly connected to another component. Components and connectors can be also composed to construct composite elements, which may themselves be components or connectors. In turn, from the behavioral viewpoint, components and connectors comprise a behavior, which expresses the interaction of an architectural element and its internal computation and uses connections to connect and transmit values. Analogously to π-calculus, π-ADL provides connections, which are abstractions that represent communication channels between architectural elements (components and connectors). By using typed connections, components and connectors can send (output connections) and receive (input connections) any value of the existing types, as well as connections themselves. In order to attach a component to a connector, at least a connection of the former must be attached to a connection of the latter. Such an attachment takes place by means of unification or value passing, so that attached connections can transport values, connections or even architectural elements. Figure 1 depicts the main architectural concepts of π-ADL. From a black-box perspective, only connections of components and connectors and values passing through connections are observable. From a white-box perspective, internal behaviors of such elements are also observable. Figure 1: Main architectural concepts of the π-ADL language. 2.2 Type system π-ADL is formally defined by a transition and type system [38, 41]. As depicted in Figure 2, the formal type system of π-ADL is structured upon three main layers, namely: • π-ADLB (base π-ADL), which provides the connection and behavior constructs; • π-ADLFO (first-order π-ADL), which extends π-ADLB with base and constructed data types, and; • π-ADLHO (higher-order π-ADL), which provides full first-class citizenship to the elements of the language. π-ADL: A Formal Description Language for Software Architectures 6 p-ADLHO higher-order p-ADL p-ADLFO-CT collection types p-ADLFO p-ADLFO-CV first-order p-ADL constructed value types p-ADLB base value types p-ADLFO-BV base p-ADL Figure 2: Layered π-ADL type system. Sections 2.2.1 to 2.2.3 present the atomic and composite data types defined in π-ADLFO . In turn, Section 2.2.4 presents the behavior types defined in π-ADLB . 2.2.1 Basic types The basic value types are used to express atomic values. Table 1 shows the base value types defined in the π-ADLFO−BV layer. Table 1: Base types defined in π-ADLFO−BV . Type Syntactic representation Definition Natural Integer Real Boolean String Natural Integer Real Boolean String Natural numbers (non-negative integers) Integer numbers (signed) Real numbers (floating-point) Boolean logical values Character strings In addition to these primitive types, π-ADL defines a type called Any that works as a generic type in the language, so that it can admit values of any type (basic or constructed ones). Therefore, an any type can be seen as a union type with no constraint on the type of value that it can hold. 2.2.2 Constructed types The π-ADLFO−CV layer provides constructors for defining composite types by using the base types from π-ADLFO−BV . Table 2 summarizes these constructed value types, each one described as follows. Tuple. Values of a tuple type tuple[T1 , T2 , . . . , Tn ] are n-uples (v1 , v2 , . . . , vn ) (n ≥ 2) in which each value vi is of type Ti . For example, the declaration t is tuple[Integer, String] refers to the declaration of a tuple t associated to pairs in which the first value is an integer value and the second one is a string value. The individual values within a tuple can be projected into other variables by using the project instruction. Therefore, for the tuple t exemplified above, the instruction π-ADL: A Formal Description Language for Software Architectures 7 Table 2: Constructed value types defined in π-ADLFO−CV . Type Syntactic representation Definition Tuple tuple[T1 , T2 , . . . , Tn ] Tuple (v1 , v2 , . . . , vn ) in which each vi is of type Ti View view[l1 : T1 , l2 : T2 , . . . , ln : Tn ] Labeled form of a tuple (v1 , v2 , . . . , vn ) in which each vi has a label li and is of type Ti Location location[T ] Variable of type T in which values can be stored (write) and retrieved (read) project t as a : Integer, b : String sequentially assigns the values within t to the variables a and b, which respectively receive an integer value and a string value. View. A view value can be understood as a labeled form of a tuple value. Values of a view type view[l1 : T1 , l2 : T2 , . . . , ln : Tn ] are views (l1 : v1 , l2 : v2 , . . . , ln : vn ) (n ≥ 2) in which each value vi is labeled as li and is of type Ti . For example, the declaration v is view[x : Integer, y : String] refers to the declaration of a view v associated to pairs in which the first value (x) is an integer value and the second one (y) is a string value. The individual values of a view can be projected into other variables by using the same project instruction used for tuples. Location. The location construct is used for declaring variables in order to store (write) and retrieve (read) values in these locations. For example, the instruction x is location[Integer] declares an integer variable named as x. Values stored in such a variable can be read by using its identifier (x) and values can be written to it by using assignment expressions very similar to their counterparts in programming languages. 2.2.3 Collection types The π-ADLFO−CT layer provides constructors for defining collection types by using the base and constructed value types from π-ADLFO−BV and π-ADLFO−CV . Table 3 summarizes these collection types, each one described as follows. Map. As in some programming languages, a map (a.k.a. associative array or hash table) is an unordered, non-sequential collection of pairs that are used to search for a value through a key, which works as an index that enables to access the value related to it. In π-ADL, values of a map type map[T1 , T2 ] are pairs < key, value > in which keys of type T1 are associated to values of type T2 . For example, the declaration m is map[String, Integer] π-ADL: A Formal Description Language for Software Architectures 8 Table 3: Collection types defined in π-ADLFO−CT . Type Syntactic representation Definition Map map[T1 , T2 ] Map of elements associating a key of type T1 with values of type T2 Set set[T ] Unordered collection of elements of type T with no duplicates Sequence sequence[T ] Ordered collection of elements of type T that can have duplicates refers to the declaration of a map m whose keys are string values associated to integer values. New values can be stored under keys of a map by using the add operation. For the map m exemplified above, the instruction m add [“A”, 1] stores the integer value 1 in m under the string key A. An element stored under a given key can be removed from a map by using the remove operation. For the map m exemplified above, the instruction m remove @(“A”) removes the respective element stored in m under the string key A. Set. In π-ADL, a set is an unordered collection of elements of the same type T and with no duplicates. Values within a set type set[T ] are values v1 , v2 , ..., vn in which each value vi pertaining to such a set is of type T . For example, the declaration s is set[Integer] refers to the declaration of a set s of integer values. New values can be added to a set by using the add operation. For the set s exemplified above, the instruction s add [1] adds the integer value 1 to s. A given element can be removed from a set by using the remove operation. For the set s exemplified above, the instruction s remove [1] removes the integer value 1 from s. Sequence. In π-ADL, a sequence is an ordered collection of elements of the same type T that can have duplicates. Values within a sequence type sequence[T ] are values v1 , v2 , ..., vn in which each value vi pertaining to such a sequence is of type T . For example, the declaration q is sequence[Integer] π-ADL: A Formal Description Language for Software Architectures 9 refers to the declaration of a sequence q of integer values. New values can be added to a sequence by using the add operation. For the sequence q exemplified above, the instruction q add #[1] adds the integer value 1 to q, sequentially to the already existing elements. A given element can be removed from a sequence by using the remove operation. For the sequence q exemplified above, the instruction q remove #0 removes the first integer value (indexed as 0) from q. As the elements of a sequence are disposed in a given order, an integer index the can be used to access the elements of the sequence. Therefore, the index 0 refers to the first element, 1 to second one, and so on. 2.2.4 Behavior types Besides the connection abstraction, the π-ADLB layer provides behavior constructs to represent the internal behavior of architectural elements, which make use of connections to send/receive values (see Section 2.1). Behaviors defined in π-ADL come from existing constructs provided by π-calculus, in which channels (connections in π-ADL) are used to transmit values between interacting processes (behaviors of architectural elements in π-ADL). In this perspective, considering P and Q as processes, c as a channel, and x as a value or name, πcalculus formally defines the following constructs: • c(x).P denotes an input prefix, a process waiting to read x from c; after receiving x via c, this process sequentially behaves as P; • c̄(x).P denotes an output prefix, the inverse operation of the input prefix denoting a process that waits to write x (previously accepted by some input process) in c; after sending x via c, this process sequentially behaves as P; • P | Q denotes a process composition of P and Q, which run in parallel; • P + Q denotes a process choice, thus behaving as P or Q, and; • 0 denotes an inert (nil) process, which does nothing (i.e., it has no remaining computation). Similarly to these basic π-calculus constructs, π-ADL provides the behavior types summarized in Table 4, each one described as follows. Type declarations. In π-ADL, it is possible to define an alias to an existing type (base or constructed one) within the scope of a given behavior. As an example, consider the following instructions: type Latitude is Real type Longitude is Real type GeoCoordinate is tuple[Latitude, Longitude] 10 π-ADL: A Formal Description Language for Software Architectures Table 4: Summary of the behavior types defined in π-ADLB . Behavior Syntactic representation Action Type type s is T Create an alias s for type T Output prefix via c send v Send value v via connection c Input prefix via c receive s : T Receive value s of type T via connection c Conditional prefix if b do p Enact prefix p if condition b is true Silent prefix unobservable Internal action Choice behavior choose B1 or B2 . . . or Bn Choose to execute behavior B1 or behavior B2 . . . Bn Composition behavior compose B1 and B2 . . . and Bn Execute behaviors B1 . . . Bn in parallel Inaction done Nothing to do The two first instructions create the Latitude and Longitude types1 , which are both defined by a real value (represented by the Real basic type). In turn, the third instruction creates the GeoCoordinate type as a tuple composed of values of the Latitude and Longitude types for representing a geographical coordinate. Prefixes. Behaviors enact (i.e., run) by performing actions, which are expressed by prefixes. As shown in Table 4, there are four types of prefixes, namely: (i) output prefix, which expresses the ability of sending a value v via a connection c; (ii) input prefix, which expresses the ability of receiving a value v via a connection c; (iii) conditional prefix, which expresses the ability of enacting a prefix if a given condition is true (doing nothing otherwise), and; (iv) silent prefix (expressed as unobservable), which represents an internal, invisible action. Choice behavior. In π-ADL, a choice behavior expresses the ability of a behavior to execute alternative sub-behaviors. When one of such sub-behaviors is enacted, the others are no longer available, i.e., only one of the choice sub-behaviors is executed. As an example, consider the following instructions: choose { via input receive v : Integer via output send (v+1) } or { via alt receive s : Integer db add [s] } 1 Conventionally, names, for example. alias names for types shall start with capital letters in order to differentiate them from variable π-ADL: A Formal Description Language for Software Architectures 11 This choice behavior is expressed by two alternative options: (i) receiving an integer value v via the input connection and then sending the increment of v via the output connection, or; (ii) receiving an integer value s via the alt connection and then adding it to the db set. It is noteworthy mentioning that when more than one sub-behavior is eligible to be enacted at the same time, the selection criteria for choosing the block that will be executed is nondeterministic. These criteria are not defined in the language, but at the underlying implementation of the behavior. Composition behavior. A composition behavior expresses the ability of a behavior to parallel compose sub-behaviors, each one independently proceeding from the others. In addition, the sub-behaviors within a composition behavior can interact among each other via shared connections, e.g., when a behavior sends a value to another executing behavior via an attached connection. The composition behavior of π-ADL corresponds to the concurrent constructions in π-calculus and each sub-behavior will be executed in a separate execution thread, in parallel. As an example, consider the following instructions: compose via via } and { via } { input receive v : Integer output send (v+1) signal send true In this composition behavior, an integer value v is received via the input connection and its increment is sent via the output connection, simultaneously to the sending of the Boolean value true via the signal output connection. As another example, consider the following instructions: compose { via x send v } and { via x receive y } The sub-behaviors of this composition behavior interact with each other via the connection x; the first one sends a value v which is received by the second sub-behavior as the value y. Inaction. Finally, the inaction behavior (expressed by the done keyword) represents a behavior that does not execute any action. 2.3 Specifying architectural elements in π-ADL A software architecture is described in π-ADL in terms of components, connectors, and their composition in order to form the architecture of the system. These elements are the abstractions that are part of an architecture description in π-ADL, which is composed of a set of components and connectors and exactly one architecture declaration (see Appendix A). As detailed in Appendix A, components and connectors are specified exactly in the same way except for the keyword used to specify the type of the architectural element (component or connector). These elements have an identifier as an unique name for them and they can optionally take a list of parameters (in the form name : type) as input, separated by commas and between parentheses. Moreover, they can have a sequence of the following instructions. π-ADL: A Formal Description Language for Software Architectures 12 Type declarations. Zero or more types constrained to the scope of the architectural element can be declared in the form type s is T , in which s is an identifier2 and T is an existing type (base, constructed or declared one). For example, the type declaration type Key is String creates a type named Key having the string type as the underlying type. Connection declarations. Zero or more typed connections constrained to the scope of the architectural element can be declared in the form connection c is d(T ), in which c is an identifier, d is the direction of the connection, and T is an existing type (base, constructed or declared one). Two directions are available for declaring a connection, namely in, for input connections, and out, for output connections. For example, the instructions connection x is in(String) connection y is out(Boolean) declare an input connection x for receiving string values and an output connection y for sending Boolean values. Variable declarations. Zero or more typed variables constrained to the scope of the architectural element can be declared in the form s is location[T ], in which s is an identifier and T an existing type (base, constructed or declared one). For example, the instruction a is location[Real] declares a variable a of real type. Protocol declaration. Protocols are used within the specification of architectural elements in order to enforce the value types that must be transmitted via a connection (in accordance to its declaration) and the order in which the sending/receiving operations must be performed. A protocol (whose declaration is optional) is declared as a set of connection actions, which specify the action to be performed by the connection (send or receive) and the type of the values that will be transmitted via the connection. Furthermore, protocol declarations can make use of multiplicity symbols to specify how many times the connection actions can be performed: an asterisk character (*) is used to specify that a given action (or set of actions) is performed zero or more times, whereas the plus character (+) is used to specify that a given action (or set of actions) is performed at least once. Finally, it is also possible to specify alternative actions by using the pipe character (|), so that A | B indicates that either action (or group of actions) A or action (or group of actions) B can be performed. As an example, consider the following protocol declaration: protocol is { ((via a receive Integer | via b receive Integer) via c send Integer)* } This protocol enforces receiving an integer value via connection a or via connection b and then sending an integer value via connection c. The asterisk character after the surrounding parentheses specifies that all of these actions (receiving via a or via b then sending via c) can be performed multiple times. When declaring a protocol, the specification of the multiplicity of the actions (by using the asterisk or the plus characters) is mandatory. 2 Conventionally, names, for example. alias names for types shall start with capital letters in order to differentiate them from variable π-ADL: A Formal Description Language for Software Architectures 13 Behavior declaration. Exactly one behavior must be mandatorily declared in order to specify the behavior of the architectural element. Such a behavior comprises a set of instructions, as defined in previous sections: • type declaration; • variable declaration; • collection projection; • assignment, which can be an assignment of an expression to a variable or addition/removal of elements of a collection variable; • function declaration with respective identifier, parameters, and return type; • function call, with respective identifier and parameters; • prefix (input, output, conditional or silent prefix); • choice behavior; • composition behavior; • recursive behavior call, or; • inaction. Similarly to components and connector abstractions, the specification of the architecture of the system itself is declared with its respective keyword (architecture), an unique identifier, and an optional list of parameters to be taken as input (separated by commas and between parentheses). However, such a specification is different as it comprises two basic elements, namely a set of element instances and a set of unifications. The element instances are contained within a composition behavior and are of the form i is E, in which i is the instance identifier and E is the identifier of an architectural element (component or connector)3 . In turn, the unifications are the means of passing values from an output connection of an element to an input connection of another. These unifications are of the form co ::E1 unifies ci ::E2 , in which co is an output connection of the element E1 and ci is an input connection of the element E2 . Therefore, values can be transmitted from E1 to E2 (their behaviors) via such connections. 2.4 An illustrative example In order to didactically illustrate how to make an architectural description in π-ADL, consider the simple client-server architecture depicted in Figure 3. This architecture is composed of two components (one client and one server) linked through one connector (link). The client component sends a message to the server component, which receives and processes the request. Next, the server component sends a message back to the client component, which in turn receives and processes the response. All of these interactions take place via the link connector between the client and server components. Figure 4 shows the π-ADL description of the Client component, which is declared with two connections, namely call, an output connection for sending requests (line 2), and wait, 3 If an architectural element requires input parameters (according to its declaration as an abstraction), the respective parameters must be provided when declaring an instance of such an element. 14 π-ADL: A Formal Description Language for Software Architectures call toServer fromClient request Server Link Client wait toClient fromServer reply Legend: Component Output connection (outwards) Connector Input connection (inwards) Uni cation Figure 3: Graphical representation of a simple client-server architecture. an input connection for receiving responses (line 3). The protocol of this component (lines 4 to 7) enforces sending integer values via the call connection or receiving integer values via the wait connection, operations that can be performed multiple times. Besides the declaration of the integer variable i for storing values (line 9), the behavior of the Client component encompasses a choice behavior (line 10) with two sub-behaviors available as choices. In the first option, the value of the variable i is sent via the call connection (output prefix, line 11). In the second behavior option: (i) a function called storeValue is declared as receiving an integer as input parameter and assigning it to i (lines 14 to 16); (ii) an integer r is received via the wait connection (input prefix, line 17), and; (iii) the storeValue function is called passing such a received value as input, in order to store it internally to the component (line 18). After executing their respective instructions, each sub-behavior recurses (what is expressed by the behavior call behavior() in lines 12 and 19), i.e., it continues being executed. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: component Client is abstraction() { connection call is out(Integer) connection wait is in(Integer) protocol is { (via call send Integer | via wait receive Integer)* } behavior is { i is location[Integer] choose { via call send i behavior() } or { storeValue is function(v : Integer) { i = v } via wait receive r : Integer storeValue(r) behavior() } } } Figure 4: π-ADL description of the Client component. Similarly, Figure 5 shows the π-ADL description of the Server component, which is declared with two connections, namely request, an input connection for receiving requests (line 2), and reply, an output connection for sending responses (line 3). The protocol of this π-ADL: A Formal Description Language for Software Architectures 15 component (lines 4 to 7) enforces receiving an integer value via the request connection and then sending an integer value via the reply connection, so that these actions are sequentially performed and they can be performed multiple times. Next, the behavior of the Server component encompasses: (i) the declaration of the increment function, which receives an integer as input parameter and returns its increment by 1 (lines 9 to 11); (ii) receiving an integer i via the request connection (line 12), and; (iii) sending the result of the call to the increment function (with i as input parameter) via the reply connection (line 13). After executing these respective instructions, the behavior continues being executed (behavior call, line 14). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: component Server is abstraction() { connection request is in(Integer) connection reply is out(Integer) protocol is { (via request receive Integer via reply send Integer)* } behavior is { increment is function(v : Integer) : Integer { return (v+1) } via request receive i : Integer via reply send increment(i) behavior() } } Figure 5: π-ADL description of the Server component. Figure 6 shows the π-ADL description of the Link connector, which is declared with four connections: (i) fromClient, an input connection for receiving requests from the client (line 2); (ii) toServer, an output connection for sending requests to the server (line 3); (iii) fromServer, an input connection for receiving responses from the server (line 4), and; (iv) toClient, an output connection for sending responses to the client (line 5). The protocol of this connector specifies two alternative groups of actions: (i) receiving an integer value via the fromClient connection and then sending an integer value via the toServer connection (lines 7 and 8), or; (ii) receiving an integer value via the fromServer connection and then sending an integer value via the toClient connection (lines 9 and 10). The behavior of this connector comprises a choice behavior (line 12) with two sub-behaviors available as choices. In the first option, an integer value x is received via the fromClient connection and sent via the toServer connection (lines 14 and 15), thus indicating a message flow from the client to the server; in turn, the second option specifies that an integer value y is received via the fromServer connection and is sent via the toClient connection (lines 18 and 19), thus indicating a message flow from the server to the client. After executing their respective instructions, each sub-behavior recurses (lines 16 and 20). Finally, Figure 7 shows the π-ADL description of the ClientServer architecture. This architecture is specified as a composition with one instance of the Client component (line 4), one instance of the Link connector (line 5), and one instance of the Server component (line 6). The attachments of these architectural elements take place through four unifications: (i) the call output connection of the component c is bound to the fromClient input connection of the l connector (line 8), thus representing a message flow from the client to the link; π-ADL: A Formal Description Language for Software Architectures 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 16 connector Link is abstraction() { connection fromClient is in(Integer) connection toServer is out(Integer) connection fromServer is in(Integer) connection toClient is out(Integer) protocol is { ((via fromClient receive Integer via toServer send Integer) | (via fromServer receive Integer via toClient send Integer))* } behavior is { choose { via fromClient receive x : Integer via toServer send x behavior() } or { via fromServer receive y : Integer via toClient send y behavior() } } } Figure 6: π-ADL description of the Link connector. (ii) the toServer output connection of the connector l is bound to the request input connection of the s component (line 9), thus representing a message flow from the link to the server; (iii) the reply output connection of the component s is bound to the fromServer input connection of the l connector (line 10), thus representing a message flow from the server to the link, and; (iv) the toClient output connection of the connector l is bound to the wait input connection of the c component (line 11), thus representing a message flow from the link to the client. Therefore, these unifications between connections of the architectural elements enable to transmit data from client to server and vice-versa via the connector. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: architecture ClientServer is abstraction() { behavior is { compose { c is Client() and l is Link() and s is Server() } where { c::call unifies l::fromClient l::toServer unifies s::request s::reply unifies l::fromServer l::toClient unifies c::wait } } } Figure 7: π-ADL description of the ClientServer architecture. π-ADL: A Formal Description Language for Software Architectures 3 17 Tool support for π-ADL architecture descriptions One of the concerns regarding the usefulness of an ADL is directly related to the tools that it provides for supporting the activities encompassed by an architecture-driven software development approach, i.e., architectural description, analysis, code generation, and evolution [34, 42]. In fact, Dashofy et al. [21] point out that tool support is especially vital for the successful use of any ADL since architecture descriptions persist throughout the development lifecycle and evolve along with the described software system, so that ADL tools play an important role for creating, analyzing, and maintaining these documents over time. Furthermore, a recent survey about ADLs in the industry context [33] has revealed the importance of ADL tools in this scenario, although few description languages for software architectures are supported by satisfactory tools. Among the interesting findings, some requirements for such tools were identified in this survey, such as provisioning both textual and graphical comprehensive notations, aligning software architecture descriptions with their respective implementation, supporting architectural analysis, meaningful communication/documentation, simplicity, intuitiveness, and usability. In the context of the ArchWare European Project (in which the π-ADL language was initially conceived), an open-source toolset was developed in order to support formal description, analysis, refinement, code generation, and evolution of software architectures [40, 42]. All together, these tools enable the compilation of architectural models into their executable representations as well as the formal analysis and evolution of such models. However, by taking into account evolutions in the π-ADL language itself, as well as requirements of new generation software systems (such as large-scale, distribution, concurrency, and dynamicity), a new tool support for π-ADL started to be developed. In a first step, a textual editor based on the Eclipse platform [2] was developed for assisting architects in software architecture descriptions by using the π-ADL language. Furthermore, such a new tool support enables to automatically generate implementation code in Go [7], a general-purpose programming language that has shown to be suited for addressing the construction of scalable distributed systems. This section presents the developed π-ADL textual editor (Sections 3.1) and the facilities for generating implementation code in the Go programming language (Section 3.2), publicly available for download in [6]. 3.1 3.1.1 The π-ADL textual editor Background The choice of using an Eclipse-based development environment to construct the new πADL tools was motivated by its widespread use for developing software and good support in terms of open-source tools and frameworks. Moreover, as Eclipse is based on the Java programming language, it takes advantage of the portability capabilities provided by such a language, thus enabling it to be used in multiple operating systems and hardware platforms. Among the most relevant Eclipse frameworks, the Eclipse Modeling Framework (EMF) [3] is an open-source framework used for model-driven software development and it has been the cornerstone of related technologies and other frameworks. EMF provides facilities for generating code and building tools and applications based on structured models. The typical workflow for these tasks by using the EMF facilities encompasses the construction of the models, code generation and customization, and the implementation of the application itself. Therefore, EMF can be seen as the the middle ground between abstract models and concrete programming artifacts. π-ADL: A Formal Description Language for Software Architectures 18 The (meta)model used for representing models in EMF is Ecore. In an Ecore model, classes are model entities with attributes (each one with a name and a data type) and relationships (references) among each other. From these elements, EMF enables to use instances of the classes defined in Ecore for describing a model of the system and then generating its respective code. In this perspective, all frameworks, tools, and applications built upon EMF have an underlying model based on the Ecore (meta)model. In the last years, EMF has been the basis for the construction of several tools and frameworks for developing software. Among them, Xtext [10] is an open-source, highly customizable, sophisticated framework for developing domain-specific languages (DSLs), i.e., programming or specification languages that target a specific domain problem or purpose. Xtext covers all aspects of a complete language structure by encompassing parsing the textual model written in such a language, possibly interpreting it and/or generating code from it in another language. Moreover, this infrastructure is fully integrated with the Eclipse environment, thus taking advantage of typical, useful IDE features such as editor with syntax highlighting, code completion, error markers, automatic building infrastructure, etc. Therefore, as π-ADL can be broadly understood as a DSL designed to describe software architectures, it can take advantage of the facilities provided by Xtext to develop an infrastructure for assisting software architects in the use of the language, as further described in next sections. 3.1.2 Xtext-based textual editor for π-ADL The π-ADL grammar. The main artifact used as input by Xtext for generating the π-ADL infrastructure is a grammar specification in the Extended Backus-Naur Form (EBNF) [29]. This grammar is a set of production rules (or simply rules) that describe the form of the elements that are valid according to the language syntax. When compiling this grammar specification, Xtext generates the respective Ecore metamodel and a Java class corresponding to each production rule. Figure 8 shows an excerpt of the π-ADL grammar4 that was specified based on the π-ADL EBNF production rules described in Appendix A. Rules start with their respective name followed by a colon and end with a semicolon. In Figure 8, line 1 indicates that the current grammar reuses an Xtext grammar with common terminal symbols (org.eclipse.xtext.common.Terminals), such as identifiers (ID) and integer numbers. The ArchitectureDescription rule (lines 5 to 8) corresponds to an architecture description itself, which is composed of a set with zero or more architectural elements (represented by the ArchitecturalElement rule) and exactly one architecture declaration (represented by the Architecture rule). In turn, the ArchitecturalElement rule (lines 10 to 12) define that an architectural element can be either a component or a connector, respectively defined by the Component and Connector rules. Finally, the syntax of the specification of a component is defined by the Component rule (lines 14 to 24). A component is declared by using the component keyword and it comprises the name attribute, which is represented by the ID terminal defined in the Xtext terminals grammar. Optionally, it can also take a list of parameters (each one defined by the Parameter rule and separated by commas) as input, between parentheses. Within the definition of this architectural element (delimited by braces), one can have in sequence: • declaration of zero or more types defined by the TypeDeclaration rule (line 18); • declaration of zero or more connections defined by the ConnectionDeclaration rule (line 19); 4 The complete π-ADL grammar specification is publicly available in [6]. π-ADL: A Formal Description Language for Software Architectures 19 • declaration of zero or more variables defined by the VariableDeclaration rule (line 20); • optional declaration of a protocol defined by the ProtocolDeclaration rule (line 21), and; • declaration of exactly one behavior defined by the BehaviorDeclaration rule (line 22). 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: grammar fr.irisa.archware.PiADL with org.eclipse.xtext.common.Terminals generate piADL “http://www.irisa.fr/archware/PiADL” ArchitectureDescription: archElements+=ArchitecturalElement* architecture=Architecture ; ArchitecturalElement: Component | Connector ; Component: ‘component’ name=ID ‘is’ (‘abstraction()’ | ‘abstraction(’ (parameters+=Parameter (‘, ’ parameters+=Parameter)*)? ‘)’) ‘{’ typeDecl+=TypeDeclaration* connections+=ConnectionDeclaration* varDecl+=VariableDeclaration* protDecl=ProtocolDeclaration? behavior=BehaviorDeclaration ‘}’ ; Figure 8: Excerpt of the π-ADL grammar specified in the Xtext framework. Figure 9 shows a partial diagram of the π-ADL metamodel representing the elements syntactically defined in Figure 8. This Ecore-based metamodel is automatically generated from the grammar specification5 . It is interesting to highlight how models are related to each other in this context. An architecture description in π-ADL is a model of a concrete software architecture. This model conforms to the π-ADL metamodel (Figure 9), so that each element that composes the structure of the described architecture is an instance of the abstract entities (classes) defined in such a metamodel. In turn, the π-ADL metamodel itself conforms to the Ecore metamodel, i.e., Ecore is hereby a metametamodel (metamodel of a metamodel). Therefore, each class defined in the π-ADL metamodel is an instance of the abstract entities (EClasses) defined in the Ecore metamodel. 5 Although it is possible to generate an Ecore metamodel of the language from its grammar specification, some people do not regard this practice as a good one since metamodels are at a higher abstraction level than the concrete syntax model. In the case of π-ADL, it was opted starting from the grammar specification and then generating the metamodel because such a specification was almost complete from previous versions of the language, so that it was reused. π-ADL: A Formal Description Language for Software Architectures 20 Figure 9: Partial diagram of the generated π-ADL Ecore-based metamodel. Automatic generation of the π-ADL infrastructure. In order to generate the π-ADL language infrastructure, Xtext uses a script written in the Modeling Workflow Engine (MWE2) DSL to configure the generation of its artifacts. In particular, this script (also automatically created by Xtext) is used by the framework to derive a specification from the grammar that is compatible with the parser generator that will be used for generating the parser of the language. When running it, Xtext automatically generates the following artifacts depicted in Figure 10: (i) a Java implementation of a parser, which is automatically generated by the ANTRL parser generator [1] and will be responsible for the syntactic analysis of the architecture textual description; (ii) an Ecore metamodel with the abstract entities of the language and the relationships among them; (iii) a code generator, which is used to generate code from the architecture textual description, and; (iv) an Eclipse-based code editor for assisting the textual description. In addition, Xtext creates an abstract syntax tree (AST) from the input textual model for representing the structure of the parsed model, as well as it generates the respective Java classes to persist such an AST. It is noteworthy mentioning that although the Java programming language can be used for customizing the generated artifacts, Xtext fosters the use of Xtend [9], a fully Java-interoperable programming language that features a more compact, easier to use syntax, as well as advanced features such as type inference and lambda expressions. As the AST model will need to be continuously traversed throughout the implementation of a DSL, Xtend provides useful mechanisms for straightforwardly doing this while being easy to use and enabling to write better readable code. Moreover, as Xtend programs are translated into Java code, Xtend code can access all of the Java libraries, thus enabling these languages to seamlessly coexist. 21 π-ADL: A Formal Description Language for Software Architectures Language project Ecore metamodel (.ecore) p-ADL Grammar Xtext generator (.xtext) (.mwe2) ANTRL-based parser (.java) Legend: manual, required generated, required generated, optional Code generator (.xtend) Validator (.xtend) Unit test Editor Testing classes (.java) Navigation Outline Code completion Syntax highlighting Error marking Automatic building Testing project Editor (UI) project Figure 10: Artifacts generated by Xtext from the π-ADL grammar specification. Validations. Once the architecture description is checked as correct by the parser, it might still have errors as its overall correctness cannot always be determined during the parsing procedure, i.e., a semantic analysis must be performed over the current model. For this purpose, Xtext provides means of constructing validators. In the Xtext framework, validators are classes that contain methods (validation rules) implementing additional constraint checks over the abstract elements of the current model. Xtext provides some default validators (e.g., for checking that two entities have the same name), but it enables developers to implement custom validators by extending the base validation classes that come with the framework. Xtext performs validation by invoking each Xtend method annotated with the @Check directive and passing all instances that have a compatible runtime type to each method. In the body of such methods, the semantic checks are implemented for the element passed as parameter. If a semantic check fails, it is possible to trigger warning or error messages that will appear in the textual editor while making the architecture description. Table 5 summarizes the main error/warning conditions checked by the implemented validation methods. Interpreting expressions. In order to describe the behavior of components and connectors, a software architect can make use of expressions that are similar to the ones used in programming languages, such as logical, relational, equality, and arithmetic expressions. Determining the data types handled by such expressions is important for ensuring that an expression value sent via a connection is the one expected, i.e., its type is equal to the type specified when declaring such a connection. However, type checking cannot be performed during parsing (thus requiring a semantic analysis) and expressions are resolved at runtime, i.e., their value is calculated while they are described. As Xtext is mainly concerned with syntactic analysis and does not support expression resolution, an interpreter and a validator were developed for handling expressions in π-ADL architecture descriptions. As the first step, a type provider implemented as an Xtend class was developed aiming at providing the type of a given expression. This class contains a set of typeOf methods that 22 π-ADL: A Formal Description Language for Software Architectures Table 5: Error/warning conditions checked by the implemented validation methods. Target element Warning/error condition Severity Architecture description Architecture name is equal to the name of a component or connector Warning Architectural element Architectural element with no specified protocol Warning Architectural element Inconsistencies between connection declarations and protocol specifications (data type and/or direction) Error Architectural element Protocol with undeclared connections Error Architectural element Empty behavior Warning Architectural element Names of declared types starting lower cased Warning Architectural element Inconsistencies between prefixes within behaviors and connection declarations (data type and/or direction) Error Assignment Assignment to undeclared variable Error Function call Call to undeclared functions Error Function declaration Function declared with a return type and with no return statement in its body Warning Architecture Architectural element instances with equal names Error Architecture Instance of undeclared architectural element Error Architecture Architecture with no unifications between architectural elements Warning Architecture Connection elements in unification are both components or connectors Error Architecture Unification is not from an output connection to an input connection Error Architecture Unification must encompass connections of the same data type Error Architecture Unification accessing undeclared connection Error return the type of a given expression received as input. Four basic situations are possible when determining the type of an expression: (i) when the type of the expression does not depend upon the types of its sub-expressions – negation, logical, equality, and relational expressions will always return a Boolean value, whereas atomic values have their types directly determined by their literals; π-ADL: A Formal Description Language for Software Architectures 23 (ii) when the type of the expression depends upon the operation and its operands – in this case, other situations are possible: • the division operation will always return a real value, independently of the operands; • the multiplication, modulus, and subtraction operations will return a real value if one of the operands is of this type or an integer value otherwise; • the sum operation will return a string value if one of the operands is a string, a real value if one of them is real, or an integer value otherwise. (iii) when the expression is an assignment expression – the type of the expression is determined by the declared type of the variable to which the value will be assigned; (iv) when the expression is a function call – the type of the expression is determined by the return type specified when declaring the function. Next, the expression interpreter of π-ADL was implemented as another auxiliary Xtend class. This interpreter contains a method called interpret that receives an expression as input and determines its type by using the typeOf methods implemented in the type provider class. Therefore, after identifying the atomic values of the operands and converting them to conventional Java primitive types, the interpretation method performs the respective operations given by the operands, similarly to what is done when evaluating an expression by using a typical programming language. Finally, the expression validator of π-ADL was implemented as another Xtend class in order to perform the semantic type checking for expressions. The methods of this class were annotated with the @Check directive and then called at runtime in conjunction with the validation methods that check the conditions shown in Table 5. Table 6 summarizes the main error conditions checked by the implemented expression validation methods. All of these methods trigger error messages when their respective semantic checks fail. Features of the π-ADL text editor. Some interesting features provided by Xtext to the generated π-ADL text editor (see Figure 11) are: • error and warning alerts while describing the architecture, an useful feature that enables architects to early detect and fix errors and potential problems on such a description, thus saving time and mental effort to correct them; • syntax highlighting, which is useful for distinguishing keywords (reserved words) of the language from identifiers that are allowed to be used, for example; • content assist (accessed with the Ctrl + Space bar keyboard shortcut), which provides suggestions on how to complete a given statement/expression based on the syntactic rules, and; • automatic build on save, which enables to automatically generate code from the architecture description (if it is correct according to the syntactic and validation rules) when it is saved in the language editor. π-ADL: A Formal Description Language for Software Architectures 24 Table 6: Error conditions checked by the implemented expression validation methods. Target expression Error condition Negation expression Operand is not a Boolean value Logical expression Both operands are not Boolean values Equality expression Both operands are not Boolean values Relational expression Both operands are not of the same type or they are Boolean values Multiplication, division, and modulus operations Both operands are not numeric values Minus operation Both operands are not numeric values Sum operation Both operands are neither numeric nor string values Variable assignment Type of the value to be assigned is not equal to the type of the variable (defined when declaring it) Conditional prefix Type of the guard is not Boolean Conditional statement (if/then/else) Type of the guard is not Boolean While loop Type of the condition is not Boolean For loop Type of the stop condition is not Boolean 3.2 Generation of implementation code from architecture descriptions According to Medvidovic and Taylor [34], the ultimate goal of any software design and modeling endeavor is to produce the executable system. An elegant architectural model is of limited value unless it can be converted into a running application, and doing this manually might result in many problems in terms of consistency and traceability between an architecture and its implementation. Therefore, the provision of ADL tools able to assist the production of source code is highly desirable, if not imperative. Moreover, the automatic instantiation of a software system from its respective architecture description fosters the management of the transition from system design to implementation [19]. Georgas et al. [23] point out that a good, well-analyzed architectural design is useless unless there is a clear mapping between this architecture and its implementation. The less complete or automated this mapping, the more opportunity there is for an architectural drift, a phenomenon in which the system’s implementation gradually diverges from its intended design. Furthermore, as software architectures are typically defined independently from implementation, most existing ADLs are disconnected from the runtime level, thus entailing mismatches and inconsistencies between architecture and implementation mainly as the architecture evolves. Specifically with respect to the π-ADL description language, the π-ADL.NET Project [44] was proposed few years ago as the result of the integration of π-ADL with the Microsoft .NET Framework [5]. In π-ADL.NET, formal architecture descriptions in π-ADL are compiled to CIL (Common Intermediate Language), thus resulting in a code that is able to access the existing resources provided by the .NET platform. By enabling the execution of the architecture description, π-ADL.NET supports runtime analysis of the concrete architecture and it π-ADL: A Formal Description Language for Software Architectures 25 Figure 11: Screenshot of the Eclipse-based π-ADL textual editor. seeks to preserve architectural integrity of the system at the implementation level. However, despite its intention of bringing a formally founded ADL to an implementation platform, the main limitation of π-ADL.NET that makes it not well suited for new generation software systems regards the lack of counterparts when performing the mappings from π-ADL to CIL or the .NET platform. For instance, in π-ADL, behaviors and abstractions communicate through connections, which have no corresponding elements in CIL, so that a .NET class was developed by hand to emulate π-ADL connections, with requisite threading and synchronization functionality. Furthermore, π-ADL.NET also lacks of support for distribution, thus becoming a constraint when implementing distributed systems, a typical feature of the today’s software systems. Considering these limitations faced by π-ADL.NET, a new support for generating implementation code from π-ADL architecture descriptions had to be provided. In this context, Go [7] was chosen as the target programming language because it is an easy general-purpose language designed to address the construction of scalable distributed systems and handle multicore and networked computer architectures, as required by new generation software systems. Furthermore, the integration of π-ADL with Go is mainly fostered by their common basis on the π-calculus process algebra and the straightforward relationship between elements of the languages, such as the use of connections in π-ADL and channels in Go as means of communication between concurrent processes. This section addresses how generating source code in the Go programming language from π-ADL architecture descriptions. Section 3.2.1 presents a brief overview about the Go programming language. In turn, Section 3.2.2 defines the correspondences between the elements of π-ADL to Go, whereas Section 3.2.3 presents the technical elements used to perform such a π-ADL: A Formal Description Language for Software Architectures 26 mapping between the languages, as introduced in [16]. 3.2.1 The Go programming language Go is a new general-purpose language that was launched as an internal project at Google, Inc. in 2007 and became a public open-source project on November 2009. In 2012, Go was stably released as Go 1 by including a language specification [8], standard libraries and custom tools, and it is currently in version 1.3, which was released by mid-2014. In the last years, Go has been used by Google and a variety of commercial and noncommercial organizations. It is also integrated to the Google App Engine [4], the Google’s cloud-based development platform. Go was designed to address the construction of new generation large-scale software systems, which are to be efficient, dynamic, and deployed on multicore and networked computer architectures. In order to achieve these purposes, the language aims to combine the lightweight, ease of use, and expressiveness of interpreted and dynamically typed languages, such as JavaScript and Python, with the efficiency and safety of traditional statically typed, compiled languages such as Java. Moreover, it is possible to directly compile even a large Go program to native code in few seconds. Such a lightning fast compilation is made possible to a small extent because the language is easy to parse, but mostly due to its dependence management. For example, consider a source code A depending on another source code B, which in turn depends upon a third source code C. In a traditional compiled language, A would require the object files generated from both B and C. However, in Go, everything that C exports is cached in the object file generated from B, so that just the object file generated from B is sufficient to build the source code A. This management results in considerable speedups for large software systems with several dependencies. One of the main features of Go is its lightweight support for concurrent communication and execution through high-level operations, in contrast to the considerable effort required to develop, maintain, and debug concurrent programs in mainstream languages such as C++ and Java. In this perspective, the solution provided by Go is threefold. First, the high-level support for concurrent programming enables programmers to easily develop concurrent programs. Second, concurrent processing is performed through goroutines, which are lightweight processes (similar to threads, but lighter), which can be created and automatically load-balanced across the available processors and cores. Finally, the automatic and efficient garbage collection relieves programmers of the memory management typically required by concurrent programs. In Go, goroutines communicate by using typed channels, which are used as means for sending and receiving values of any type. When a channel communication takes place, the sending and/or receiving channels (and their respective goroutines) are synchronized at the moment of the communication [25]. Therefore, explicit locking and other low-level details are abstracted away, thus simplifying the development of concurrent programs. Furthermore, due to its theoretical foundations on π-calculus, Go also supports the mobility of channels, i.e., channels are seen as first-class objects that can be transported via other channels. For sake of space, next sections introduce just some elements of Go used in the implementation code generated from architecture descriptions in π-ADL. The interested reader is invited to refer to [8, 13, 17, 47] in order to check the complete specification of the language, its main elements, and details about its syntax. 3.2.2 Correspondences between π-ADL and Go Table 7 summarizes the relationships between the architectural elements of π-ADL and the elements of Go. Next paragraphs detail only the main correspondences between these lan- π-ADL: A Formal Description Language for Software Architectures 27 guages as some of them (e.g., conditional statements, control loops, expressions, etc.) are identical and then straightforward. Table 7: Summary of the correspondences between architectural elements of π-ADL and elements of Go. π-ADL Component Connector Behavior Connection Architecture Declaration of connections Unification of connections Go Function (goroutine) Function (goroutine) Body of function (goroutine) Channel Main function Maps of channels Channels as parameters to goroutines Components, connectors, and their behavior. In a π-ADL architecture description, components and connectors are created as abstractions that can be instantiated within the specification of the architecture. In Go, components and connectors are represented as functions that will be called as goroutines, thus being equivalent to the notion of communicating processes in πcalculus. Such functions are signed with the respective names of the components and connectors that they represent and the body of these functions comprises the behavior of such architectural elements. Connections. As introduced in Section 2.1, one of the main elements of the π-calculus process algebra are channels, which are used as means of communication and synchronization between concurrent processes. In π-ADL, connections are used to send and/or receive values between architectural abstractions (components and connectors) and their behaviors. Similarly (and then straightforwardly to π-calculus), the typed channels in Go are used to send and/or receive values between processes (goroutines to be synchronized), so that connections in π-ADL are mapped to channels in Go. The data type of the values transmitted through a channel is the one specified in the declaration of the connection. Declaration of connections. In the main function, maps of channels are created in order to represent the set of connections associated to a component or connector. These <string, channel> maps use as keys the names of the connections declared in the architecture description and such keys map to the respective channel object representing the connection. These maps are used in order to enable rastreability of the connection names when performing the translation from the architectural description π-ADL to the respective code in Go. As an example, consider the following excerpts of the π-ADL description of the Client and Server components and of the Link connector (see Figures 4 to 7) (left), and the respective Go code regarding these elements (right). The instantiation of the Client and Server components within the ClientServer architecture is codified in Go as the creation of a map of channels representing the set of connections of these architectural elements. For example, the map representing the instance of the Client component (c) is created with two integer typed channels and using the names of the connections (call and wait) as keys of this map. The same applies to the instance of the Server component (s) and to the instance of the Link connector (l). π-ADL: A Formal Description Language for Software Architectures component Client is abstraction() { connection call is out(Integer) connection wait is in(Integer) // ... } connector Link is abstraction() { connection fromClient is in(Integer) connection toServer is out(Integer) connection fromServer is in(Integer) connection toClient is out(Integer) // ... } component Server is abstraction() { connection request is in(Integer) connection reply is out(Integer) // ... } architecture ClientServer is abstraction() { behavior is { compose { c is Client() and l is Link() and s is Server() } 28 func main() { c := map[string]interface{}{ “call” : make(chan int64), “wait” : make(chan int64), } l := map[string]interface{}{ “fromClient” : make(chan int64), “toServer” : make(chan int64), “fromServer” : make(chan int64), “toClient” : make(chan int64), } s := map[string]interface{}{ “request” : make(chan int64), “reply” : make(chan int64), } } Unification of connections. In π-ADL, a connection of a component can be attached to a connection of a connector in order to enable these elements to communicate. In Go, this unification process takes place by: (i) passing the channels regarding the connections to be unified as parameters of the functions (goroutines) that represent behaviors of components and connectors, and then; (ii) passing the contents of the sending channel (output connection) to the receiving channel (input connection). As an example, consider the following excerpt of Go code generated from the π-ADL description of the Server component (see Figure 5): 1: 2: 3: 4: 5: 6: 7: func Server(conn map[string]interface{}, fromc, toc interface{}) { go func(fromc, toc interface{}) { if (fromc != nil && toc != nil) { v, _ := reflect.ValueOf(fromc).Recv() reflect.ValueOf(toc).Send(v) } }(fromc, toc) In line 1, the Server function receives as parameters (i) a map of channels (conn) representing the connections declared in the Server component and (ii) the channels representing connections to be unified with this component (fromc and toc). Next, an internal, anonymous goroutine is called in order to perform the unification process itself by receiving the value contained into the fromc output connection (line 4) and then sending this value via the toc input connection (line 5). Therefore, the following call to Server goroutine in the main function go Server(s, l[“toServer”], s[“request”]) takes as parameters (i) the map of channels regarding the Server instance (s), (ii) the output channel of the l connector (toServer), and (iii) the input channel of the s component (request), thus meaning that data are being trasmitted from the Link connector to the Server component. This data transmission is effectively performed by the internal goroutine within the Server function. Architecture. The main element of an architectural description is the architecture itself. In π-ADL, an architecture is specified as a composition of component and connector instances. In π-ADL: A Formal Description Language for Software Architectures 29 Go, it is represented by the main function (func main), which stands for the entry-point of a Go program (thus being the first function called when the program executes) and has no parameters and type. In order to create the instances of components and connectors, the goroutines that represent these architectural elements are called within the main function. As already mentioned, three parameters are provided in such calls, namely the respective map of channels that represent the connections of the component/connector and the output/input channels associated to the connections that will be unified to this component/connector. It is important to highlight that when any input connection of a component/connector is not being unified with any output connection of another component/connector, a nil unification takes place, i.e., null values are passed as second and third parameters in the goroutine call. For instance, the following goroutine call in the main function go A(c, nil, nil) means that an input connection of the A element (third parameter) is not being unified with any output connection of another element (second parameter). As the values of these parameters are null, the internal goroutine within the A function (responsible for the unification operation itself) is not executed since it checks whether both channels given as parameters are not null. Basic, constructed, and collection types. As presented in Sections 2.2.1 to 2.2.3, π-ADL provides three data types: (i) basic types, which are used to express atomic values; (ii) constructed types, which are composite types constructed upon the basic types, and; (iii) collection types, which are types representing collections based on the previous basic and constructed value types. Table 8 summarizes the mappings from these types defined in π-ADL to data types in Go. As also introduced in Section 2.2.1, π-ADL also provides a special basic type named Any, which works as a generic type in the language, thus admitting values of any type. For similar purposes, this type is mapped to empty interfaces (interface{}), which are means of generic typing in Go. As empty interfaces do not have defined methods, any type is able to satisfy these interfaces. Behavior types. Table 9 summarizes the mappings from the behavior types defined in π-ADL (see Section 2.2.4) to Go. 3.2.3 Code generation procedure As described in Section 3.1, the Xtext framework uses the π-ADL grammar specification to automatically generate the entry point for implementing a code generator. The implementation of this code generation consists of an Xtend class whose methods are used for translating the abstract elements defined in the π-ADL metamodel to their respective implementation. Therefore, each method in the code generator class receives an abstract element of the language (defined in the abstract syntax tree and according to the π-ADL Ecore-based metamodel) and generates the respective Go instruction(s) regarding this element based on the correspondences defined in Section 3.2.2. The code generator Xtend class is publicly available for download in [6]. Figure 12 depicts the technical elements involved in the generation of source code in the Go programming language from architecture descriptions in π-ADL. A π-ADL architecture description is syntactically verified against its grammar specification and it serves as input to 30 π-ADL: A Formal Description Language for Software Architectures Table 8: Summary of the mappings from data types defined in π-ADL to types in Go. Type π-ADL Syntax representation in Go Semantics in Go Basic types Natural Integer Real Boolean String uint64 int64 float64 bool string Unsigned integer numbers) Signed integer numbers Floating-point numbers Boolean logical values Character strings) Tuple [x]interface{} Empty interface array of length n (n is the number of composing types) View map[string]interface{} Map whose keys are the labels for the values Location var x T Variable declaration (x is the variable name and T the variable type) Map map[T1 ]T2 Map with keys of type T1 and values of type T2 Set map[T ]bool Map with keys of type T of the set Constructed types Collection types Sequence []T Slice (dynamic array) with elements of type T the code generator. In turn, the code generator makes use of the implemented validators in order to semantically check the π-ADL architecture description. Therefore, if such an architecture description is correct according to the syntactic and validation rules of the language, then the respective source code in Go is automatically generated with the automatic build capability provided by the π-ADL textual editor. 4 Application: A flood monitoring system Wireless sensor networks (WSNs) are composed of motes, which are tiny hardware/software platforms equipped with an embedded CPU, low power wireless networking capabilities, and simple sensors [48]. Among the large number of real-world applications in which WSNs have been increasingly employed, an interesting and promising scenario is the flood monitoring in urban areas. During rainy seasons, floods are challenging to urban centers traversed by large rivers due to material, human, and economic losses in flooded areas. In order to minimize such problems, a flood monitoring system can support the monitoring of urban rivers and create alert messages to notify authorities and citizens about the risks in case of an imminent flood. Successful examples of WSN-based flood monitoring systems are the ones used to monitor the River Ribble near Preston, United Kingdom [27], and the one used to monitor the River Monjolinho in São Carlos, Brazil [28, 48]. These systems are mainly composed of mul- 31 π-ADL: A Formal Description Language for Software Architectures Table 9: Summary of the mappings from behavior types defined in π-ADL to Go. π-ADL behavior type Syntax representation in Go Semantics in Go Type type s T Declaration of a type s as an alias for type T Output prefix conn[“c”].(chan T ) <- v Send value v of type T via channel (connection) c Input prefix s := <- conn[“c”].(chan T ) Receive value s of type T from channel (connection) c Silent prefix // Unobservable Comment Choice behavior Selection of a block (corresponding to a sub-behavior Bi ) to execute based on receiving/sending operations over channels (pi ) select { case p1 : B1 case p2 : B2 } Composition behavior go func() { B }() Inaction return Creation and invocation of a goroutine for each sub-behavior B Empty return is input of generates generates Code generator uses is veri ed with p-ADL architectural description Go source code generates p-ADL grammar Xtext MWE2 generator generates p-ADL validator uses uses uses p-ADL metamodel conforms to uses Expression validator uses conforms to Expression interpreter Ecore metametamodel Figure 12: Elements for generating source code in Go from π-ADL architecture descriptions. tiple motes, which are spread in the proximities of the river and monitor the water level as a flood risk indicator, and a gateway station, which analyzes such data and then triggers alerts when a flood condition is detected. As these motes typically use pressure sensors, measured raw data need to undergo some processing in order to provide the height reached by the water level (centimeters of water). Moreover, depending on the monitored river area, sensed data are π-ADL: A Formal Description Language for Software Architectures 32 transmitted in a multihop communication. As a sensor node is typically a resource-constrained device in terms of power and networking capabilities, the gateway station responsible for receiving data provided by such a sensor may be far and then out of its network coverage area. Therefore, data sensed by some motes in their respective sites are successively sent to neighbor sensors that will forward such data to other neighbor sensors until reaching the gateway station, which will process them. The communications among these elements can take place by using wireless network connections such as GPRS, IEEE 802.15.4 (ZigBee), Bluetooth, etc. Figure 13 illustrates this flood monitoring system scenario. Figure 13: Flood monitoring system scenario. Figure 14 depicts a simplified architecture of a WSN-based flood monitoring system that is composed of three sensor nodes (S1, S2 and S3), one gateway component (G), and three connectors linking these components (L1, L2 and L3). In this architecture, water level data are measured by sensors S1 and S2 and sent to sensor S3 by using the links L1 and L2. In turn, sensor S3 receive these data and forward them (without any additional processing) to the gateway G via the link L3. Next paragraphs detail the π-ADL specification of these architectural elements and their generated implementation in Go. Sensor component. Figure 15(a) shows the specification of the Sensor component in πADL, which is composed of three connections: (i) the sense input connection is used for receiving raw data measured by the sensor; (ii) the pass input connection is used for receiving data from a neighbor sensor, and; (iii) the measure output connection is used for sending data. The behavior of this component encompasses the definition of the convertRawData function, which is responsible for preprocessing sensed raw data by making a unit conversion from millivolts (as measured by the pressure sensors) to centimeters of water, types declared as ha- 33 π-ADL: A Formal Description Language for Software Architectures sense pass S1 from measure L1 to sense pass S3 pass S2 from measure L2 from L3 measure to to data sense G Legend: Component Output connection (outwards) Connector Input connection (inwards) alert Uni cation Figure 14: Architecture of the WSN-based flood monitoring system. ving the Real basic type defined in π-ADL as underlying type. As the implementation of the convertRawData function may change according to the sensor specifications provided by the respective manufacturers, it was set as unobservable in this example. Furthermore, such a behavior can proceed through two alternative options, as specified by the choice behavior (choose): (i) data received via the sense input connection are processed by the convertRawData function and then sent via the measure output connection, or; (ii) data received via the pass input connection are directly sent (i.e., without any processing) via the measure output connection. As partially shown in Figure 15(b), this component is implemented in Go by the Sensor function, which receives as parameters the map of channels representing the set of named connections of this component (conn) and the channels representing the connections to be unified when calling this goroutine within the main function. The Sensor function also comprises the declaration of a local function (closure) corresponding to the convertRawData function specified in the behavior of this component. In order to represent the choice behavior for alternative behavior options, the select instruction is used for selecting the pair of channels according to the reception of the messages. Therefore, the value to be written to the measure output channel can be the one received via the sense input channel (sensed data) or the one received via the pass input channel (data from another sensor). ZigBee connector. Figure 16(a) shows the description of the ZigBee connector in π-ADL, which encompasses the input connection for receiving data and the output connection for sending data. In turn, as shown in Figure 16(b), this connector is implemented in Go by the ZigBee function, which receives as parameters the map of channels representing the set of named connections of this connector (conn) and the channels representing the connections to be unified when calling this goroutine within the main function. In the ZigBee function, the value received via the input channel is assigned to a variable (m) to be sent via the output channel. Gateway component. Figure 17(a) shows the specification of the Gateway component in πADL, which is composed of two connections: (i) the data input connection is used for receiving 34 π-ADL: A Formal Description Language for Software Architectures component Sensor is abstraction() { type MV is Real type CmH2O is Real connection sense is in(MV) connection pass is in(CmH2O) connection measure is out(CmH2O) protocol is { ((via sense receive MV | via pass receive CmH2O) via measure send CmH2O)* } behavior is { convertRawData is function(measure : MV) : CmH2O { unobservable } choose { via sense receive d : MV via measure send convertRawData(d) type CmH2O float64 behavior() type MV float64 } or { via pass receive m : CmH2O func Sensor(conn map[string]interface{}, via measure send m fromc, toc interface{}) { behavior() go func(fromc, toc interface{}) { } if (fromc != nil && toc != nil) { } v, _ := reflect.ValueOf(fromc).Recv() } reflect.ValueOf(toc).Send(v) (a) } }(fromc, toc) var convertRawData func(measure MV) CmH2O convertRawData = func(measure MV) CmH2O { // Empty body (unobservable) } select { case d := <- conn[“sense”].(chan MV): conn[“measure”].(chan CmH2O) <- convertRawData(d) Sensor(conn, fromc, toc) case m := <- conn[“pass”].(chan CmH2O): conn[“measure”].(chan CmH2O) <- m Sensor(conn, fromc, toc) } } func main() { S1 := map[string]interface{}{ “sense” : make(chan MV), “pass” : make(chan CmH2O), “measure” : make(chan CmH2O), } // ... } (b) Figure 15: Description of the Sensor component in π-ADL (left) and its corresponding implementation in Go (right). data collected by the sensor nodes, and; (ii) the alert output connection is used for sending alert messages in case of risk of flood. The behavior of this component encompass the definition of two functions. The calculateHI function calculates the hazard index (HI) [26], which is a measure that indicates the potential of flood risk based on the water level measures gathered by the sensors. The HI measure is used by the triggerAlert function in order to determine the severity of the flood risk and then send a message accordingly. Therefore, data received via the data input connection are provided as input to the triggerAlert function, which will analyze the potential of flood risk according to the calculated HI, and then the corresponding message is sent via the alert connection. As partially shown in Figure 17(b), this component is implemented in Go by the Gateway function, which receives as parameters the map of channels representing the set of named connections of this component (conn) and the channels representing the connections to be unified when calling this goroutine within the main function. The Gateway function also comprises the π-ADL: A Formal Description Language for Software Architectures connector ZigBee is abstraction() { type CmH2O is Real connection input is in(CmH2O) connection output is out(CmH2O) protocol is { (via input receive CmH2O via output send CmH2O)* } behavior is { via input receive m : CmH2O via output send m behavior() } } (a) 35 func ZigBee(conn map[string]interface{}, fromc, toc interface{}) { go func(fromc, toc interface{}) { if (fromc != nil && toc != nil) { v, _ := reflect.ValueOf(fromc).Recv() reflect.ValueOf(toc).Send(v) } }(fromc, toc) m := <- conn[“input”].(chan CmH2O) conn[“output”].(chan CmH2O) <- m ZigBee(conn, fromc, toc) } func main() { // ... L1 := map[string]interface{}{ “input” : make(chan CmH2O), “output” : make(chan CmH2O), } // ... (b) } Figure 16: Description of the ZigBee connector in π-ADL (left) and its corresponding implementation in Go (right). declaration of local functions (closures) corresponding to the calculateHI and triggerAlert functions specified in the behavior of this component. Notice that the statements implementing these functions are identical to the ones used in the description of this component in π-ADL. WSNFloodMonitoring architecture. Finally, Figure 18 shows the specification of the WSNFloodMonitoring architecture in π-ADL, which corresponds to the main executable function (main) in Go. In this function, instances of the Sensor and Gateway components (S1, S2, S3 and G) and of the ZigBee connector (L1, L2 and L3) are created by calling the respective goroutines that represent such elements and their behavior with the respective maps of channels. The unifications of connections specified within the composition behavior (compose) take place by passing the channels as the parameters to the goroutines (second and third parameters). For instance, first call to the ZigBee goroutine unifies the measure connection of the component S1 to the input connection of the connector L1, so that the contents of the output channel are sent to the input channel. Similarly, the call to the Gateway goroutine unifies the output connection of the connector L3 to the data connection of the component G. Although the example system presented in this section is somewhat simple, it can be easily scaled-up in order to highlight the distribution, concurrency, and dynamicity features addressed by the ensemble composed of the π-ADL description language and the Go programming language as its underlying implementation: • In the mapping process from π-ADL to Go, components and connectors are implemented as goroutines, which are lightweight processes. In this perspective, increasing the number of architectural elements to be considered in the architecture in order to have a largescale system does not promote a considerable impact mainly due to the efficient execution π-ADL: A Formal Description Language for Software Architectures component Gateway is abstraction() { type CmH2O is Real connection data is in(CmH2O) connection alert is out(String) protocol is { (via data receive CmH2O via alert send String)* } behavior is { calculateHI is function(data : CmH2O) : Real { unobservable } triggerAlert is function(measure : CmH2O) : String { hi is location[Real] hi = calculateHi(measure) if (hi > 0 && hi < 0.5) then { return "Low risk" } else if (hi >= 0.5 && hi < 1.0) then { return "Medium risk" } else if (hi >= 1.0 && hi < 1.4) then { return "High risk" } else { return "Very high risk" } } via data receive d : CmH2O via alert send triggerAlert(d) behavior() } } 36 func Gateway(conn map[string]interface{}, fromc, toc interface{}) { go func(fromc, toc interface{}) { if (fromc != nil && toc != nil) { v, _ := reflect.ValueOf(fromc).Recv() reflect.ValueOf(toc).Send(v) } }(fromc, toc) var calculateHI func(data CmH2O) float64 calculateHi = func(data CmH2O) float64 { // Empty body (unobservable) } var triggerAlert func(measure CmH2O) string triggerAlert = func(measure CmH2O) string { var hi float64 hi = calculateHI(measure) if (hi > 0 && hi < 0.5) { return "Low risk" } else if (hi >= 0.5 && hi < 1.0) { return "Medium risk" } else if (hi >= 1.0 && hi < 1.4) { return "High risk" } else { return "Very high risk" } } d := <- conn[“data”].(chan CmH2O) conn[“alert”].(chan string) <- triggerAlert(d) Gateway(conn, fromc, toc) } func main() { // ... G := map[string]interface{}{ “data” : make(chan CmH2O), “alert” : make(chan string), } // ... } Figure 17: Description of the Gateway component in π-ADL (left) and its corresponding implementation in Go (right). support provided by Go. • Go natively offers an easy, lightweight support for the concurrent communication and execution of distributed programs mainly by using goroutines. In this perspective, as the architectural elements of the system are logically and physically distributed, such features provided by Go enable their concurrent execution and then they foster an easy development and deployment of this type of system. • There might be cases in which it is necessary to add, remove, or replace sensors or connections in the system. Components and connectors can be dynamically added to the system as the creation of these elements is simply performed through new calls to the respective goroutines that implement them. In case of replacing components and connectors, it is necessary to make new calls to the goroutines that are to replace the elements and then rearrange the communication channels in order to enable their synchronization. Finally, when removing these elements, the goroutines associated with them are to be blocked by closing the communication channels (connections) and the garbage collector of the Go language will be in charge of dealing with their effective destruction. 37 π-ADL: A Formal Description Language for Software Architectures architecture WSNFloodMonitoring is abstraction() { behavior is { compose { S1 is Sensor() and S2 is Sensor() and S3 is Sensor() and L1 is ZigBee() and L2 is ZigBee() and L3 is ZigBee() and G is Gateway() } where { S1::measure unifies L1::input S2::measure unifies L2::input L1::output unifies S3::pass L2::output unifies S3::pass S3::measure unifies L3::input L3::output unifies G::data } } } (a) func main() { S1 := map[string]interface{}{ “sense” : make(chan MV), “pass” : make(chan CmH2O), “measure” : make(chan CmH2O), } S2 := map[string]interface{}{ “sense” : make(chan MV), “pass” : make(chan CmH2O), “measure” : make(chan CmH2O), } S3 := map[string]interface{}{ “sense” : make(chan MV), “pass” : make(chan CmH2O), “measure” : make(chan CmH2O), } L1 := map[string]interface{}{ “input” : make(chan CmH2O), “output” : make(chan CmH2O), } L2 := map[string]interface{}{ “input” : make(chan CmH2O), “output” : make(chan CmH2O), } L3 := map[string]interface{}{ “input” : make(chan CmH2O), “output” : make(chan CmH2O), } G := map[string]interface{}{ “fromSensor” : make(chan CmH2O), “alert” : make(chan string), } go go go go go Sensor(S1, Sensor(S1, Sensor(S2, Sensor(S2, Sensor(S3, nil, nil, nil, nil, nil, nil) nil) nil) nil) nil) go go go go go go ZigBee(L1, ZigBee(L2, Sensor(S3, Sensor(S3, ZigBee(L3, Gateway(G, S1[“measure”], S2[“measure”], L1[“output”], L2[“output”], S3[“measure”], L3[“output”], L1[“input”]) L2[“input”]) S3[“pass”]) S3[“pass”]) L3[“input”]) G[“data”]) } (b) Figure 18: Description of the WSNFloodMonitoring architecture in π-ADL (left) and its corresponding implementation in Go (right). 5 Final remarks This report introduced π-ADL, a formal language based on the π-calculus process algebra for specifying dynamic software architectures under structural and behavioral viewpoints and supporting their automated analysis with respect to functional and non-functional properties. In order to tackle the existing gap between architecture descriptions and their implementations in the context of large-scale, dynamic, distributed software systems, π-ADL was integrated with Go, a general-purpose programming language suitable for building large-scale distributed systems deployed in multicore and networked computer architectures. Go was chosen to serve as implementation language due to its basis on π-calculus (the same of π-ADL), so that the straightforward relationship between elements of the languages has fostered such an integration. Finally, π-ADL was endowed with a tool support for assisting software architects in the description of architectures by using the π-ADL language and for automatically generating implementation code in Go. π-ADL: A Formal Description Language for Software Architectures 38 In future works, the mapping process from π-ADL to Go will be evaluated by using existing model transformation metrics, as the ones discussed in [37] for text-to-text (T2T) transformations. Furthermore, future versions of the π-ADL language will include architectural elements for dynamic reconfiguration, also addressing how reconfiguration actions specified at the architectural level take place at the implementation level and vice-versa [15, 24], as well as the verification and enforcement of structural, behavioral, and quality properties before, during, and after the reconfiguration process itself. Finally, the current tool support for π-ADL will be extended to be made available as an Eclipse IDE plug-in with corresponding graphical and textual representations of software architectures. Acknowledgements This work was partially supported by the Brazilian National Agency of Petroleum, Natural Gas and Biofuels through the PRH-22/ANP/MCTI Program, and by the following Brazilian funding agencies: CAPES, under grant 11097/2013-2; CNPq, under grants 400449/2013-7 and 203194/2014-4, and; INES, under grant 573964/2008-4. π-ADL: A Formal Description Language for Software Architectures 39 Appendix A – π-ADL Grammar This appendix describes the concrete textual syntax of π-ADL by using the Extended Backus-Naur Form (EBNF) meta-language [29]. EBNF is a notation for formally describing the context-free grammar of a language, i.e., its syntax. Such a notation consists of terminal symbols, which are a sequence of one or more characters forming an irreducible element of the language, and non-terminal production rules, which governs how a particular syntactic element can be legally formed in terms of terminal symbols. Syntactic elements have names that are used in production rules and they are distinguished from names and reserved words (keywords) in the language. The EBNF meta-language uses the following meta-symbols: • a right arrow (→) expresses the definition of a production rule, so that the rule A → B can be read A is defined as B.; • the pipe symbol (|) indicates a choice in a production rule; • brackets surrounding a term denote that it is optional in the production rule; • the asterisk character (∗) is used to express the occurrence of a term by zero or many times; • the plus character (+) is used to express the occurrence of a term by one or many times (i.e., such a term occurs at least once); • the ampersand character (&) indicates that the elements can occur in any order, so that A & B indicates that both AB and BA sequences of the A and B elements are valid; • ellipses (...) indicate a character range; • parentheses are used to group elements. In the EBNF specification of the π-ADL grammar, reserved words and terminal symbols of the language are expressed in a typewriter font, whereas names of production rules are typed in the italic form and the abovementioned meta-symbols of the EBNF notation appear in normal writing. It is important to highlight that names of production rules and attributes in this specification seek to be representative and self-explanatory. As an example, consider the three following production rules: ArchitectureDescription → ArchitecturalElement ∗ Architecture ArchitecturalElement → Component | Connector Component → component Identi f ier is abstraction([Parameter (, Parameter)∗]) { TypeDeclaration ∗ ConnectionDeclaration ∗ VariableDeclaration ∗ [ProtocolDeclaration] BehaviorDeclaration } π-ADL: A Formal Description Language for Software Architectures 40 The ArchitectureDescription rule refers to an architecture description composed of a set of architectural elements (defined by the ArchitectureElement production rule) and an architecture itself (defined by the Architecture production rule). An architectural element can be either a component or a connector, respectively defined by the Component and Connector production rules. A component is declared by using the component keyword and it comprises an identifier (represented by the Identi f ier production rule). Optionally, it can also take a list of parameters (each one defined by the Parameter production rule and separated by commas) as input, between parentheses. Within the definition of this architectural element (delimited by braces), one can have in sequence: • declaration of zero or more types defined by the TypeDeclaration production rule; • declaration of zero or more connections defined by the ConnectionDeclaration production rule; • declaration of zero or more variables defined by the VariableDeclaration production rule; • optional declaration a protocol defined by the ProtocolDeclaration production rule, and; • declaration of exactly one (as the multiplicity characters ∗ and + are not used) behavior defined by the BehaviorDeclaration production rule. π-ADL: A Formal Description Language for Software Architectures π-ADL production rules ArchitectureDescription → ArchitecturalElement ∗ Architecture ArchitecturalElement → Component | Connector Component → component Identi f ier is abstraction([Parameter (, Parameter)∗]) { TypeDeclaration ∗ ConnectionDeclaration ∗ VariableDeclaration ∗ [ProtocolDeclaration] BehaviorDeclaration } Connector → connector Identi f ier is abstraction([Parameter (, Parameter)∗]) { TypeDeclaration ∗ ConnectionDeclaration ∗ VariableDeclaration ∗ [ProtocolDeclaration] BehaviorDeclaration } TypeDeclaration → type Identi f ier is ValueType ConnectionDeclaration → connection Identi f ier is ConnectionMode (ValueType) ConnectionMode → in | out VariableDeclaration → Identi f ier is Location ProtocolDeclaration → protocol is { ( (ConnectionProtocol (| ConnectionProtocol)∗) & [ConnectionProtocol] )(* | +) } ConnectionProtocol → ( ∗ via Identi f ier ConnectionAction ValueType ) ∗ ConnectionAction → send | receive BehaviorDeclaration → behavior is { Behavior ∗ } Behavior → TypeDeclaration | VariableDeclaration | ExplicitPro jection | Assignment | FunctionDeclaration 41 π-ADL: A Formal Description Language for Software Architectures | | | | | | FunctionCall Pre f ix ChoiceBehavior ComposeBehavior Recurse Inaction ExplicitPro jection → project Identi f ier as Parameter (, Parameter) ∗ Assignment → VariableAssignment | CollectionAddition | CollectionRemoval VariableAssignment → Identi f ier = AbstractExpression CollectionAddition → Identi f ier add (AddToMap | AddToSet | AddToSequence) AddToMap → [ AbstractExpression, AbstractExpression ] AddToSet → [ AbstractExpression ] AddToSequence → #[ AbstractExpression ] CollectionRemoval → Identi f ier remove (RemoveFromMap | RemoveFromSet | RemoveFromSequence) RemoveFromMap → @( AbstractExpression ) RemoveFromSet → [ AbstractExpression ] RemoveFromSequence → #IntegerNumber FunctionDeclaration → Identi f ier is function( [Parameter (, Parameter)∗] ) [: ValueType] { Statement ∗ [Return] } Return → return AbstractExpression Statement → TypeDeclaration | VariableDeclaration | ExplicitPro jection | Assignment | FunctionCall | I f T henElse | W hile | For | Return | Unobservable I f T henElse → if AbstractExpression then { Statement ∗ } ElseI f ∗ 42 π-ADL: A Formal Description Language for Software Architectures [Else] ElseI f → else if AbstractExpression then { Statement ∗ } Else → else { Statement ∗ } W hile → while AbstractExpression do { Statement ∗ } For → for ( VariableAssignment; LogicalExpression; VariableAssignment ) do { Statement ∗ } Unobservable → unobservable Pre f ix → SilentPre f ix | InputPre f ix | Out putPre f ix | ConditionalPre f ix SilentPre f ix → unobservable InputPre f ix → via Identi f ier receive Parameter Out putPre f ix → via Identi f ier send AbstractExpression Parameter → Identi f ier : ValueType ConditionalPre f ix → if AbstractExpression then { Pre f ix ∗ } ChoiceBehavior → choose { Behavior + } (or { Behavior + }) + CompositionBehavior → compose { Behavior + } (and { Behavior + }) + Inaction → done Recurse → behavior( [AbstractExpression (, AbstractExpression)∗] ) Architecture → architecture Identi f ier is abstraction( [Parameter (, AbstractExpression)∗] ) { 43 π-ADL: A Formal Description Language for Software Architectures 44 behavior is { compose { ElementInstantiation (and ElementInstantiation) ∗ } where { Uni f ication ∗ } } } ElementInstantiation → Identi f ier is Identi f ier ([Identi f ier (, Identi f ier )∗] ) Uni f ication → ConnectionAccess unifies ConnectionAccess ConnectionAccess → Identi f ier :: Identi f ier ValueType → BasicType | ConstructedType | Identi f ier BasicType → NaturalType | IntegerType | RealType | BooleanType | StringType | AnyType NaturalType → Natural IntegerType → Integer RealType → Real BooleanType → Boolean StringType → String AnyType → Any ConstructedType → Tuple | View | Map | Set | Sequence Tuple → tuple[ ValueType (, ValueType) ∗ ] View → view[ LabeledType (, LabeledType) ∗ ] LabeledType → Identi f ier : ValueType Location → location[ ValueType ] Map → map[ ValueType, ValueType ] Set → set[ ValueType ] Sequence → sequence[ ValueType ] AbstractExpression → Expression | ConstructedValue Expression → LogicalExpression LogicalExpression → EqualityExpression (|| | &&) EqualityExpression EqualityExpression → RelationalExpression (== | !=) RelationalExpression RelationalExpression → ArithmeticExpression (>= | <= | > | <) ArithmeticExpression ArithmeticExpression → Term (+ | -) Term π-ADL: A Formal Description Language for Software Architectures 45 Term → Factor (* | / | mod) Factor Factor → ( Expression ) | UnaryExpression | AtomicElement UnaryExpression → ! AtomicElement AtomicExpression → LiteralElement | Identi f ier LiteralElement → IntegerLiteral | RealLiteral | BooleanLiteral | StringLiteral IntegerLiteral → Number RealLiteral → RealNumber StringLiteral → String BooleanLiteral → true | false ConstructedValue → FunctionCall | TupleValue | ViewValue FunctionCall → Identi f ier ( [AbstractExpression (, AbstractExpression)∗] ) TupleValue → tuple[ AbstractExpression (, AbstractExpression) ∗ ] ViewValue → view[Identi f ier : AbstractExpression (, Identi f ier : AbstractExpression)∗] Identi f ier → (a...z | A...Z | _) (a...z | A...Z | _ | 0...9) String → “ <any ASCII character> ” RealNumber → Number . Number Number → (0...9) + 46 π-ADL: A Formal Description Language for Software Architectures References [1] ANTRL. http://www.antlr.org/. [2] Eclipse IDE. http://eclipse.org/. [3] Eclipse Modeling Framework Project (EMF). http://www.eclipse.org/modeling/emf/. [4] Go Runtime Environment – Google App Engine. appengine/docs/go/. http://developers.google.com/ [5] Microsoft .NET Framework. http://www.microsoft.com/net. [6] PiADL2Go: Mapping π-ADL architectural descriptions to implementations in the Go language. http://consiste.dimap.ufrn.br/projects/piadl2go/. [7] The Go Programming Language. http://golang.org. [8] The Go Programming Language Specification. http://golang.org/ref/spec. [9] Xtend. https://www.eclipse.org/xtend/. [10] Xtext. http://www.eclipse.org/Xtext/. [11] ALLEN, R; DOUENCE, R; GARLAN, D. Specifying and analyzing dynamic software architectures. In: Astesiano, E, editor, PROCEEDINGS OF THE FIRST INTERNATIONAL CONFERENCE ON FUNDAMENTAL APPROACHES TO SOFTWARE ENGINEERING (FASE’98), volume 1382 de Lecture Notes in Computer Science, p. 21–37. Springer Berlin Heidelberg, Germany, 1998. [12] ALLEN, R. J. A formal approach to software architecture. PhD thesis, Carnegie Mellon University, Pittsburgh, PA, USA, 1997. [13] BALBAERT, I. The way to Go: A thorough introduction to the Go programming language. iUniverse, USA, 2012. [14] BASS, L; CLEMENTS, P; KAZMAN, R. Software Architecture in practice. AddisonWesley, USA, 3rd edition, 2012. [15] BATISTA, T; JOOLIA, A; COULSON, G. Managing dynamic reconfiguration in component-based systems. In: Morrison, R; Oquendo, F, editors, PROCEEDINGS OF THE 2ND EUROPEAN WORKSHOP ON SOFTWARE ARCHITECTURE (EWSA 2005), volume 3527 de Lecture Notes in Computer Science, p. 1–17. Springer-Verlag Berlin Heidelberg, Germany, 2005. [16] CAVALCANTE, E; OQUENDO, F; BATISTA, T. Architecture-based code generation: From π-ADL architecture descriptions to implementations in the Go language. In: Avgeriou, P; Zdun, U, editors, PROCEEDINGS OF THE 8TH EUROPEAN CONFERENCE ON SOFTWARE ARCHITECTURE (ECSA 2014), volume 8627 de Lecture Notes in Computer Science, p. 130–145. Springer International Publishing, Switzerland, 2014. [17] CHISNALL, D. The Go programming language phrasebook. Addision-Wesley / Pearson Educational, Inc, USA, 2012. π-ADL: A Formal Description Language for Software Architectures 47 [18] CLEMENTS, P. A survey of architecture description languages. In: PROCEEDINGS OF THE 8TH INTERNATIONAL WORKSHOP ON SOFTWARE SPECIFICATION AND DESIGN (IWSSD 1996), p. 16–25, USA, 1996. IEEE Computer Society. [19] DASHOFY, E. M. Supporting stakeholder-driven, multi-view software architecture modeling. Phd dissertation, University of California, Irvine, USA, 2007. [20] DASHOFY, E. M; VAN DER HOEK, A; TAYLOR, R. N. A highly-extensible, XMLbased architecture description language. In: PROCEEDINGS OF THE 2001 WORKING IEEE/IFIP CONFERENCE ON SOFTWARE ARCHITECTURE (WICSA 2001), p. 103–112, Piscataway, NJ, USA, 2001. IEEE Computer Society. [21] DASHOFY, E. M; VAN DER HOEK, A; TAYLOR, R. N. An infrastructure for the rapid development of XML-based architecture description languages. In: PROCEEDINGS OF THE 24TH INTERNATIONAL CONFERENCE ON SOFTWARE ENGINEERING (ICSE 2002), p. 266–276, New York, NY, USA, 2002. ACM. [22] GARLAN, D; MONROE, R; WILE, D. ACME: An architecture description interchange language. In: PROCEEDINGS OF THE 1997 CONFERENCE OF THE CENTRE FOR ADVANCED STUDIES ON COLLABORATIVE RESEARCH (CASCON’97), p. 169–189, USA, 1997. IBM Press. [23] GEORGAS, J. C; DASHOFY, E. M; TAYLOR, R. N. Architecture-centric development: A different approach to Software Engineering. Crossroads Magazine, 12(4), Aug. 2006. [24] GOMES, A. T. A; BATISTA, T. V; JOOLIA, A; COULSON, G. Architecting dynamic reconfiguration in dependable systems. In: de Lemos, R; Gacek, C; Romanovsky, A, editors, ARCHITECTING DEPENDABLE SYSTEMS IV, volume 4615 de Lecture Notes in Computer Science, p. 237–261. Springer-Verlag Berlin Heidelberg, Germany, 2007. [25] HOARE, C. A. Communicating sequential processes. Communications of the ACM, 21(8):666–677, Aug. 1978. [26] HORITA, F. E. A; FAVA, M. C; MENDIONDO, E. M; ROTAVA, J; SOUZA, V. C; UEYAMA, J; AO PORTO DE ALBUQUERQUE, J. AGORA-GeoDash: A geosensor dashboard for real-time flood risk monitoring. In: Hiltz, S. R; Pfaff, M. S; Plotnick, L; Shih, P. C, editors, PROCEEDINGS OF THE 11TH INTERNATIONAL CONFERENCE ON INFORMATION SYSTEMS FOR CRISIS RESPONSE AND MANAGEMENT (ISCRAM 2014), p. 309–318, USA, 2014. The Pennsylvania State University. [27] HUGHES, D; GREENWOOD, P; BLAIR, G; COULSON, G; GRACE, P; PAPPENBERGER, F; SMITH, P; BEVEN, K. An experiment with reflective middleware to support grid-based flood monitoring. Concurrency and Computation: Practice & Experience, 20(11):1303–1316, Aug. 2008. [28] HUGHES, D; UEYAMA, J; MENDIONDO, E; MATTHYS, N; HORRÉ, W; MICHIELS, S; HUYGENS, C; JOOSEN, W; MAN, K. L; GUAN, S.-U. A middleware platform to support river monitoring using wireless sensor networks. Journal of the Brazilian Computer Society, 17(2):85–102, Jun. 2011. π-ADL: A Formal Description Language for Software Architectures 48 [29] ISO/IEC 14977:1996(E). ISO/IEC International Standard for Information Technology – Syntactic metalanguage, Extended BNF. ISO/IEC, Geneva, Switzerland, 1996. [30] ISO/IEC/IEEE 42010:2011(E). ISO/IEC/IEEE International Standard for Systems and Software Engineering – Architectural Description. ISO, Geneva, Switzerland, 2011. [31] LUCKHAM, D. C; KENNEY, J. J; AUGUSTIN, L. M; VERA, J; BRYAN, D; MANN, W. Specification and analysis of software architecture using RAPIDE. IEEE Transactions on Software Engineering, 21(4):336–355, Apr. 1995. [32] MAGEE, J; DULAY, N; EISENBACH, S; KRAMER, J. Specifying distributed software architectures. In: PROCEEDINGS OF THE 5TH EUROPEAN SOFTWARE ENGINEERING CONFERENCE (ESEC’95), p. 137–153, United Kingdom, 1995. Springer-Verlag London. [33] MALAVOLTA, I; LAGO, P; MUCCINI, H; PELLICCIONE, P; TANG, A. What industry needs from architectural languages: A survey. IEEE Transactions on Software Engineering, 39(6):869–891, Jun. 2013. [34] MEDVIDOVIC, N; TAYLOR, R. N. A classification and comparison framework for software architecture description languages. IEEE Transactions on Software Engineering, 26(1):70–93, Jan. 2000. [35] MILNER, R. A calculus for communicating systems, volume 92 de Lecture Notes in Computer Science. Springer-Verlag Berlin Heidelberg, Germany, 1980. [36] MILNER, R. Communicating and mobile systems: The π-calculus. Cambridge University Press, USA, 1999. [37] NGUYEN, P. H. Quantitative analysis of model transformations. Master’s thesis, Technische Universiteit Eindhoven, Eindhoven, The Netherlands, 2010. [38] OQUENDO, F. π-ADL: An architecture description language based on the higherorder typed π-calculus for specifying dynamic and mobile software architectures. ACM SIGSOFT Software Engineering Notes, 29(3):1–14, May 2004. [39] OQUENDO, F. Tutorial on ArchWare ADL – Version 2. Technical report, ArchWare Consortium, 2005. [40] OQUENDO, F. π-Method: A model-driven formal method for architecture-centric software engineering. ACM SIGSOFT Software Engineering Notes, 31(3):1–13, May 2006. [41] OQUENDO, F; ALLOUI, I; CÎMPAN, S; VERJUS, H. The ArchWare ADL: Definition of the abstract syntax and formal semantics. Technical report, ArchWare Consortium, 2002. [42] OQUENDO, F; WARBOYS, B; MORRISON, R; DINDELEUX, R; GALLO, F; GARAVEL, H; OCCHIPINTI, C. ArchWare: Architecting evolvable software. In: Oquendo, F; Warboys, B. C; Morrison, R, editors, PROCEEDINGS OF THE FIRST EUROPEAN WORKSHOP ON SOFTWARE ARCHITECTURE (EWSA 2004), volume 3047 de Lecture Notes in Computer Science, p. 257–271. Springer-Verlag Berlin Heidelberg, Germany, 2004. π-ADL: A Formal Description Language for Software Architectures 49 [43] PIERCE, B. C. Foundational calculi for programming languages. In: Tucker, A. B, editor, HANDBOOK OF COMPUTER SCIENCE AND ENGINEERING. CRC Press, USA, 1996. [44] QAYYUM, Z. Realization of software architectures using a formal language: Towards languages dedicated to formal development based on π-ADL6 . PhD thesis, Université de Bretagne-Sud, Vannes, France, 2009. [45] SHAW, M; CLEMENTS, P. The Golden Age of Software Architecture. IEEE Software, 23(2):31–39, Mar./Apr. 2006. [46] SHAW, M; GARLAN, D. Software Architecture: Perspectives on an emerging discipline. Prentice Hall, USA, 1996. [47] SUMMERFIELD, M. Programming in Go: Creating applications for the 21st Century. Addision-Wesley, USA, 2012. [48] UEYAMA, J; HUGHES, D. R; MATTHYS, N; HORRÉ, W; JOOSEN, W; HUYGENS, C; MICHIELS, S. An event-based component model for wireless sensor networks: A case study for river monitoring. In: PROCEEDINGS OF THE 28TH BRAZILIAN SYMPOSIUM ON COMPUTER NETWORKS AND DISTRIBUTED SYSTEMS (SBRC 2010), p. 997–1004, Porto Alegre, RS, Brazil, 2010. SBC. [49] WASSERMAN, A. I. Toward a discipline of Software Engineering. IEEE Software, 13(6):23–31, Nov. 1996. 6 Original title in French: Concrétisation des architectures logicielles à l’aide d’un langage formel: vers les langages dediés au développement formel fondés sur π-ADL
© Copyright 2026 Paperzz