Studies into Refactoring of Software Architectures

0 downloads 0 Views 498KB Size Report
3.3 Channels for SAAT CSP processes before refactoring . ..... The program that our case-study is based on is SAAT (Software Architecture Analysis Tool) ...
TECHNISCHE UNIVERSITEIT EINDHOVEN Department of Mathematics and Computing Science

MASTER’S THESIS

Studies into Refactoring of Software Architectures by Marc van Kempen ([email protected])

Supervisor: Dr. M.R.V. Chaudron Eindhoven, June 2005

2

Contents 1

2

Introduction

13

1.1

Software Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.1.1

Modern Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.1.2

Classic Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14

1.2

Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

1.3

Connectors and Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16

1.4

Problem statement

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

1.5

Approach . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17

1.6

Outline of the thesis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

18

CSP 2.1

3

19 CSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

2.1.1

Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

2.1.2

Traces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19

2.1.3

Hiding or abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

2.1.4

Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

2.1.5

Communication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

20

2.1.6

Choice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21

2.1.7

If-then-else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

2.1.8

Refinement and equality . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22

Refactoring Architectural Design

23

3.1

Statechart refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

26

3.2

Mapping statecharts to CSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

29

3.2.1

Mapping SAAT before refactoring to CSP . . . . . . . . . . . . . . . . . . .

30

3.2.2

Mapping SAAT after refactoring to CSP

. . . . . . . . . . . . . . . . . . .

31

3.2.3

Behaviour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

31

3

CONTENTS

4 4

Refactoring Architectural Style

33

4.1

KWIC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33

4.2

A few words on flow and Figures . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34

4.3

Refactoring from Pipe and Filter to Blackboard . . . . . . . . . . . . . . . . . . . .

35

4.3.1

Pipe and Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

4.3.1.1

Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

4.3.1.2

Connector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36

4.3.1.3

CSP model of the generic Pipe and Filter style . . . . . . . . . . .

36

4.3.1.4

KWIC as instance of the generic Pipe and Filter Architecture . . .

37

Blackboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

4.3.2.1

CSP model of the generic Blackboard style . . . . . . . . . . . . .

38

4.3.2.2

KWIC as instance of the generic Blackboard Architecture . . . . .

42

Behaviour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

44

Refactoring from Client/Server to Blackboard . . . . . . . . . . . . . . . . . . . . .

44

4.4.1

Client/Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

4.4.1.1

CSP model of the Client/Server architecture . . . . . . . . . . . .

47

4.4.1.2

KWIC instance of the generic Client/Server Architecture . . . . .

49

Client/Server refactored to Blackboard . . . . . . . . . . . . . . . . . . . . .

52

4.4.2.1

KWIC instance of the generic Blackboard Architecture . . . . . .

53

Behaviour . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

58

Comparing Quality Attributes of Styles . . . . . . . . . . . . . . . . . . . . . . . .

58

4.5.1

Adding one component to the Client/Server Architecture . . . . . . . . . . .

58

4.5.2

Adding one component to the Blackboard Architecture . . . . . . . . . . . .

61

Java implementation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

61

4.6.1

CSP semantics in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

4.6.2

Darwin notation in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . .

62

4.6.3

Pipe and Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

63

4.6.4

Blackboard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

4.6.5

Automating refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67

4.3.2

4.3.3 4.4

4.4.2

4.4.3 4.5

4.6

5

Conclusion

69

5.1

69

Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS A Java Implementations KWIC

5 73

A.1 Package kwic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

73

A.1.1 Package kwic.component . . . . . . . . . . . . . . . . . . . . . . . . . . . .

74

A.2 Package refactor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

A.2.1 Package refactor.base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81

A.2.2 Package refactor.bb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84

A.2.3 Package refactor.pf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91

B CSP Input files for FDR

95

B.1 SAAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

95

B.2 Refactoring from Pipe and Filter to Blackboard . . . . . . . . . . . . . . . . . . . .

96

B.3 Refactoring from Client/Server to Blackboard . . . . . . . . . . . . . . . . . . . . . 101

6

CONTENTS

List of Figures 3.1

SAAT Class diagram before refactoring . . . . . . . . . . . . . . . . . . . . . . . .

24

3.2

SAAT class diagram after refactoring . . . . . . . . . . . . . . . . . . . . . . . . . .

25

3.3

Statechart for SAAT before refactoring . . . . . . . . . . . . . . . . . . . . . . . . .

27

3.4

Statechart for DBCreate

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

27

3.5

Statechart of SAAT after refactoring . . . . . . . . . . . . . . . . . . . . . . . . . .

27

3.6

Statechart of DB, introduced after refactoring . . . . . . . . . . . . . . . . . . . . .

28

3.7

Statechart of Statistics, introduced after refactoring . . . . . . . . . . . . . . . . . .

28

4.1

Description of conventions used in Figures . . . . . . . . . . . . . . . . . . . . . . .

35

4.2

Pipe and Filter architecture style . . . . . . . . . . . . . . . . . . . . . . . . . . . .

35

4.3

Sequence diagram showing a message exchange for the Pipe and Filter Architecture .

37

4.4

KWIC in Pipe and Filter style

. . . . . . . . . . . . . . . . . . . . . . . . . . . . .

38

4.5

Blackboard Style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39

4.6

Blackboard style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41

4.7

Sequence diagram showing a message exchange for the Pipe and Filter architecture (see also Figure 4.5) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43

4.8

KWIC system in Blackboard style. . . . . . . . . . . . . . . . . . . . . . . . . . . .

45

4.9

Typical Client/Server architecture . . . . . . . . . . . . . . . . . . . . . . . . . . .

46

4.10 Client/Server architecture using connectors. . . . . . . . . . . . . . . . . . . . . . .

48

4.11 Sequence diagram showing a message exchange where Client1 request a response from Server1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

4.12 KWIC in Client/Server style . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

50

4.13 Generic Client/Server refactored to Blackboard style . . . . . . . . . . . . . . . . .

54

4.14 Generic Pipe and Filter Architecture side by side with Generic Blackboard Architecture 55 4.15 KWIC refactored from Client/Server to Blackboard . . . . . . . . . . . . . . . . . .

56

4.16 Sequence Diagram showing a message exchange for the Blackboard architecture, where Client1 requests a response from Server1 . . . . . . . . . . . . . . . . . . . .

59

7

8

LIST OF FIGURES 4.17 Adding a client node to the Client/Server Architecture . . . . . . . . . . . . . . . . .

60

4.18 Adding a client node to the Blackboard architecture. . . . . . . . . . . . . . . . . . .

61

4.19 Pipe and Filter architecture refactored to Blackboard . . . . . . . . . . . . . . . . .

66

List of Tables 3.1

Metrics for the SAAT architecture . . . . . . . . . . . . . . . . . . . . . . . . . . .

24

3.2

Metrics for the SAAT architecture after refactoring . . . . . . . . . . . . . . . . . .

25

3.3

Channels for SAAT CSP processes before refactoring . . . . . . . . . . . . . . . . .

30

5.1

Overview of possible refactorings between architectural styles . . . . . . . . . . . .

70

9

10

LIST OF TABLES

Thanks First of all I must thank Yvon, for bearing with me over the past two years when I first got the idea to „go back in time“ and be a student again, and my friend and colleague Henry without whose patience things would have been much more difficult. I would like to thank the University of Pretoria for providing a warm welcome for me during my stay there as part of my master’s. I would like to especially thank Prof. Derrick Kourie for supervising me during my stay. Also I would like to thank Dr. Andrew Boake for his insightful comments and the interesting discussions we had. I would like to thank Mohammad Mousavi, who in spite of his own deadline for his PhD Thesis, still managed to make time to educate me about CSP. I would also like to thank Prof. Judith Bishop of the University of Pretoria, for providing me with valuable feedback on the chapter about refactoring UML design. I would like to thank Formal Systems (Europe) Limited for providing me with an academic license for FDR2. Finally of course Michel Chaudron, my supervisor at the TUE, for his relentless pursuit of refactoring architectural styles, which seemed to be too big a task when I started this, but proved doable in the end.

11

12

LIST OF TABLES

Chapter 1

Introduction Software in a real-world environment keeps evolving, as detailed requirements become clearer, and new requirements emerge. Software evolves by being modified, having new functionality added or removed. This often results in the internal structure of the software becoming more complex and moving away from the original design. At a certain point it becomes desirable to reorganise (restructure) the system in order to make it more maintainable, readable and extendable. Also, through experience, it has become clear that before adding new functionality, it is advisable to clean up the existing design in order to prepare it for the coming changes. This process has become known as restructuring [Arn86], or better known as refactoring [Opd92, Fow99] for object-oriented software. The defining feature of a refactoring is that the overall behaviour of the system must be preserved. Most commonly, refactoring operates on the source-code level. In this thesis however, refactoring will be applied at a design level, a structural refactoring and to architectural styles. Refactoring on an architectural level has clear benefits. Since the architecture is defined in the early stages of development, the corrections of flaws during the design and analysis stage can be achieved much cheaper and faster than during the implementation. Also, since each architectural style has its own specific quality attributes (extendability, flexibility, performance, etc.), migrating and integrating large systems, based on changes in the quality attributes requirements, will become much less painful. In other words it will ease the development of systems over time. In this thesis we will look at two different types of refactoring. 1. Refactoring of distribution of responsabilities of a Software Architecture, expressed as a UML model. 2. Refactoring from one Software Architecture style to another. Pivotal to any refactoring and thus the underlying treatment of it, is the preservation of behaviour. To be able to reason about behaviour, we need to formalise it. To this end we will use an existing formalism, CSP (Communicating Sequential Processes)[Hoa85], a process algebra. We will show that it is possible to express the architectural styles as CSP processes, after which we can use the underlying theory of CSP and tool support to prove that the original architecture and the refactored architecture have the same behaviour. 13

CHAPTER 1. INTRODUCTION

14

1.1 Software Architecture In the following sections, a selection of some popular definitions of the term „software architecture“ will be presented. The first section contains definitions from recent work, while the second section contains the more „Classic“ definitions. This selection is based on the definitions collected at the web page http://www.sei.cmu.edu/architecture/definitions.html.

1.1.1 Modern Definitions 1. From the book Software Architecture in Practice (2nd edition), (Bass, Clements, Kazman; Addison-Wesley 2003) [LBK03] (we show just an excerpt here, for the full definition see the referenced text): The software architecture of a program or computing system is the structure or structures of the system, which comprise software elements, the externally visible properties of those elements, and the relationships among them. „Externally visible“ properties refers to those assumptions other elements can make of an element, such as its provided services, performance characteristics, fault handling, shared resource usage, and so on. 2. ANSI/IEEE Std 1471-2000, Recommended Practice for Architectural Description of SoftwareIntensive Systems [ANS]: Architecture is defined by the recommended practice as the fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution. This definition is intended to encompass a variety of uses of the term architecture by recognizing their underlying common elements. Principal among these is the need to understand and control those elements of system design that capture the system’s utility, cost, and risk. In some cases, these elements are the physical components of the system and their relationships. In other cases, these elements are not physical, but instead, logical components. In still other cases, these elements are enduring principles or patterns that create enduring organizational structures. The definition is intended to encompass these distinct, but related uses, while encouraging more rigorous definition of what constitutes the fundamental organization of a system within particular domains.

1.1.2 Classic Definitions 1. Booch, Rumbaugh, and Jacobson, 1999 [BRJ99]: An architecture is the set of significant decisions about the organization of a software system, the selection of the structural elements and their interfaces by which the system is composed, together with their behavior as specified in the collaborations among those elements, the composition of these structural and behavioral elements into progressively larger subsystems, and the architectural style that guides this organization—these elements and their interfaces, their collaborations, and their composition.

1.1. SOFTWARE ARCHITECTURE

15

2. Philippe Kruchten, 1999 [Kru99] Software architecture is not only concerned with structure and behaviour, but also with • usage

• functionality • performance • resilience

• reuse • comprehensibility

• economic and technological constraints and trade-offs • aesthetics

3. Garlan and Shaw, 1994 [GS94]: In what has come to be regarded as a seminal paper on software architecture, Mary Shaw and David Garlan suggest that software architecture is a level of design concerned with issues ...beyond the algorithms and data structures of the computation; designing and specifying the overall system structure emerges as a new kind of problem. Structural issues include gross organization and global control structure; protocols for communication, synchronization, and data access; assignment of functionality to design elements; physical distribution; composition of design elements; scaling and performance; and selection among design alternatives." 4. Garlan and Perry, 1995 [GP95]: David Garlan and Dewayne Perry adopted the following definition for their guest editorial to the April 1995 IEEE Transactions on Software Engineering devoted to software architecture: The structure of the components of a program/system, their interrelationships, and principles and guidelines governing their design and evolution over time. (The source of this definition was a weekly discussion group devoted to software architecture at the Software Engineering Institute.) 5. Boehm, et al., 1995 [GAACB95] Barry Boehm and his students at the USC Center for Software Engineering write that: A software system architecture comprises • A collection of software and system components, connections, and constraints. • A collection of system stakeholder’s need statements.

• A rationale which demonstrates that the components, connections, and constraints define a system that, if implemented, would satisfy the collection of system stakeholder’s need statements.

6. Soni, Nord, and Hofmeister, 1995 [SNH95]: Soni, Nord, and Hofmeister of Siemens Corporate Research write that, based on structures found to be prevalent and influential in the development environment of industrial projects they studied, software architecture has at least four distinct incarnations: Within each category, the structures describe the system from a different perspective:

CHAPTER 1. INTRODUCTION

16

• The conceptual architecture describes the system in terms of its major design elements and the relationships among them. • The module interconnection architecture encompasses two orthogonal structures: functional decomposition and layers. • The execution architecture describes the dynamic structure of a system.

• The code architecture describes how the source code, binaries, and libraries are organized in the development environment.

1.2 Refactoring „Refactoring is the process of changing a software system in such a way that it does not alter the external behaviour of the code yet improves its internal structure. It is a disciplined way to clean up code that minimizes the chances of introducing bugs. In essence when you refactor you are improving the design of the code after it has been written.“ [Fow99]. This classical definition of refactoring pertains to modification of source code, specifically it speaks of improving the design of a system (code) after it has been implemented (written). In this thesis, as discussed in the introduction, we focus on the refactoring of software architecture. Therefore we give a definition of refactoring solely in the context of refactoring software architecture and we define it to be: Software Architecture Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the system yet improves one (or more) of its quality attributes, such as maintainability, performance, security, etc.

1.3 Connectors and Components Software architecture consists of components and connectors. Components are connected to each other through connectors. As for software architecture, a multitude of definitions for components can be found. We’ll show an appropriate cross section here. 1. Clemens Szyperski, 1997 [Szy97] A software component is a unit of composition with contractually specified interfaces and explicit interaction protocols. A software component is indepently deployable and subject to composition by third parties. A binary unit of independent production, acquisition, and deployment that interacts to form a functioning system.

1.4. PROBLEM STATEMENT

17

2. Grady Booch, Software Components with Ada, 1987 [Boo87] A reusable software component is a logically cohesive, loosely coupled module that denotes a single abstraction. 3. Desmond D Souza, in Catalysis [DW98] A replaceable unit of development work which encapsulates design decisions and which will be composed with other components as part of a larger unit. A key property of components in the context of this thesis is reusability. This will be an objective requirement of our refactoring. As we will show, we can refactor architecture without changing the components. A connector is a module that connects components without performing any computation itself. By using different connectors, with the same interface specification, we are able to refactor without changing the component. As the component only needs to know how to communicate with the connector, the manner in which connectors communicate with each other is subject to change.

1.4 Problem statement The motivation for this thesis is to research different types of refactoring that can be defined at an architectural level. As mentioned in the introduction, the thesis will concentrate on a structure refactoring and on refactoring from one architecture style to another. Another question that will be addressed, is to what degree refactoring can be automated. Ideally if the refactoring process is sufficiently well formalized, an automated process should be possible. Which leads to the question: „how can refactorings be formalized?“ We want not only to be able to describe the refactoring itself in a sufficiently formal way, but also be able to verify the result, i.e. the original system and the refactored system are behaviourally equivalent. Additionally, this formalization adds to the understanding and classification of architectural styles.

1.5 Approach The refactorings mentioned will be examined using case-studies. As UML has become a de facto standard as a choice for design language, the design level refactoring will focus on refactoring UML models. This area of research is relatively new and although a significant amount of refactorings have been defined that operate on a source code level, few have been described that operate on UML models. One example of UML refactoring research is the paper written by Sunyé et al., describing several (primitive) UML refactoring operations [SPTJ01]. To prove behaviour preservation several methods can be considered [TM04]. One way is using preconditions expressed in first-order predicate logic. This is a rather conservative approach however that rules out many legal refactorings. Mens et al. suggest a graph transformation formalism, used to check certain aspects of behaviour preservation [MDJ02]. Another approach is to use type checking [TKB03]. All typed software entities should still have the same type after the refactoring. These

CHAPTER 1. INTRODUCTION

18

however all operate on source code for the most part. Since we will focus on refactoring UML models we will need to employ a different method to prove behaviour preservation. We will define the behaviour in the UML diagram using Statecharts. The statecharts are then mapped to corresponding CSP [Hoa85], a process algebra, after which it is possible to show that the original system and the refactored system are behaviourally equivalent. The architectural style refactorings use adapted elements of Darwin [MDEK95], an architecture description language (ADL) and CSP. Using FDR2 1 [Ros94], a model-checking tool that can be used to test wether certain properties hold in a CSP system, we can then show that the original system and the refactored system are behaviourally equivalent.

1.6 Outline of the thesis In this section we will give a short outline of the remainder of this thesis. In chapter two we will give a short introduction to CSP. This will be just a reference, only covering the elements of CSP used in this thesis, and not an introductory text. In chapter three we will discuss refactoring architectural design. We will show how to refactor a design that contains a „God-class“ anti-pattern to one with more delegation and structure. The behaviour of the design will be defined by statecharts which will be translated to CSP processes. Once we have defined the CSP processes we can use proof techniques to establish behaviour equivalence. In chapter four we will discuss refactoring architectural styles. We describe two refactorings, first from the Pipe and Filter style to the Blackboard style and second from the Client/Server style to the Blackboard style. For both refactorings we will use graphical representations and CSP processes. The CSP processes will again enable use to use formal methods to establish behaviour equivalence. Chapter five contains the conclusions and some remarks about future work and finally, the appendices contain the sources to the CSP M (Machine Readable CSP [Sca98]) files used by FDR2, and the corresponding Java implementations.

1

FDR2 is a product of Formal Systems (Europe) Limited, see also http://www.fsel.com.

Chapter 2

CSP 2.1 CSP This chapter will introduce the reader to some of the basics of CSP. Only those elements that are used in this thesis will be described. Also it will not be an in-depth treatment, but just an overview. For more information see [Hoa85, Ros94, Ros97] and the manual for FDR2. CSP is a process algebra. This means that it is a mathematical system to describe processes.

2.1.1 Processes In CSP processes consist of sequences of events. The occurence of each event should be regarded as instantaneous or atomic. Hoare defines a process to be an object with certain behaviour pattern, where the behaviour pattern is defined by a sequence of events. Each process will only engage in the events that are in its alphabet. The notation used to describe a process is the prefix notation: Let P be a process, we define P as follows: P=x→P This means that the process P will engage in the event x and then proceed as process P. By its nature this is a recursive definition. We denote the alphabet of P to be αP. We define some special purpose processes: STOP is the process that does not engage in any event. SKIP is the process that terminates successfully.

2.1.2 Traces „A trace of the behaviour of a process is a finite sequence of symbols recording the events in which the process has engaged up to some moment in time.“[Hoa85] 19

CHAPTER 2. CSP

20 hx, yi is a trace consisting of two elements, x followed by y. hi is the empty trace (no events). Concatenation of a trace s and t is defined as: sat For example: if s = ha, bi and t = hc, di, then s a t = ha, b, c, di.

2.1.3 Hiding or abstraction The hiding operator makes a subset of the events from the alphabet A of a given process P internal. This means these events are invisible to and beyond the control of the environment. This functions as an abstraction mechanism. Let B ⊆ A P0 = P\B Process P0 is the process P with all the events in the set B hidden. For example: let P = x → y → P, then A = {x, y}. Let B = {y}, a trace of the process P\B will then be 0 or more repititions of y: {y, y, ...}.

2.1.4 Composition Given two processes P and Q, we define A = αP ∩ αQ. Then the parallel composition these processes is written as: P kA Q This means that P and Q will engage both in any event e in their respective alphabets regardless of the state of the other process when e ∈ / A. However for the parallel composition of the processes to proceed when e ∈ A, both processes must be prepared to engage in the event e. The processes are said to synchronise on the elements in the alphabet A, and A is called the synchronisation alphabet. For example: let P = x → y → P and Q = y → Q, then the parallel composition P k {y} Q will first engage in the event x and then engage in the event y when both P and Q are prepared to engage in the event y.

2.1.5 Communication In the previous section we have discussed synchronisation between two processes. We will now introduce a special class of event called a communication. A communication is an event that is described by a pair c.v, where c is the name of the channel on which the communication takes place, and v is the value that is being communicated. Let message and channel be functions such that:

2.1. CSP

21

message(c.v) = v channel(c.v) = c A process that is initially prepared to write a value v to a channel c and then behaves like P is defined as: (c!v → P) = (c.v → P) A process which is initially prepared to read any value x from the channel c and then behaves like P(x) is defined as: (c?x → P(x)) = (y : {y | channel(y) = c} → P(message(y))) Example: read a value from the channel in and write the values times two to the out channel: in?x → out!(2 × x)

2.1.6 Choice Consider two processes P and Q, the process (x → P) 2 (y → Q) will behave as (x → P) if the event x occurs, and as (y → Q) if the event y occurs. Thus the choice for the process is determined externally. This type of choice operator is called external choice, and the process is said to be deterministic. A special form of this operator is the following notation: 2k∈A P(k) which is a shorthand notation for P(k0 ) 2 P(k1 ) 2 ... 2 P(kn ) where ki ∈ A, and P(k) is a process with parameter k. The process (x → P) u (y → Q) Will behave either as (x → P) or as (y → Q), but the choice is made internally, the environment has no control over it. This type of choice operator is called internal choice, and the process is said to be nondeterministic.

CHAPTER 2. CSP

22

2.1.7 If-then-else We will use special construct to implement and if-then-else. We define the process P| Q This is equivalent to if (expr == true) then P else Q In other words, if expr evaluates to true the process continues as process P else it continues as process Q.

2.1.8 Refinement and equality A process Q is said to be a refinement of a process P if it satisfies at least the same conditions as process P. We distinguish three models of refinement. Traces refinement A process Q is a traces refinement of a process P if all the possible sequences of events which Q can do are also possible for P. P vT Q ≡ traces(Q) ⊆ traces(P)

(2.1)

Failures refinement A process Q is a failures refinement of a process Q if it not only can perform all the communications that P can perform, but also block all the events which P blocks. A failure is a pair (s, X), where s is a trace of the process and X is a set of events the process can refuse to perform at that point, failures is the set of all possible failures for a process. We can now define failures refinement as follows: P vF Q ≡ failures(Q) ⊆ failures(P)

(2.2)

Failures-Divergences refinement This model adds livelock (i.e. perform an infinite sequence of internal events) to the refinement. When a process livelocks it will not engage in an externally visible event. The failures-divergences model allows to detect this state by adding the concept of divergences. The divergences of a process are the traces after which a process may livelock. P vFD Q ≡ divergences(Q) ⊆ divergences(P) ∧ failures(Q) ⊆ failures(P)

(2.3)

Equality Two processes that can be shown to refine both ways, are said to be equivalent. Note that the equivalence only pertains to the abstraction used (the events that are not hidden). P = Q ≡ P vFD Q ∧ Q vFD P

(2.4)

In the remainder of this thesis when we speak of equality of processes resulting from proving FailuresDivergences refinement both ways, we mean equality related to the Failures-Divergences model.

Chapter 3

Refactoring Architectural Design In this chapter we present a case-study of a refactoring that achieves a more equal distribution of responsabilites. The program that our case-study is based on is SAAT (Software Architecture Analysis Tool) [MCL04]. SAAT is a tool that can be used to calculate metrics about UML models. These metrics can then be used in analysing the model for potential flaws or anti-patterns. As an exercise the SAAT tool was used to calculate metrics about the SAAT tool itself. The UML class-diagram for SAAT was initially designed as depicted in Figure 3.1. The associations indicate a call relationship, i.e. if module A has an association with module B, a method of module A will call a method in module B. SAAT calls the methods in the associated modules sequentially. First the database is created (DBCreate.create()), the (XMI) input file is parsed (Parser.parse()) and the parsed data is inserted into the database (DBFill.fill()). After the data is inserted first it is checked (DBCheck.check()), after that the data is analysed (Analyse.analyse()), statistics are calculated (StatCalc.calculate()) and the resulting statistics are filtered according to user defined criteria. After evaluating the architecture with SAAT the following metrics were found, see Table 3.1. From these metrics it was concluded that the SAAT module has characteristics of a so called “God-class” [SW00]. A „God-class“, also known as “The Blob”, is an anti-pattern. It distinguishes itself by having a single class with many attributes and/or operations. It can be characterized as a Controller class with simple, data-object classes and it lacks object oriented design. In this case we have a „Controller class“. The SAAT class sequentially calls a number of methods (services) in other classes. A „God-class“ typically has a negative impact on modifiability, maintainability and performance. The design is refactored by introducing two new classes, DB and Statistics. The database operations (DBCreate.create(), Parser.parse(), DBFill.fill() and DBCheck.check()) will be delegated to the DB class. The statistics operations (StatCalc.calculate() and StatFilter.filter()) will be delegated to the Statistics class. The refactoring used here is similar to „Convert Procedural Design to Objects“, see [Fow99]. 23

CHAPTER 3. REFACTORING ARCHITECTURAL DESIGN

24

Figure 3.1: SAAT Class diagram before refactoring

StatFilter Saat + filter ():void + execute ():void StatCalc

DBCreate

+ calculate ():void

+ create ():void Analyse

Parser

+ parse ():void

DBFill

+ fill ():void

+ analyse ():void DBCheck

+ check ():void

Table 3.1: Metrics for the SAAT architecture Modules SAAT

Coupling before refactoring 7

DBCreate Parser DBFill DBCheck Analyse

0 0 0 0 0

StatCalc StatFilter

0 0

25 The modified architecture is shown in Figure 3.2, the metrics for the modified architecture are shown in Table 3.2. From these metrics we conclude that the architecture was indeed improved. In this section we have described a refactoring for an architecture expressed using UML. The following sections will introduce behaviour for the original architecture and the refactored architecture and will show that they have equivalent behaviour.

Figure 3.2: SAAT class diagram after refactoring

Saat

+ execute ():void

DB

Statistics

Analyse

+ execute ():void

+ execute ():void

+ analyse ():void

DBCreate

DBCheck

StatCalc

StatFilter

DBFill + create ():void

+ check ():void

+ calculate ():void

+ fill ():void

Parser

+ parse ():void

Table 3.2: Metrics for the SAAT architecture after refactoring Modules SAAT DB DBCreate Parser DBFill DBCheck Analyse Statistics StatCalc StatFilter

Coupling after refactoring 3 3 0 0 1 0 0 2 0 0

+ filter ():void

26

CHAPTER 3. REFACTORING ARCHITECTURAL DESIGN

3.1 Statechart refactoring In order to be able to reason about the behaviour it first needs to be defined. Unfortunately, there is no generally accepted formal semantics for UML, and thus no way to define behaviour formally in UML alone. However if we first define the behaviour using statecharts and map these to CSP processes, we will have a formal representation of the behaviour. If statecharts are to be mapped to CSP, they are subject to certain restrictions: 1. Every class in the UML model will have its own statechart, defining its behaviour. 2. Method calls on one object from another will be modelled by „Call Events“. Return from the method call is modelled by using a signal event in a systematic way. 3. Execution of the body of the method is implied by the semantics of the Call Event [Gro03]. If so desired the behaviour of the method being called can also be defined using a statechart. We will not do so in this example. We will illustrate modeling the method call with an example. Say class A wants to execute a method m() in class B, the statechart for class A will send the event m() after which it will wait for the return event m r (the return event), the initial transition in the statechart for class B will wait for m() to happen, upon reception of this event, the body of the method will execute, do whatever else is needed and then send m r. Finally the statechart for class B will wait for the call event again. Using the heuristics described above we construct a statechart defining the behaviour for the SAAT module before the refactoring, as can be seen in Figure 3.3, and the statechart for one of the submodules (DBCreate) in Figure 3.4. All other submodules have a similar structure. The notation for the statecharts is as follows: • Object.method() is used to indicate a call event. A transition from one state to another waits for the corresponding event. The semantics are that the transition is taken and the corresponding method is executed. • The notation ^signal name is used to transmit an event by name. In the form ^Object.method() it can be used to transmit a call event. • The format for a transition label is as follows:

event name[guard expression]/action1; action2; ...;^event1,event2,...

The transition waits for the event with the name “event name”. If the event occurs and the guard expression evaluates to true, the transition is taken, the actions are executed, and the events are transmitted. After applying the refactoring, two classes have been introduced, and therefore we need two new statecharts: one for the class DB, see Figure 3.6 and one for the class Statistics, see Figure 3.7. The method DB.process() will be called from SAAT, and the DB class will then call the methods from the classes whose associations have been moved to the DB class. The statechart for the Statistics class is implemented in a similar manner. Because the calling of several methods has been delegated to

3.1. STATECHART REFACTORING

27

Figure 3.3: Statechart for SAAT before refactoring

/ ^DBCreate.create()

Waiting for Create

/ ^Parser.parse()

Database Created

Waiting for Parse

DBCreate.create_r Initial State Parser.parse_r

Waiting for DB Check

DBFill.fill_r

Database filled

Waiting for Fill Database

/ ^DBCheck.check()

Parsed / ^DBFill.fill()

DBCheck.check_r

Database Checked

/ ^Analyse.analyse()

Waiting for Analyse

Analysed

/ ^StatCalc.calculate()

Waiting for Calc. Stat.

Analyse.analyse_r

StatCalc.calculate_r

StatFilter.filter_r

Waiting for Stat. Filter

Statistics Calculated / ^StatFilter.filter()

Final State

Figure 3.4: Statechart for DBCreate

create()

Created Initial State

/ ^DBCreate.create_r

Figure 3.5: Statechart of SAAT after refactoring

/ ^DB.process()

Waiting for Data processing

Data Processed DB.process_r

/ ^Analyse.analyse()

Initial State Waiting for Analyse

Statistics.process_r Final State

Waiting for Statistics

Analysed

/ ^Statistics.process()

Analyse.analyse_r

CHAPTER 3. REFACTORING ARCHITECTURAL DESIGN

28

Figure 3.6: Statechart of DB, introduced after refactoring

process()

Processing

Initial State

/ ^DB.process_r

/ ^DBCreate.create()

DB Checked

Waiting for Create

/ ^Parser.parse()

Database Created

Waiting for Parse

DBCreate.create_r

DBCheck.check_r

Parser.parse_r

Waiting for DB Check

Database filled

DBFill.fill_r

Waiting for Fill Database

/ ^DBCheck.check()

Parsed / ^DBFill.fill()

Figure 3.7: Statechart of Statistics, introduced after refactoring

process()

Processing

/ ^StatCalc.calculate()

Waiting for Stat. Calc.

Initial State StatCalc.calculate_r

Stat. Calculated Final State

/ ^Statistics.process_r / ^StatFilter.filter() Stat. Filtered

StatFilter.filter_r

Waiting for Stat. Filtering

3.2. MAPPING STATECHARTS TO CSP

29

the DB and Statistics classes respectively, we also need to change the statechart corresponding to the SAAT class. See Figure 3.5 for the refactored statechart corresponding to the SAAT class. The statecharts will now be mapped to CSP processes. This will enable us to show that the two processes, representing the overall behaviour of the architecture before and after the refactoring, are identical in the method body executions and the order thereof. We can do so by comparing the processes using FDR2 while hiding all events, except the events representing the method calls themselves.

3.2 Mapping statecharts to CSP We will now show how to map the statecharts to corresponding CSP constructs. Module A has a (call method) association with Module B. The behaviour of Module A and Module B is defined by processes P and Q. Assume that process P will call method m in Process Q. Name the request channel q, name the return channel q r. Execution of the method m is indicated by the occurence of an event named m(). Recall that in CSP, events are considered to be atomic. As a result, the foregoing assumes that any other event either occurs before or after the event m(). This is fine in the present context where concurrency is not being considered. However, if we needed to model the possible concurrent execution of two or more methods, then m() could be replaced by two sequential events such as StartExecutionm() → FinishExecutionm() . This would allow for the start and finish events to be arbitrarily interleaved with other events, thus modeling concurrency. In the interest of clarity we will use the event m() to indicate execution of the method m. Let P be a process that sends a message to request execution of method m, waits for the method to finish executing by waiting for a return value and then proceeds. P = (q!m → q r?x → . . . → P) Let Q be a process that waits for a request to execute method m, executes it and waits again. Q = (q?x → m() → q r!m → Q) Further αq(P) = αq(Q) = αq r(P) = αq r(Q) = Σ = {m} For the events q?x and q r?x, x must be such that x ∈ Σ. Generally, therefore, the receiver can anticipate receiving any one of the events that are in X. However, in the present context where X is a singleton set, the receiver can rely on the message being the only member of the set.

CHAPTER 3. REFACTORING ARCHITECTURAL DESIGN

30

Table 3.3: Channels for SAAT CSP processes before refactoring Module name SAAT DBCreate Parser DBFill DBCheck Analyse StatGen StatFilter

Process name SAAT DBCreate Parser DBFill DBCheck Analyse StatGen StatFilter

Call request channel n.a. cr p f ch a sg sf

Call return channel n.a. cr r p r f r ch r a r sg r sf r

3.2.1 Mapping SAAT before refactoring to CSP In the case of SAAT the mapping to CSP is as follows: We define channels for sending a call request message and for sending a return from call message. For the modules the following channels are defined. We keep the naming short in order to keep the definitions manageable, see Table 3.3. This gives us the following process definitions. It should be clear that the CSP processes correspond to the statecharts shown earlier. We do not need a check for the correct message at the receiving end, as per definition of the alphabet for the channels, only one message can be sent. = (cr!create → cr r?x → p!parse → p r?x → f !fill → f r?x → ch!check → ch r?x → a!analyse → a r?x → sg!generate → sg r?x → sf !filter → sf ?x → SKIP) DBCreate = (cr?x → create() → cr r!create → DBCreate) Parser = (p?x → parse() → p r!parse → Parser) DBFill = (f ?x → fill() → f r!fill → DBFill) DBCheck = (ch?x → check() → ch r!check → DBCheck) Analyse = (a?x → analyse() → a r!analyse → Analyse) StatGen = (sg?x → generate() → sg r!generate → StatGen) StatFilter = (sf ?x → filter() → sf r!filter → StatFilter) Saat

The process S models the complete execution of the system as the concurrent composition of the processes mentioned above: S = Saat kSync (DBCreate ||| Parser ||| DBFill ||| DBCheck ||| Analyse ||| StatGen ||| StatFilter) where Sync = {cr, cr r, p, p r, f , f r, ch, ch r, a, a r, sg, sg r, sf , sf r}.

3.2. MAPPING STATECHARTS TO CSP

31

3.2.2 Mapping SAAT after refactoring to CSP The refactoring introduces two new classes, DB and Statistics, for which the channels and processes are named as follows: Module name DB Statistics

Process name DB Stat

Call request channel db st

Call return channel db r st r

The processes for the refactored model are defined as follows. Only the modified or added processes are shown. The others have not changed. Saat0 = (db!process → db r?x → a!analyse → a r?x → st!process → st r?x → SKIP) DB = (db?x → cr!create → cr r?x → p!parse → p r?x → f !fill → f r?x → ch!check → ch r?x → db r!process → DB) Stat = (st?x → sg!generate → sg r?x → sf !filter → sf r?x → st r!process → Stat) The process S0 models the complete execution of the refactored system as the concurrent composition of the aforementioned processes and the two new ones: S0 = Saat0 k{db,db r,a,a r,st,st r} ((DB kSync (DBCreate ||| Parser ||| DBFill ||| DBCheck)) ||| Analyse ||| (Stat k{sg,sg r,sf ,sf r} (StatGen ||| StatFilter))) where Sync = {cr, cr r, p, p r, f , f r, ch, ch r}.

Note that the introduction of the class DB, and Statistics and the corresponding DB.process(), and Stat.process() methods does not lead to the occurence of the corresponding method execution events in the CSP definition. This is because the execution of those methods is explicitly modelled by the events to execute the methods in the associated objects.

3.2.3 Behaviour To establish behaviour equivalence, we hide all events except the function application events. The processes shown above are then translated to CSP M . We can now determine refinement in the Failuresdivergences model, between the two processes (S and S 0 ) in both directions, using FDR2. Translating these definitions to CSP M , see appendix B.1, and evaluating them using FDR2, gave the following result: S vFD S0 ∧ S0 vFD S and therefore S = S0

32

CHAPTER 3. REFACTORING ARCHITECTURAL DESIGN

Chapter 4

Refactoring Architectural Style In this chapter we will describe refactorings between different architectural styles. To illustrate the method we will use the well known KWIC (KeyWord In Context) example, introduced by Parnas [Par72] and used to illustrate software design in different software architecture styles by Garlan and Shaw [GS94]. It is on this last paper that we will build our examples. We will describe two refactorings, one from the Pipe and Filter style to the Blackboard style and one from the Client/Server style to the Blackboard style. In both cases we will start with a generic description of the styles and then show the specific version for the KWIC case. First we will describe the KWIC case.

4.1 KWIC KWIC is an system that takes sentences as input, generates all possible different variations of the sentence with the last word in the sentence moved to the front of the sentence and then sorts them. An example will demonstrate this process more clearly. Take the sentence „Refactoring Architectural Style“. This is not a sentence in the grammatical sense, but it will do as an example. The possible word-shifted variations are: Refactoring Architectural Style Style Refactoring Architectural Architectural Style Refactoring Sorted this becomes: Architectural Style Refactoring Refactoring Architectural Style Style Refactoring Architectural 33

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

34

Which is the output of the KWIC process. We decompose the system in four different components: • Input Reads sentences from an input source, makes sure that there are no trailing or leading spaces, lines are terminated consistently, etc. • Shifter This component reads an input sentence and generates the word-shifted variations of it. As shown in the example above. • Alpha The alphabetizer component. This component takes care of sorting the shifted sentences • Output Finally the last component makes sure that the output is sent to the output medium.

4.2 A few words on flow and Figures A few words of explanation may be needed for the notation used in the figures in this chapter, and the concepts behind them. The figures represent software architectures in a specific style, composed of components and connectors. The components and connectors exchange messages. These messages will be subdivided in one of three categories: 1. Data flow The message contains only data that the application operates upon. 2. Control flow The message contains control information. If component A sends a control message to component B, it means that component A sends information to component B that determines how component B will continue to operate. 3. Reference flow The message contains information about the destination(s) and source of the message. Components are drawn as rectangular boxes. Connectors are drawn as boxes with round corners. The arrows connecting the components and connectors have different meanings, depending on the type, see Figure 4.1. We will now describe the KWIC case in the different architectural styles and show how to refactor from one style to another.

4.3. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD

35

Figure 4.1: Description of conventions used in Figures

Data flow

Component

Data flow (I/O) Control flow Data + Control + Reference flow (or Data + Control)

Connector

Figure 4.2: Pipe and Filter architecture style Data flow Input Medium

Output Medium

System I/O

V1

V4

in

out

Filter F1

out

in

Connector C1

out

V2

in

Filter F2

V3

4.3 Refactoring from Pipe and Filter to Blackboard 4.3.1 Pipe and Filter A Pipe and Filter style architecture consists of filters connected through pipes. A filter is a module that reads data on its input port, modifies it and writes the modified data to its output port. The filters are connected via pipes. A pipe is a uni-directional channel between a source and a sink, in this context a pipe is a connector that connects two filters, and a filter is a component. The pipe and filter style can be depicted as shown in Figure 4.2. A connector abstracts the data coupling between the filters. As we will see, it will also enable us later to refactor more easily to a Blackboard style, as we will only have to adapt the connectors and not the filters. 4.3.1.1

Filter

The filter component reads data from its input channel, processes it, performs some internal computation (i.e. executes a method f()) on the data read, and then outputs the modified data on its output channel.

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

36 4.3.1.2

Connector

The connector is the pipe between filters. It accepts data on its input channel, buffers it and outputs data on its output channel. 4.3.1.3

CSP model of the generic Pipe and Filter style

We borrow a notation from Darwin [MDEK95], an ADL (Architecture Description Language), to define the coupling of a and connector. This type of binding is called exogenous binding and enables flexible composition of components and connectors. We extend this notation with an assignment a name for the connection. The channel name will later be used in constructing the CSP processes and makes the connection a first class citizen. Filters F1 and F2, and a connector C1 are composed by: Filter Connector F1.in F1.out F2.in F2.out

F1, F2 C1 − − IN.out − − C2.in − − C2.out − − OUT.in

= V1 = V2 = V3 = V4

This defines two filters F1 and F2, and one connector C1. IN and OUT are the I/O processes connecting this system to the „outside“. A schema of this structure is depicted in Figure 4.2. Filter

We assume that the data manipulation is performed by some function f on input x. Filter = in?x → out!f (x) → Filter

For which we require that f (eof ) = eof . Connector The coupling between filters is performed by a connector. The CSP definition of a generic connector is: Connector = Chi Chi = in?x → Chxi Ca = in?x → C a a | C 0 a c hsi c hsi x c hsi 0 = SKIP Chi 0 C0 a = out!c → Chsi c

hsi

System I/O

IN and OUT are system I/O processes that respectively output data and receive data.

IN = out!x → IN OUT = in?x → OUT

4.3. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD

37

Figure 4.3: Sequence diagram showing a message exchange for the Pipe and Filter Architecture Input Medium

Filter F1

Connector C1

Filter F2

Output Medium

: send data to Filter F1 : Send filtered data to C1 : Forward data : Send filtered data to Output Medium

Process The resulting process for our example system, can be defined as follows (leaving out the synchronisation alphabets for more clarity): System = IN k F1 k C1 k F2 k OUT See Figure 4.3 for a sequence diagram showing a message exchange. The exchange is a scenario where data is read from the Input Medium, processed by F1, F2 and then sent to the Output Medium.

4.3.1.4

KWIC as instance of the generic Pipe and Filter Architecture

The Pipe and Filter version of the KWIC system can now be defined as an instance of the generic Pipe and Filter architecture style as follows (see also Figure 4.4), this time taking into account the synchronisation alphabets: Filter Connector Input.in Input.out Shifter.in Shifter.out Alpha.in Alpha.out Output.in Output.out

Input, Shifter, Alpha, Output IN, IN CS, CS AL, AL OU, OUT − − IN.out − − IN CS.in − − IN CS.out − − CS AL.in − − CS AL.out − − AL OU.in − − AL OU.out − − OUT

KWIC = IN k{V1} Input k{applyInput} IN CS k{V2} Shifter k{applyShift} CS AL k{V3} Alpha k{applyAlpha} AL OU k{V4} Output k{applyOutput} Output

= V1 = applyOutput = V2 = applyShift = V3 = applyAlpha = V4 = applyOutput

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

38

Figure 4.4: KWIC in Pipe and Filter style Input Medium

Output Medium

in

out

Input

C1

Circular Shifter

C2

Alphabetizer

C3

Output

Legenda

Data flow Connector:

in

out

Filter:

in

out

System I/O

4.3.2 Blackboard In this section we will describe the generic Blackboard architecture style and the CSP definition of the style. Next we will show the refactoring of the Pipe and Filter KWIC instance to a Blackboard KWIC instance. A Blackboard architecture consists of Knowledge sources (the components), a Blackboard and a Controller. When we will refactor the Pipe and Filter style KWIC to a Blackboard KWIC, the components (Filters in the Pipe and Filter style) will remain exactly the same. The only thing we will need to modify are the connectors. In the Blackboard style the connectors are connected to the components on one hand and to the Blackboard and the Controller on the other hand. The Blackboard style can be depicted as shown in Figure 4.5. We use the concepts of filter and connector in this style as well. Conceptually the connectors have remained the same. The most important difference here is the fact that the connector is now placed between the filter and the blackboard, instead of directly between two filters. However, from the point of view of the filter, it is still only communicating with a connector. It has no knowledge of the presence of the Blackboard.

4.3.2.1

CSP model of the generic Blackboard style

For a Blackboard architecture as given in Figure 4.5, we will define it using the adapted Darwin notation as follows (analogous to the Pipe and Filter example)

4.3. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD

39

Figure 4.5: Blackboard Style Input Medium

Output Medium Data flow System I/O

in

Control flow

Filter F1

out Filter F2

out

in

in

out

Connector C1

Controller

Connector C2

out

in

in

out Black Board

Filter Connector Blackboard F1.in C1.out C2.in F2.out

F1, F2 C1, C2 BB − − IN.out − − BB.in − − BB.out − − C2.in

= V1 = V2 = V3 = V4

As the interface for the filters has remained the same, and the controller of the Blackboard controls the connectors, the filters are identical to the ones used in the Pipe and Filter architecture! We define the Blackboard as a parallel composition of the following processes (without synchronisation): System = Blackboard k F1 k F2 k IN k OUT where IN and OUT are system I/O processes that respectively output data and receive data (these are the same processes that were used in the Pipe and Filter refactoring): IN = out!x → IN OUT = in?x → OUT

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

40

F1 and F2 are filters, and Blackboard is defined as: Blackboard = Controller k C1 k C2 k BB The controller controls the operation of the connectors. The connectors can only proceed if the controller cooperates. It does this by reading a value on an input channel connected to the connector. The example below is already geared towards the functionality of the controller needed for the Pipe and Filter refactoring. A generic controller is hard to define, since it very much depends on the system the controller is used for. We can however say that any controller used in the Blackboard will need to employ some kind of control mechanism for the connectors. We have chosen to use a control channel which makes use of the synchronisation semantics of CSP (see also section 2.1.4). Controller = c1?x → Controller| Controller 0 Controller 0 = c2?x → Controller 0 | X The connectors read from their input channel, write the value to the control channel and then to the output channel. Only if the controller reads from its control channel will the connector be able to proceed.: C = in?x → ctrl!x → out!x → C The filters F1, F2 are identical to the ones in the Pipe and Filter architecture, so we will not repeat their definition here. The Blackboard storage process is defined as follows: BBhi BB a s



= in?x → BBhxi = in?x → BB a a hxi

s

hci

2 out!c → BBs Multiple Blackboard storage processes must be defined as the number of connectors increases. Consider when there are more than 2 filters (and thus more than 2 connectors), one connector will read from the Blackboard while the other will write to it. If the data is being read from and written to the same Blackboard process, the eof event might never occur and the process will processing the same data over and over again. To solve this case the controller will also have to be adapted to let the connectors read and write in turn, but also allow multiple output events for a single input event: Define Connectors C1, C2, C3, C4 and Filters F1, F2, F3. See also Figure 4.6 Controller

= c1?x → Controller| Controller1

= (c2?x → Controller1| Controller1 c3) 2 (c3?x → Controller1| Controller1 c2) Controller1 c2 = c2?x → Controller1 c2| Controller2 Controller1 c3 = c3?x → Controller1 c3| Controller2

Controller1

Controller2

= c4?x → Controller2| X

4.3. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD

41

Figure 4.6: Blackboard style Data flow System I/O

Input Medium

Output Medium

Control flow

in

out

F1

F3

F2

out

in

out

in

in

out

in

out

C2

C1

C4

C3

out

in

out

in

in

out

in

out

Black Board

c1

c2

c3

Controller

c4

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

42

We read alternatively on channels c2 and c3 until on both channels eof has been read, then we proceed to the next pair of connectors, or the final connector. The channels c1, c2, c3, c4 serve as control channels, giving the controller the ability to control the progress of execution of the corresponding connectors. The direction of the control channel arrows signifies the „direction“ of control, not the direction of the data flow (which travels in the opposite direction). See Figure 4.7 for a sequence diagram of a message exchange of a simple Pipe and Filter architecture.

4.3.2.2

KWIC as instance of the generic Blackboard Architecture

The KWIC system can now be defined as an instance of the generic Blackboard architecture as follows (see also Figure 4.8.), taking the synchronisation alphabets into account: KWIC0 = IN k{chIN Input} Input k{applyInput} IN BB k{chIN BB} BB1 k{chBB BBCS} BB CS k{chBBCS Shifter} Shifter k{applyShift} CS BB k{chCSBB BB} BB2 k{chBB BBAL} BB AL k{chBBAL Alpha} Alpha k{applyAlpha} AL BB k{chALBB BB} BB3 k{chBB BBOU} BB OU k{chBBOU Output} Output k{applyOutput} OUT k{in bb,bb cs,cs bb,bb al,al bb,bb ou} Controller where

Controller Controller1 Controller2 Controller3

= = = =

in bb?x → Controller| Controller1 bb cs?x → cs bb?x → Controller1| Controller2 bb al?x → al bb?x → Controller2| Controller3 bb ou?x → Controller3| X

For the Controller we define several control channels, in the figure these are all connected to the same Controller module. These channels will be regarded to be accessible by all Controller processes (Controller, Controller1, Controller2, Controller3). BB and the connectors IN BB, BB CS, CS BB, BB AL, AL BB, BB OU are the same as defined above.

Connector C1

Blackboard

Controller

Connector C2

Filter F2

Output Medium

: Send data to F1 : Send filtered data to C1 : Put data on BB : send notification to controller that data is ready : Instruct C2 to pick up data : Forward data to F2 : Send filtered data to Output Medium

4.3. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD

Filter F1

43

Figure 4.7: Sequence diagram showing a message exchange for the Pipe and Filter architecture (see also Figure 4.5)

Input Medium

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

44 Filter Connector

Input, Shifter, Alpha, Output IN, IN BB, BB CS, CS BB, BB AL, AL BB, BB OU, OUT BB BB1, BB2, BB3 Controller Ctrl Input.in − − IN Input.out − − IN BB.in BB1.in − − IN BB.out BB1.out − − BB CS.in Shifter.in − − BB CS.out Shifter.out − − CS BB.in BB2.in − − CS BB.out BB2.out − − BB AL.in Alpha.in − − BB AL.out Alpha.out − − AL BB.in BB3.in − − AL BB.out BB3.out − − BB OU.in Output.in − − BB OU.out Output.out − − OUT Ctrl.in bb − − IN BB.ctrl Ctrl.bb cs − − BB CS.ctrl Ctrl.cs bb − − CS BB.ctrl Ctrl.bb al − − BB AL.ctrl Ctrl.al bb − − AL BB.ctrl Ctrl.bb ou − − BB OU.ctrl

= chIN Input = applyInput = chIN BB = chBB BBCS = chBBCS Shifter = applyShift = chCSBB BB = chBB BBAL = chBBAL Alpha = applyAlpha = chALBB BB = chBB BBOU = chBBOU Output = applyOutput = in bb = bb cs = cs bb = bb al = al bb = bb ou

4.3.3 Behaviour To establish behaviour equivalence, we hide all events except the function application events. The processes shown above are then translated to CSP M (Machine Readable CSP, see [Sca98]). We can now determine refinement in the Failures-divergences model, between the two KWIC processes (KWIC and KWIC0 ) in both directions, using FDR2. See appendix B.2 for the CSP sources file used as input for FDR. KWIC vFD KWIC0 ∧ KWIC0 vFD KWIC and therefore KWIC = KWIC 0 From this result we conclude that the refactoring preserved the relevant behaviour.

4.4 Refactoring from Client/Server to Blackboard In this section we will study our second Software Architecture style refactoring. We will describe the Client/Server Architecture style and add connectors. With the connectors in place we will show that

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD

45

Figure 4.8: KWIC system in Blackboard style. Data flow Input Medium

Output Medium

System I/O Control flow

in

out Circular Shifter

Input

Alphabetizer

Output

out

in

out

in

out

in

in

out

in

out

in

out

IN_BB

BB_CS

CS_BB

BB_AL

AL_BB

BB_OU

out

in

out

in

out

in

in

out

in

out

in

out

Black Board

in_bb

bb_cs

cs_bb

bb_al

Controller

al_bb

bb_ou

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

46

Figure 4.9: Typical Client/Server architecture out2 Client 1

Client 2 in2

in1

out1

out

in

in

out

out2

in2

in1 Server 1

Server 2 out1

Control + Data

Filter (client or server)

it is possible to refactor this architecture to a Blackboard style architecture. Finally, using CSP and formal methods we will be able to show that the refactoring preserves behaviour. Analogous to our previous case, we will also regard the client/server architecture to be composed of filters (components) and connectors. The clients and servers will function as filters (components). The external behaviour of the connectors complies with the standard interface, specified in the Pipe and Filter case. Connectors must have more than two ports in this case, contrary to the connectors used in the Pipe and Filter refactoring, where by the nature of the architecture, connectors can suffice with two ports.

4.4.1 Client/Server A client/server architecture consists of clients and servers. A client sends a request to a server, which processes it and sends back a response. It is typical for clients to connect to more than one server, and it is also typical that servers have more than one client connect to them. A simplified client/server architecture can be seen in Figure 4.9. Note that in this figure no reference data is involved. Since we use separate channels for every connection, the reference is implied, and we do not need to add it. If we wish to be able to refactor this architecture to a Blackboard style architecture we need to find a way to separate the connection to the servers from the components. We can achieve this by introducing connectors in this architecture as follows. A connector serves as an abstraction for the connection between the client and the server. The client sends the request to the connector which then sends it to the connector connected to the receiving

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD

47

server. The server processes the request and sends the response to its connector, which sends it on the connector corresponding to the originating client, which, finally, delivers the response to the client. As we will see later on, this will also enable us to refactor this architecture into a Blackboard without the clients and/or server knowing about this. The same, simple, Client/Server architecture redesigned with connectors can be seen in Figure 4.10. Note that in this figure we do have reference flow. The clients are no longer connected to the servers through separate channels, but have only one channel (bi-directional) to the connector. If the message is to arrive at the server and find its way back, it will have to contain reference information. 4.4.1.1

CSP model of the Client/Server architecture

The Client/Server architecture with connectors delegates the message delivery to the connectors. In order to be able to do so, the connector will need to be told where to deliver the message. On the other end, the server also communicates with a connector, which will also need to know where to send the reply from the server. Therefore, the message sent contains both data and reference information. We define the type Message as follows: Message = {data, src, dest} Let req, res be of type Message, we can now define the connector based Client/Server architecture as follows: = IN in?x → (out!(x.client.server1) → in?(x.s.d) → OUT out!x → Client) u (out!(x.client.server2) → in?(x.s.d) → OUT out!x → Client) Server = in?req → out!f (req) → Server

Client

CCON1 = c in?req → if req.dest = S1 then (out1!req → CCON1) else if req.dest = S2 then (out2!req → CCON1) else CCON1 2 in1?res → c out!res → CCON1 2 in2?res → c out!res → CCON1 CCON2 = c in?req → out!req → CCON2 2 in?res → c out!res → CCON2 SCON1 = in?req → s out!req → SCON1 2 s in?res → out!res → SCON1

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

48

Figure 4.10: Client/Server architecture using connectors.

Client 1 in

Client 2 out

c_out

c_in

in

c_out

out2

CCON1 in1

in

in2

in

in2

SCON2 s_out

out

out

out2

in1

SCON1 s_in

c_in CCON2

out1

out

out

out1

in

out

Server 1

Connector

Client or Server

s_in

s_out

in

Server 2

Control + Data + Reference

SCON2 = (in1?req | in2?req) → s out!req → SCON2 2 s in?req → if r.src = C1 then (out1!req → SCON2) else if r.src = C2 then (out2!req → SCON2) else SCON2

The data, reference and control messages are sent over the same connection in this design.

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD Client Server ClientConnector ServerConnector Client1.out Client1.in Ccon1.in1 Ccon1.out1 Ccon1.in2 Ccon1.out2 Server1.in Server1.out Client2.out Client2.in Ccon2.in Ccon2.out Server2.in Server2.out

49

Client1, Client2 Server1, Server2 Ccon1, Ccon2 Scon1, Scon2 − − Ccon1.c in = V0 − − Ccon1.c out = V1 − − Scon1.out = V2 − − Scon1.in = V3 − − Scon2.out1 = V4 − − Scon2.in1 = V5 − − Scon1.s out = V6 − − Scon1.s in = V7 − − Ccon2.c in = V8 − − Ccon2.c out = V9 − − Scon2.out2 = V10 − − Scon2.in2 = V11 − − Scon2.s out = V12 − − Scon2.s in = V13

The specification above defines the following CSP system (split into subprocesses which are combined into the final system to make the specification more readable): Client1Proc Client2Proc Server1Proc Server2Proc System

= = = = =

Client1 k{V0,V1} Ccon1 Client2 k{V8,V9} Ccon2 Server1 k {V6,V7} Scon1 Server2 k {V12,V13} Scon2 (Client1Proc k{V2,V3} Server1Proc) k{V4,V5} (Client2Proc k{V10,V11} Server2Proc)

To illustrate the message exchange we have added a sequence diagram showing the exchanges for a message request from Client1 to Server1, see Figure 4.11. 4.4.1.2

KWIC instance of the generic Client/Server Architecture

In this section we are going to describe KWIC in the Client/Server architecture. In order to get a better separation of concerns, the client should not know about which servers to send requests to and in which order, to be able to compute the result. This kind of knowledge should find its place in a business logic tier. We will therefore introduce an extra tier for the KWIC case in the form of the KWICBL component. The client simply sends a message to KWICBL, requesting it to compute the KWIC output for the input contained in the data of the message. KWICBL then performs the necessary message exchanges with the other servers in order to get the result. Applying our methodology to the KWIC case gives the following design, see Figure 4.12

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

50

Figure 4.11: Sequence diagram showing a message exchange where Client1 request a response from Server1. Client1

CCON1

SCON1

Server1

: Request for data from Server1 : Forward request : Forward request : Send response to Client1 : Forward response : Send response to Client1

Figure 4.12: KWIC in Client/Server style Control + Data + Reference

Connector IN

OUT

out

Client or Server

in

IN_in

OUT_out

Client1

from.input

in

out

out

in Ccon1

s_in

to.input

to.shifter

from.shifter s_out

to.c1

from.c1

to.alpha

from.kwicbl from.alpha KWICBL

Scon_KWIC

S c o n 1

S c o n 2

S c o n 3

out

in Input

in

out

out

in

in

out

out

in

Shifter

Alpha in

out

out

in

to.kwicbl to.output

from.output

S c o n 4

Output in

out

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD First we define the generic CSP processes. = IN in?x → out!(x.c1.kwicbl) → in?(x.s.d) → OUT out!x → Client1 ClientConnector = in?req → s out!req → s in?res → out!res → ClientConnector Node = {c1, kwicbl, input, shifter, alpha, output} Client1

ServerConnectorKWIC = 2i∈Node (from.i?(x.s.d) → to.d!(x.s.d) → ServerConnectorKWIC) ServerConnector(i) = to.i?req → out!req → ServerConnector(i) 2 in?req → from.i!res → ServerConnector(i) KWICBL = to.kwicbl?(x.s.d) → from.kwicbl!(x.kwicbl.input) → to.kwicbl?(x.s1.d1) → from.kwicbl!(x.kwicbl.shifter) → to.kwicbl?(x.s1.d1) → from.kwicbl!(x.kwicbl.alpha) → to.kwicbl?(x.s1.d1) → from.kwicbl!(x.kwicbl.output) → KWICBL Secondly we define the bindings to configure the system. Client Server IO IN IO OUT ClientConnector ServerConnectorKWIC ServerConnector KWICBL

Client1.IN out Client1.IN in Client1.out Client1.in Ccon1.s in Ccon1.s out

Client1 Input, Shifter, Alpha, Output IN OUT Ccon1 Scon KWIC Scon1, Scon2, Scon3, Scon4 Kwicbl

− − OUT.in = OUT out − − IN.out = IN in − − Ccon1.in = V0 − − Ccon1.out = V1 − − Scon KWIC.to.c1 = to.c1 − − Scon KWIC.from.c1 = from.c1

Kwicbl.to.kwicbl Kwicbl.from.kwicbl Scon1.to.input Scon1.from.input

− − Scon − − Scon − − Scon − − Scon

KWIC.to.kwicbl = to.kwicbl KWIC.from.kwicbl = from.kwicbl KWIC.to.input = to.input KWIC.from.input = from.input

51

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

52 Input.out Input.in Scon2.to.shifter Scon2.from.shifter Shifter.out Shifter.in Scon3.to.alpha Scon3.from.alpha Alpha.out Alpha.in Scon4.to.output Scon4.from.output Output.out Output.in

− − Scon1.in = applyOutput − − Scon1.out = V2 − − Scon KWIC.to.shifter = to.shifter − − Scon KWIC.from.shifter = from.shifter − − Scon2.in = applyShift − − Scon2.out = V3 − − Scon KWIC.to.alpha = to.alpha − − Scon KWIC.from.alpha = from.alpha − − Scon3.in = applyAlpha − − Scon3.out = V4 − − Scon KWIC.to.output = to.output − − Scon KWIC.from.output = from.output − − Scon4.in = applyOutput − − Scon4.out = V5

IN and OUT are the same system I/O processes that we have seen before. The definitions above result in the specifications for the following auxiliary CSP processes: CSCLIENT = (((IN ||| OUT) k {IN in,OUT out} Client1) k{V0,V1} Ccon1) CSSERVERS = (ServerConnector(input) k {applyInput,V2} Input) ||| (ServerConnector(shifter) k {applyShift,V3} Shifter) ||| (ServerConnector(alpha) k{applyAlpha,V3} Alpha) ||| (ServerConnector(output) k{applyOutput,V3} Output) CSKWIC = KWICBL k{from.kwicbl,to.kwicbl} ServerConnectorKWIC And finally the composition of these, forming the KWIC process: KWIC0 = (CSCLIENT k{from.kwicbl,to.kwicbl} CSKWIC) k{from.input,to.input,from.shifter,to.shifter,from.alpha,to.alpha,from.output,to.output} CSSERVERS

4.4.2 Client/Server refactored to Blackboard In this section we will describe the refactoring from the Client/Server architecture style to the Blackboard style, see also Figure 4.13. The connection from the client to the connector has remained the same, data, reference and control flow travel over the same connection. However, due to the nature of the Blackboard architecture, the control and reference flow is then directed to the controller, while the data is written to and read from the Blackboard. The controller, specified below, may need some additional explanation. The controller waits for a request on one of its control input channels. These channels are connected to connectors. After it has received a message it will determine where to send it by looking at the dest property of the message.

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD

53

It then sends a special message, go, to the connector connected to the server where the message is being sent to. The connector now knows that it is supposed to read a message from the blackboard, send it to the server, await its response and put the response on the Blackboard. After the response has been put on the Blackboard, the connector sends the response to the connector which now sends a go message to the client, instructing it to get the response from the Blackboard and send it to the client. ClientCon = c in?req → ctrl out!req → b out!req → ctrl in?go → b in?res → c out!res → ClientCon ServerCon = ctrl in?go → b in?req → s out!req → s in?res → b out!res → ctrl out!res → ServerCon Controller = c1?req → if m.dest = S1 then (s1!go → s1?res → c1!go → Controller) else if req.dest = S2 then (s2!go → s2?res → c2!go → Controller) else Controller 2 c2?req → s2!go → s2?res → c2!go → Controller To illustrate the fact that the Architecture Style can be viewed as a connection mechanism for components, consider the following figure (see Figure 4.14). In this figure we have placed the generic versions of Pipe and Filter and Blackboard side by side. The dashed box indicates where the boundaries of the style lie. The components are not, and should not be, aware of the Architectural Style that connects them. This separation of interconnection between components makes it possible to replace one style with another, provided that the new style can accomodate the communication requirements of the components. 4.4.2.1

KWIC instance of the generic Blackboard Architecture

Applying the general refactoring to the KWIC case yields the following configuration, see Figure 4.15. First we define the CSP processes. Controller = 2i∈Node (ctrl from.i?(x.s.d) → ctrl to.d!go → ctrl from.d?(x.s.d) → ctrl to.d!go → Controller) ClientCon(i) = c in?req → ctrl from.i!req → b out!req → ctrl to.i?go → b in?res → c out!res → ClientCon(i) ServerCon(i) = ctrl to.i?go → b in?req → s out!req → s in?res → b out!res → ctrl from.i!res → ServerCon(i) Secondly we define the binding.

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

54

Figure 4.13: Generic Client/Server refactored to Blackboard style

Data flow

Server 1

Client 1 in

out

c_out

in

c_in

s_out

b_out

b_in

CCON1 b_in

out

out

out

Client 2

Server 2 in

s_in

s_out

b_out

b_in

SCON1

in

Data + Control + Reference Flow

Control + Reference flow

out

in

s_in

c_out

b_out

b_in

SCON2

in

out

c_in CCON2

in

b_out

out

Black Board

c2 c1

c3 Controller

out

c4

in

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD

55

Figure 4.14: Generic Pipe and Filter Architecture side by side with Generic Blackboard Architecture Client 1 in

out

c_out

c_in

in

out2

in

in2

in

c_out

out

in

c_in

s_out

b_in

in2

out1

s_in

b_in

in

b_out

out

in

out

in

Blackboard

s_out out

in

out

Server 1

in

in

Controller

Server 2 b_out

b_in

b_in

SCON2

Client or Server

in Server 2

Data flow

Style boundary

b_out CCON2

s_in

s_out Control + Data + Reference

Connector

Control + Reference flow

s_in SCON1

b_out

out

out

SCON2 s_out

out

Server 1

CCON1 out

out2

in1

SCON1 s_in

c_in

in

CCON2

out1

out

out

c_out

CCON1 in1

Client 1

Client 2

out

c_out

c_in

in Client 2

out

56

IN

in

IN_in

in

out

s_in

in

c_out

SCON_KWIC b_in

out

c_in

in

s_out

CCON1 b_out

out

Input

Client1

in

b_in

out

out

s_in

in

in

b_in

s_in

in

c_out

SCON2 b_out

out

out

s_out

SCON1 b_out

Alpha

Shifter

in

b_in

Black

in Board

Controller

out

c_in

in

c_out

SCON3 b_out

out

Output

b_in

c_in SCON4

b_out

out

out

in

b_in

b_out

out

in

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

s_out

OUT_out

Figure 4.15: KWIC refactored from Client/Server to Blackboard

out

KWICBL

Data flow

OUT

Data + Control + Reference Flow

Control + Reference flow

4.4. REFACTORING FROM CLIENT/SERVER TO BLACKBOARD Client Server ClientConnector ServerConnector BlackBoard Controller Client1.IN out Client1.IN in Client1.out Client1.in Ccon1.b in Ccon1.b out

Input.in Input.out Scon1.b in Scon1.b out Shifter.in Shifter.out Scon2.b in Scon2.b out Alpha.in Alpha.out Scon3.b in Scon3.b out Output.in Output.out Scon4.b in Scon4.b out

Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl Ctrl.ctrl

Client1 Input, Shifter, Alpha, Output Ccon1 Scon1, Scon2, Scon3, Scon4 BB Ctrl − − OUT.in = OUT out − − IN.out = IN in − − Ccon1.c in = V0 − − Ccon1.c out = V1 − − BB.out = B0 − − BB.in = B1

− − Scon1.s out = V2 − − Scon1.s in = applyInput − − BB.out = B0 − − BB.in = B1 − − Scon2.s out = V3 − − Scon2.s in = applyShift − − BB.out = B0 − − BB.in = B1 − − Scon3.s in = V4 − − Scon3.s out = applyAlpha − − BB.out = B0 − − BB.in = B1 − − Scon4.s in = V5 − − Scon4.s out = applyOutput − − BB.out = B0 − − BB.in = B1

from.c1 to.c1 from.input to.input from.shifter to.shifter from.alpha to.alpha from.output to.output

− − Ccon1.ctrl − − Ccon1.ctrl − − Scon1.ctrl − − Scon1.ctrl − − Scon2.ctrl − − Scon2.ctrl − − Scon3.ctrl − − Scon3.ctrl − − Scon4.ctrl − − Scon4.ctrl

from.c1 = ctrl.from.c1 to.c1 = ctrl.to.c1 from.input = ctrl from.input to.input = ctrl to.input shifter = ctrl from.shifter to.shifter = ctrl to.shifter from.alpha = ctrl from.alpha to.alpha = ctrl to.alpha from.output = ctrl from.output to.output = ctrl to.output

Having defined these bindings, we can define the following auxiliary processes:

57

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

58

BBClient = ((IN ||| OUT) k {IN in,OUT out} Client1) k{V0,V1} ClientCon(c1) BBServers = (Input k{V2,applyInput} ServerCon(input)) ||| (Shifter k{V3,applyShift} ServerCon(shifter)) ||| (Alpha k{V4,applyAlpha} ServerCon(alpha)) ||| (Output k{V5,applyOutput} ServerCon(output)) Define a synchronization alphabet: Sync = {ctrl from.c1, ctrl to.c1, ctrl from.input, ctrl to.input ctrl from.shifter, ctrl to.shifter, ctrl from.alpha, ctrl to.alpha, ctrl from.output, ctrl to.output, ctrl from.kwicbl, ctrl to.kwicbl} Finally we can define the refactored KWIC system: KWIC0 = (BBClient ||| BBServers) k Sync Controller

4.4.3 Behaviour To establish behaviour equivalence, we hide all events except the function application events. The processes shown above are then translated to CSP M . We can now determine refinement in the Failuresdivergences model, between the two KWIC processes (KWIC and KWIC 0 ) in both directions, using FDR2. See appendix B.3 for the csp sources file used as input for FDR. KWIC vFD KWIC0 ∧ KWIC0 vFD KWIC and therefore KWIC = KWIC 0 Figure 4.16 shows a scenario where Client1 retrieves data from Server1.

4.5 Comparing Quality Attributes of Styles In this section we will do a very high level assessment of the quality attributes of Client/Server style compared to the Blackboard style. Extensibility is studied by adding one component to the Client/Server Architecture and to the Client/Server Architecture refactored to the Blackboard style and evaluating the implications.

4.5.1 Adding one component to the Client/Server Architecture Consider Figure 4.17, in this case the connectors for Server 1, SCON1 and Server 2, SCON2 both have to be changed in order to be made aware of the new client. Adding a server has similar repercussions. As the total number of components in the architecture grows, the numbers of changes to be made grows at the same rate.

BlackBoard

Controller

SCON1

Server1

: request for data from Server1 : put data : Notify controller that there is a message for Server1 : Notification forwarding : Notification forwarding : Put requested data on BlackBoard : Notify controller that data is ready : Notification forward : Notification forward : Get data from BB : Send data to Client1

4.5. COMPARING QUALITY ATTRIBUTES OF STYLES

CCON1

59

Figure 4.16: Sequence Diagram showing a message exchange for the Blackboard architecture, where Client1 requests a response from Server1

Client1

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

60

Figure 4.17: Adding a client node to the Client/Server Architecture Client 3 in Client 1 in

c_out

Client 2 out

c_in

in

out2

in

in

in2

in1

in

Server 1

Connector

Client or Server

out1

s_in

out

Control + Data + Reference

c_in

Client 2

c_in in

CCON3

out2

s_out

in

in

in

Server 1

out

out2

in1

in2

SCON2 s_out

out

c_in CCON2

in2

out1

s_in

out

c_out

SCON1

in

Server 2

out

out

SCON2 s_out

out

c_out

c_out

CCON1 out

out2

in1

SCON1 s_in

c_in

in

CCON2 in2

out1

out

out

c_out

CCON1 in1

Client 1

out

out1

s_in

out

s_out

in

Server 2

4.6. JAVA IMPLEMENTATION

61

Figure 4.18: Adding a client node to the Blackboard architecture. Client 1 in

c_out

out

in

c_in

s_out

CCON1 b_in

out

s_in

in

c_out

SCON1 b_out

out

Client 1

Server 1

b_in

in

out

b_in

in

out

in

out

out

in

b_in

in Server 2

Connector

Client or Server

b_out

b_in

CCON2 s_in

out

in

b_out

out

in

in

out

in

Controller

SCON2 s_out

s_in SCON1

b_in

out

out

Blackboard

in

b_out

in

s_out

b_out

Controller

b_in

out

CCON1

Blackboard out

in

Server 1

c_in CCON1

b_out

out

Client 1

c_out

c_in

in Client 2

Control + Data + Reference

b_out

b_in

SCON2

out

s_out

s_in

in

b_out CCON2

c_out

out

in

Server 2

Control + Reference flow

c_in

out

Client 2

Data flow

4.5.2 Adding one component to the Blackboard Architecture Consider Figure 4.18, in this case only the controller needs to be changed in order to be made aware of the new client. Adding a server also requires only the controller to be changed. The number of changes remains constant as the total number of components in the architecture grows. So while the number of changes required for the Blackboard remains constant, it also poses a risk. The whole system depends on the availability of one component, the controller. The Client/Server version does not have this problem, but is more difficult to change. We conclude that the Client/Server Architecture has better reliability, but the Blackboard architecture is more extensible.

4.6 Java implementation In this section we will discuss the practical implications of implementing an architecture, in accordance with the methods outlined in chapter 4, in Java.

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

62

There are two key elements to the method, that need to be present in the implementation. 1. We must be able to communicate over channels between processes, while following the CSP semantics. 2. We must be able to assemble components and connectors following the enhanced Darwin notation.

4.6.1 CSP semantics in Java Java does not offer CSP semantics natively. Instead Java employs a thread model and synchronisation is supplied using semaphores. In order to implement the CSP semantics in Java we need an external library. We have chosen the package JCSP1 to supply the CSP semantics, it covers all the necessary primitives to implement the methods outlined in chapter 4. For example: For two processes P and Q, the parallel composition PkQ is equivalent to public class P implements CSProcess { public class Q implements CSProcess {

} }

new Parallel(CSProcess[] {new P(), new Q()}).run()

4.6.2 Darwin notation in Java In order to implement the Darwin notation in Java, we will define an interface that all components and objects that will employ channels for communication and synchronisation will have to provide. public void addChannelInput(Channel ch, String name); public void addChannelOutput(Channel ch, String name); public Channel getChannel(String name); Any such object (wishing to use channels) will simply be able to look up a channel by name and get the externally supplied channel reference. For example: Given the following Darwin definition: 1

JCSP is a package for Java implementing CSP semantics. It is written by Peter Welch from the University of Kent, see also http://www.cs.kent.ac.uk/projects/ofa/jcsp/.

4.6. JAVA IMPLEMENTATION

63

Component comp Connector conn comp.out − − conn.in = ch We get the following corresponding code fragment: // First we define the binding Component comp = new Component(); Connector conn = new Connector(); Channel ch = new Channel(); comp.addChannelOutput(ch, "out"); conn.addChannelInput(ch, "in"); // Now inside Connector we can do: Channel in = getChannel("in"); value = in.read(); // Inside component we can do: Channel out = getChannel("out"); out.write(value);

4.6.3 Pipe and Filter We will now discuss the implementation of the Pipe and Filter architecture, using the implementation considerations mentioned above. We will start at the top (with respect to call hierarchy) and work our way down in the specifics of the implementation where explanation is required. Recall from the discussion about KWIC, that we have spread the functionality over four components: Input, Shifter, Alpha, Output. We also defined IN, OUT as system I/O processes. We will define a PipeAndFilter class that will encapsulate the steps that are necessary to connect the components to connectors using channels. The constructor argument is used to create the first component. This component is a bit special in that it has its own input mechanism (in this case it reads from a file). All the other components that are added using the method addFilter(), are added as a Connector-Component pair. This also covers the last component, as this component will have its own output mechanism. Note that it is free to do so since it is the last component in the line and not connected to a connector via its out channel, nor does it even need an out channel.

PipeAndFilter pf pf.addFilter(new pf.addFilter(new pf.addFilter(new pf.addFilter(new pf.addFilter(new pf.run();

= new PipeAndFilter(new IOin(fname)); Input()); Shifter()); Alpha()); Output()); IOout());

The method addFilter(Component c) is implemented as follows:

CHAPTER 4. REFACTORING ARCHITECTURAL STYLE

64

public void addFilter(Component filter) { // Get the component that was last added Component comp1 = getLastComponent(); Channel ch = comp1.getChannel("out"); // Connect a connector to the last component in the chain // and add the connector to the chain // Darwin: comp1.out -- conn.in = ch Connector conn = new PipeAndFilterConnector(); chain.add(conn); conn.addChannelInput(ch, "in"); ch = new One2OneChannel(); conn.addChannelOutput(ch, "out"); // Connect a filter to the connector and add the filter // to the chain // Darwin: conn.out -- filter.in = ch chain.add(filter); filter.addChannelInput(ch, "in"); ch = new One2OneChannel(); filter.addChannelOutput(ch, "out"); } First a connector is connected to the component that was added last. To this connector we connect the component given as the parameter of the method. This corresponds to the following Darwin binding: Component Connector comp1.out conn.out

comp1, filter conn − − conn.in = ch − − filter.in = ch

We also keep track of all the components and connectors that are added in a list (chain), in order to i) be able to find the last connector, ii) be able to execute the parallel composition of all the components and connectors when we want to run the PipeAndFilter process. Which brings us to the last method that is shown above (pf.run()). public void run() { CSProcess[] processes = new CSProcess[chain.size()]; for (int i=0; i if (x == eof) then STOP else ControllerN m = (Message) c2.read(); while (m.getData() != null) { m = (Message) c2.read(); } // STOP } /** * Read from two channels alternatively until on both channels EOF has * been read */ private Message readAlternative(Alternative alt, Channel c1, Channel c2) { Message m1 = null; Message m2 = null; int n = alt.select(); System.out.println("n=" + n); switch(n) { case 0: m1 = (Message) c1.read(); break; case 1: m2 = (Message) c2.read(); break; default: throw new RuntimeException( "invalid index in alt.select()=" + n); } // Continue reading until on both channels EOF has been read while (m1==null || (m1 != null && m1.getData() != null) || m2==null || (m2 != null && m2.getData() != null)) { n = alt.select(); System.out.println("n=" + n); switch(n) { case 0: m1 = (Message) c1.read(); break; case 1: m2 = (Message) c2.read(); break; default: throw new RuntimeException( "invalid index in alt.select()=" + n); } } return null; } }

/* * Storage.java * * Created on June 6, 2005, 11:46 AM * * Copyright (C) 2005 Marc van Kempen ([email protected]) * All rights reserved */ package refactor.bb;

89

APPENDIX A. JAVA IMPLEMENTATIONS KWIC

90 import import import import import import import

java.util.ArrayList; java.util.HashMap; jcsp.lang.CSProcess; jcsp.lang.Channel; jcsp.lang.ChannelInput; jcsp.lang.ChannelOutput; refactor.base.Message;

/** * * @author marc */ public class Storage implements CSProcess { private HashMap channels; /** Creates a new instance of Storage */ public Storage() { channels = new HashMap(); } public void addChannelInput(ChannelInput provider, String name) { channels.put(name, provider); } public void addChannelOutput(ChannelOutput consumer, String name) { channels.put(name, consumer); } public Channel getChannel(String name) { return (Channel) channels.get(name); } public void run() { ArrayList buffer = new ArrayList(); Channel in = getChannel("in"); Channel out = getChannel("out"); Message m = (Message) in.read(); while (m.getData() != null) { buffer.add(m); m = (Message) in.read(); } // process buffer for (int i=0; i p_r?x -> f!fill -> f_r?x -> ch!check -> ch_r?x -> a!analyse -> a_r?x -> sg!generate -> sg_r?x -> sf!filter -> sf_r?x -> SKIP DBCreate = cr?x -> cr_r!doCreate(create) -> DBCreate Parser = p?x -> p_r!doParse(parse) -> Parser DBFill = f?x -> f_r!doFill(fill) -> DBFill DBCheck = ch?x -> ch_r!doCheck(check) -> DBCheck Analyse = a?x -> a_r!doAnalyse(analyse) -> Analyse StatGen = sg?x -> sg_r!doGenerate(generate) -> StatGen StatFilter = sf?x -> sf_r!doFilter(filter) -> StatFilter SaatSystem1 = Saat [| {| cr, cr_r, p, p_r, f, f_r, ch, ch_r, a, a_r, sg, sg_r, sf, sf_r |} |] (DBCreate ||| Parser ||| DBFill ||| DBCheck ||| Analyse |||

95

APPENDIX B. CSP INPUT FILES FOR FDR

96 StatGen ||| StatFilter) Internals = {| cr, p, f, ch, a, sg, sf |} SaatSystem = SaatSystem1 \ Internals

--- Saat refactored -channel db, db_r, st, st_r : Values Saat’ = db!process -> db_r?x -> a!analyse -> a_r?x -> st!process -> st_r?x -> SKIP DB = db?x -> cr!create -> cr_r?x -> p!parse -> p_r?x -> f!fill -> f_r?x -> ch!check -> ch_r?x -> db_r!process -> SKIP Stat = st?x -> sg!generate -> sg_r?x -> sf!filter -> sf_r?x -> st_r!process -> SKIP SaatSystem2 = Saat’ [| {| db, db_r, a, a_r, st, st_r |} |] ((DB [| {| cr, cr_r, p, p_r, f, f_r, ch, ch_r |} |] (DBCreate ||| Parser ||| DBFill ||| DBCheck) ) ||| Analyse ||| (Stat [| {| sg, sg_r, sf, sf_r |} |] (StatGen ||| StatFilter) ) ) Internals’ = {| db, db_r, st, st_r, cr, p, f, ch, a, sg, sf |} SaatSystem’ = SaatSystem2 \ Internals’ assert SaatSystem [FD= SaatSystem’ assert SaatSystem’ [FD= SaatSystem

B.2 Refactoring from Pipe and Filter to Blackboard -------------------------------------------------------------------

B.2. REFACTORING FROM PIPE AND FILTER TO BLACKBOARD -- Pipe and Filter architecture style refactored to Blackboard ---------------------------------------------------------------------- $Rev: 67 $ -- $Date: 2005-06-07 11:06:32 +0200 (Tue, 07 Jun 2005) $ ---- Common definitions -Values = { -1, 0, 1 } channel in, out: Values channel V0, V1, V2, V3, V4, V5, V6 : Values channel applyInput, applyShift, applyAlpha, applyOutput: Values --- Buffer size (or max. nr. of input tokens) -N=3 --- System I/O processes, limit the -IN’(n) = if (n == 0) then (in.-1 -> out!(-1) -> else (in?x -> if (x == -1) out!(-1) else out!x -> ) IN = IN’(N) OUT = in?x -> out!x -> OUT

number of input tokens to N

STOP) then -> STOP IN’(n-1)

--- Filters -doInput(x) = x doShift(x) = x doAlpha(x) = x doOutput(x) = x Input = in?x -> out!doInput(x) -> Input -- Simulate Shifter behaviour, the essentials of which are -- that Shifter can output more than one event for every event -- received Shifter = in?x -> if (x == -1) then out!x -> STOP else out!doShift(x) -> out!doShift(x) -> Shifter -- Simulate sort behaviour, which is to read all data, sort it -- and then send it to the output. The sorting is not implemented.

97

APPENDIX B. CSP INPUT FILES FOR FDR

98

Alpha(s, reading) = if (reading) then (in?x -> if (((#s) == 2*N+1) or (x==-1)) then Alpha(s, false) else Alpha(s^, true) ) else (if (null(s)) then out!(-1) -> STOP else out!head(s) -> Alpha(tail(s), false) ) Output = in?x -> out!doOutput(x) -> Output ----------------------------------------------------- Pipe and Filter implementation with connectors ------------------------------------------------------- Connector with buffer of size N -ConnectorPF(s, reading) = if (reading) then (in?x -> if (((#s) == 2*N+1) or (x==-1)) then ConnectorPF(s, false) else ConnectorPF(s^, true) ) else (if (null(s)) then out!(-1) -> STOP else out!head(s) -> ConnectorPF(tail(s), false) ) PF1 = IN [[ out -> ->

CSCLIENT = ( (IN [[ out’