Towards More Robust Advice:
Message Flow Analysis for Composition
Filters and its Application
A thesis submitted for the degree of Master of Science at the University of Twente
A.J. de Roo
Enschede, March 29, 2007
Graduation committee:
Prof. dr. ir. M. Ak¸sit Dr. ir. L.M.J. Bergmans Ir. P.E.A. D¨urr
Ir. T. Staijen
Abstract
Aspect oriented programming improves the quality of software by allowing a better separation of concerns. Composition filters is a delegation based AOP approach. It introduces advice by filtering messages sent between objects.
The declarative syntax of composition filters opens possibilities for powerful reasoning about the behavior of a set of filters. This reasoning includes control flow analysis. But control flow analysis is only the basis of a much more powerful reasoning technique, called message flow analysis. Message flow analysis reasons about the behavior of a filter set for a specific message. It is a combination of control flow analysis and data flow analysis on the message entity.
Message flow analysis brings opportunities for powerful conflict detection techniques, analyzing concern signature modifications, inlining of a set of filters and more.
Acknowledgements
With the finishing of my thesis my graduation project has come to an end. There are a number of people that supported me during my graduation project, for which I am grateful.
First, I would like to thank the members of my graduation committee. I would like to thank Lodewijk Bergmans, my supervisor, for being an example of a true scientist to me. With his always critic eye and expert knowledge he was of great guidance to me. I also would like to thank Pascal D¨urr. He inspired me with his never ending enthusiasm about composition filters and all its facets. I would like to thank him for always being available for answering questions, giving advice and enlightening discussions. Next, I would like to thank Tom Staijen. Although a less prominent member of my committee, his initial work on this subject gave me a good start of my graduation project.
Next, I would like to thank Michiel van Oudheusen and Sverre Boschman for the enjoyable time we had together during the StarLight project and all other Compose
?
developers for their valuable input.Last, but not least, I would like to thank my parents and my sister for their love and support during all those years and for their unconditional faith in me.
Contents
Abstract i
Acknowledgements iii
I Introduction 1
1 Introduction to AOSD 2
1.1 Introduction . . . 2
1.2 Traditional Approach . . . 4
1.3 AOP Approach . . . 5
1.3.1 AOP Composition . . . 6
1.3.2 Aspect Weaving . . . 6
1.4 AOP Solutions . . . 8
1.4.1 AspectJ Approach . . . 9
1.4.2 Hyperspaces Approach . . . 10
1.4.3 Composition Filters . . . 11
2 Compose
?
13 2.1 Evolution of Composition Filters . . . 132.2 Composition Filters in Compose
?
. . . 142.3 Demonstrating Example . . . 16
2.3.1 Initial Object-Oriented Design . . . 17
2.3.2 Completing the Pacman Example . . . 17
2.4 Compose
?
Architecture . . . 222.4.1 Integrated Development Environment . . . 22
2.4.2 Compile Time . . . 22
2.4.3 Adaptation . . . 22
2.4.4 Runtime . . . 22
2.5 Platforms . . . 23
2.6 Features Specific to Compose
?
. . . 232.7 StarLight . . . 24
2.7.1 StarLight Architecture . . . 25
2.7.2 Explicit Modeling of the Returning Flow . . . 25
2.7.3 Defining New Filter Types and Filter Actions . . . 27
3.1 Introduction . . . 30
3.2 Architecture of the .NET Framework . . . 31
3.2.1 Version 2.0 of .NET . . . 32
3.3 Common Language Runtime . . . 33
3.3.1 Java VM vs .NET CLR . . . 34
3.4 Common Language Infrastructure . . . 34
3.5 Framework Class Library . . . 36
3.6 Common Intermediate Language . . . 37
II Message Flow Analysis 40 4 Motivation 41 4.1 The Purpose of Filter Reasoning . . . 41
4.1.1 Conflict Analysis and Error Detection . . . 42
4.1.2 Signature Generation . . . 43
4.1.3 Filter Inlining . . . 45
4.1.4 Further Applicability . . . 46
4.2 Problem Description . . . 46
4.3 Requirements . . . 46
4.3.1 Conceptual Requirements . . . 47
4.4 Existing Approaches . . . 48
4.4.1 Logical Expressions Approach . . . 48
4.4.2 Message-Action Tree Approach . . . 50
5 The Message Flow Simulation Approach 52 5.1 Transforming the Abstract Syntax Tree to a Flowchart . . . 52
5.1.1 Transformation Rules . . . 54
5.2 Simulating the Execution of a Message in the Filter Set . . . 59
5.2.1 Adding a Frame . . . 59
5.2.2 Execution Steps . . . 60
5.2.3 Maintaining State . . . 63
5.3 Simulating with each Possible Message . . . 64
5.3.1 Different Messages that Behave the Same . . . 64
5.3.2 Identifying Equivalence Classes . . . 65
5.3.3 State Space Example . . . 67
5.4 Computational Complexity . . . 68
5.4.1 From AST to Flowchart . . . 68
5.4.2 Simulating the Execution . . . 72
5.4.3 Total Time Complexity . . . 73
5.5 Discussion . . . 74
5.5.1 Evaluating Condition Expressions . . . 74
5.5.2 The Meta Filter Uncertainty . . . 76
5.5.3 Target Matching . . . 77
6 The Filter Reasoning Engine 80
6.1 Filter Reasoning Engine Overview . . . 80
6.2 Using Graphs and Graph Transformations . . . 81
6.2.1 Transforming the AST to a Flowchart . . . 82
6.2.2 Simulating the Flowchart . . . 82
6.2.3 Advantages and Disadvantages of using GROOVE . . . 83
6.3 The Filter Reasoning Engine Preprocessor . . . 83
6.3.1 Overview of the process . . . 84
6.3.2 Class Structure . . . 85
6.3.3 Preprocessing each filter module? . . . 85
6.4 Combining the Information of Different Filter Modules in a Filter Set . . 87
6.4.1 Combining Different Flowcharts . . . 87
6.4.2 Combining Different Execution Models . . . 88
6.5 The Filter Reasoning Model . . . 93
6.5.1 The FIRE Model Structure . . . 93
6.5.2 The Flowchart Structure . . . 94
6.5.3 The Execution Model Structure . . . 96
6.6 Filter Reasoning Engine Tools . . . 97
6.6.1 Iterators . . . 98
6.6.2 Regular Expression Checker . . . 98
6.6.3 Query Engine . . . 98
6.6.4 Viewer . . . 101
6.7 Implementation issues . . . 101
6.7.1 Is Execution Model Expansion Needed? . . . 101
6.7.2 GROOVE’s inefficiency . . . 103
III Message Flow Analysis Applied 107 7 Consistency Reasoning 108 7.1 Theory and Concepts . . . 109
7.1.1 Identifying Unreachable Components . . . 109
7.1.2 From Unreachable Component to Conflict . . . 110
7.1.3 Computational Complexity . . . 115
7.2 Design and Implementation . . . 117
7.2.1 Position in the Compose
?
Architecture . . . 1177.2.2 Structure of the Consistency Reasoning Engine . . . 117
7.2.3 Implementation of the Consistency Reasoning Algorithm . . . 118
7.2.4 Reporting Conflicts . . . 120
8 Signature Generation 122 8.1 Theory and Concepts . . . 122
8.1.1 Old Approach to Signature Generation and Type Checking . . . . 123
8.1.2 A Different Interpretation of Holljen’s Definition . . . 124
8.1.3 Representing the Signature Mapping as a Dependency Graph . . . 125
8.1.6 Using Other Filter Actions as Sufficient Requirement for Inclusion
in the Signature . . . 139
8.2 From Theory to Practice . . . 140
8.2.1 Incrementally Building a Sufficient Subset of the Dependency Graph140 8.2.2 Initialization . . . 141
8.2.3 Start Signatures . . . 141
8.2.4 Final Signatures . . . 154
8.2.5 Type Checking and Conflict Detection . . . 156
8.2.6 Using Other Filter Actions as Sufficient Requirement for Inclusion in the Signature . . . 160
8.3 Design and Implementation . . . 163
8.3.1 Location in the Compilation Process . . . 163
8.3.2 Class Structure of the Signature Generation Engine . . . 163
9 Behavioral Reasoning 166 9.1 Theory and Concepts . . . 166
9.1.1 Introduction to Behavioral Reasoning . . . 167
9.1.2 Using Filter Reasoning for Behavioral Reasoning . . . 167
9.1.3 An Algorithm with Polynomial Time Complexity . . . 168
9.2 Design and Implementation . . . 173
9.2.1 Implemented Regular Expression Language . . . 173
9.2.2 Class Structure of the Regular Expression Matcher . . . 175
9.2.3 Implementation Details . . . 178
9.2.4 Integration with the Behavioral Reasoning Engine . . . 181
10 Filter Inlining 185 10.1 Theory and Concepts . . . 185
10.1.1 Flow Based Inlining . . . 186
10.1.2 Condition Based Inlining . . . 190
10.1.3 Flow Based Inlining with Jump Instructions . . . 193
10.1.4 Flow Based Inlining with Message Conditions . . . 194
10.1.5 Incorporating Conditional Superimposition . . . 195
10.1.6 Weaving Input Filters . . . 197
10.1.7 Weaving Output Filters . . . 201
10.2 Design and Implementation . . . 202
10.2.1 Overview of the Inlining Process . . . 202
10.2.2 The Abstract Instruction Model . . . 202
10.2.3 The Inlining Engine and Model Builder . . . 204
10.2.4 The Emitter: Communication from Java to .NET . . . 205
10.2.5 Weaving the Abstract Instruction Model into Common Interme-diate Language Assemblies . . . 208
10.2.6 Filter Inlining vs Runtime . . . 213
IV Conclusion 216
11 Conclusion 217
11.1 Discussion . . . 217
11.1.1 Message Flow Analysis . . . 217
11.1.2 Consistency Reasoning . . . 218
11.1.3 Signature Generation . . . 218
11.1.4 Behavioral Reasoning . . . 219
11.1.5 Filter Inlining . . . 219
11.2 Related Work . . . 220
11.2.1 Message Flow Analysis . . . 220
11.2.2 Consistency Reasoning . . . 220
11.2.3 Signature Generation . . . 221
11.2.4 Behavioral Reasoning . . . 221
11.2.5 Filter Inlining . . . 221
11.3 Future Work . . . 222
11.3.1 Message Flow Analysis . . . 222
11.3.2 Consistency Reasoning . . . 222
11.3.3 Signature Generation . . . 223
11.3.4 Behavioral Reasoning . . . 223
11.3.5 Filter Inlining . . . 223
11.4 Contributions . . . 224
11.4.1 Message Flow Analysis . . . 224
11.4.2 Consistency Reasoning . . . 224
11.4.3 Signature Generation . . . 224
11.4.4 Behavioral Reasoning . . . 225
11.4.5 Filter Inlining . . . 225
V Appendices 226 A Motivation Example 227 A.1 Base system . . . 227
A.2 LogMail Concern . . . 228
A.3 BufferMail Concern . . . 228
List of Figures
1.1 Dates and ancestry of several important languages . . . 3
2.1 Components of the composition filters model . . . 15
2.2 Class diagram of the object-oriented Pacman game . . . 18
2.3 Overview of the Compose
?
architecture . . . 212.4 Explicit modeling of the returning flow . . . 26
2.5 Filter actions can either continue, return or exit . . . 26
3.1 Context of the .NET framework . . . 32
3.2 Relationships in the CTS . . . 35
3.3 Main components of the CLI and their relationships . . . 36
3.4 From source code to machine code . . . 37
4.1 Structure of the mail system example . . . 42
4.2 New signature of Connection. . . 44
4.3 The message-action tree . . . 50
5.1 Example of an abstract syntax tree . . . 53
5.2 Example of a flowchart . . . 54
5.3 The Transformation rules . . . 55
5.3 The Transformation rules (Continued) . . . 56
5.3 The Transformation rules (Continued) . . . 57
5.3 The Transformation rules (Continued) . . . 58
5.3 The Transformation rules (Continued) . . . 58
5.4 A frame element is used to simulate the execution . . . 59
5.5 Doing an execution step . . . 60
5.6 Branching execution steps . . . 61
5.7 Handling the substitution part . . . 62
5.8 Creating a state space during the simulation . . . 63
5.9 Example of a state space . . . 69
5.10 Execution model with an exponential number of states . . . 79
6.1 Components in the Filter Reasoning Engine . . . 80
6.2 The FIRE preprocessing process . . . 84
6.3 Class structure of the FIRE Preprocessor . . . 86
6.4 The flowchart of a filter module . . . 87
6.6 Combining two flowcharts . . . 89
6.7 The execution models corresponding to the separate filter modules . . . . 90
6.8 Adding filter module conditions to the execution model . . . 90
6.9 Extended execution model of the first filter module . . . 92
6.10 The execution models corresponding to the separate filter modules . . . . 93
6.11 Combined execution model . . . 94
6.12 The FireModelcomponent . . . 95
6.13 The Flowchartstructure . . . 96
6.14 The ExecutionModelstructure . . . 97
6.15 The Iteratorcomponents . . . 98
6.16 The QueryEnginecomponents . . . 99
6.17 The internal structure of the CTLQueryEngine . . . 100
6.18 The Viewercomponents . . . 101
6.19 The execution models corresponding to the separate filter modules . . . . 102
6.20 The combined execution model with and without expansion . . . 102
6.21 Time GROOVE took to do the transformation and simulation for varying sizes of filter sets on a linear scale . . . 104
6.22 Time GROOVE took to do the transformation and simulation for varying sizes of filter sets on a logarithmic scale . . . 105
6.23 Time the FIRE Model took to combine the results of different filter mod-ules in a filter set . . . 106
7.1 Finding unreachable nodes and edges by relating the states and transitions in the execution model back to the flowchart . . . 110
7.2 The cause and effect relation between conflicts . . . 115
7.3 CORE class diagram . . . 117
7.4 Sequence diagram showing how reachable components are found . . . 118
7.5 Sequence diagram showing how unreachable components are identified and checked for conflicts . . . 119
8.1 Partial representation of the dependency graph . . . 128
8.2 The existence of methods resolved in the dependency graph . . . 131
8.3 Cyclic dependency in the dependency graph . . . 133
8.4 An Unknownmethod node does not need to be in the dependency cycle . . 133
8.5 Example of a cyclic dispatch conflict . . . 136
8.6 Example of an infinite signature conflict . . . 137
8.7 Example of a dependency graph with a type error . . . 138
8.8 The rules to create the signature/type set . . . 143
8.9 Class structure of the signature generation engine . . . 163
9.1 Schematic representation of the flowchart . . . 168
9.2 Regular expression matcher public structure . . . 175
9.3 Regular expression matcher internal structure . . . 177
9.4 Schematic representation of the automaton corresponding to regular ex-pressione1 . . . 178
9.5 A single word regular expression . . . 179
1 2
9.8 Kleene star operator on e1 . . . 180
9.9 Sequence diagram of SECRET’s original analysis phase . . . 182
9.10 Sequence diagram of SECRET’s new analysis phase . . . 182
9.11 Regular expression matcher integrated with SECRET . . . 183
10.1 Dividing the state space into filter blocks . . . 186
10.2 Dividing a filter block into filter element blocks . . . 187
10.3 States within a filter element block . . . 188
10.4 Substitution breaks sequential flow . . . 190
10.5 The condition based inlining rules . . . 191
10.6 Substitution leads to parallel flow traces through the filter set . . . 193
10.7 Generate code layer by layer to ensure the correct execution order . . . . 195
10.8 Dividing the state space into filter blocks and conditional superimposition states . . . 196
10.9 Approach 1: Using method wrapping to weave input filters . . . 198
10.10Overriding can lead to problems with method wrapping . . . 199
10.11Approach 2: Inserting filter code at the beginning of the method body . . 199
10.12Overriding can lead to problems in the inner call context if only a Boolean value is used . . . 200
10.13Output filter code replaces the original call . . . 201
10.14An overview of the inlining process . . . 202
10.15The abstract instruction model . . . 203
10.16Structure of the inlining engine and the model builder . . . 204
10.17FilterCode structure attached to the method or call . . . 205
Listings
1.1 Modeling addition, display, and logging without using aspects . . . 4
1.2 Modeling addition, display, and logging with aspects . . . 6
1.3 Example of dynamic crosscutting in AspectJ . . . 9
1.4 Example of static crosscutting in AspectJ . . . 10
1.5 Creation of a hyperspace . . . 11
1.6 Specification of concern mappings . . . 11
1.7 Defining a hypermodule . . . 12
2.1 Abstract concern template . . . 14
2.2 DynamicScoringconcern in Compose
?
. . . 192.3 Implementation of classScore . . . 20
2.4 DynamicStrategyconcern in Compose
?
. . . 212.5 Defining theLoggingInfilter actions . . . 27
2.6 Defining theLogging filter type . . . 27
2.7 Conditional superimposition example . . . 29
3.1 Adding example in IL code . . . 38
3.2 Adding example in the C# language . . . 39
3.3 Adding example in the VB.NET language . . . 39
4.1 The LogMailconcern . . . 42
4.2 The BufferMailconcern . . . 42
4.3 The superimposed filter set . . . 43
4.4 The SecureConnectionconcern . . . 44
4.5 Inlining ofsend with pure control flow analysis . . . 45
4.6 Inlining ofsend with message specific analysis . . . 45
4.7 Message-action tree example . . . 50
5.1 Example of a filter set . . . 52
5.2 Example of same flow behavior, but different external behavior . . . 65
5.3 Example of same external behavior, but different flow behavior . . . 65
5.4 Example leading to pseudo-equivalence classes . . . 67
5.5 Not taking conditions into account leads to more execution paths . . . 74
5.6 A more elaborate example of how not taking conditions into account leads to more execution paths . . . 74
5.7 An example of aMetaaction changing a message by using aSubstitution filter . . . 77
of states . . . 78
6.1 The filter modules . . . 88
7.1 Consistency conflicts example . . . 108
7.2 Consistency reasoning complete output . . . 120
7.3 Consistency reasoning primary conflict output . . . 120
7.4 Consistency reasoning leaf conflict + root cause . . . 120
7.5 Consistency reasoning level output . . . 121
8.1 Example of the incomplete signature problem . . . 124
8.2 Example of a type problem . . . 124
8.3 Example to illustrate the dependency graph concept . . . 127
8.4 Example of a cyclic dependency conflict . . . 132
8.5 Example leading to an infinite cyclic dependency conflict set . . . 134
8.6 Example leading to a type finite cyclic dependency conflict set . . . 134
8.7 Example of a paradoxical cyclic dependency conflict . . . 135
8.8 Example of a cyclic dispatch conflict . . . 136
8.9 Example of an infinite signature conflict . . . 137
8.10 Example of a concern with type errors . . . 138
8.11 Example to illustrate why a selector is added to the distinguishable set of the target concern . . . 146
9.1 Example of a filter set with exponentially many execution paths . . . 168
9.2 The regular expression grammar in EBNF . . . 173
10.1 Generating code for filter blocks . . . 187
10.2 Generating code for filter element blocks that might accept as well as reject189 10.3 Generating code for filter element blocks that always accept . . . 189
10.4 Generating code for filter element blocks that always rejects . . . 189
10.5 filter set leading to large condition expressions . . . 192
10.6 Generated code . . . 192
10.7 Using jump instructions to jump between parallel filter flows . . . 193
10.8 Using message conditions to select between parallel filter flows . . . 194
10.9 Generate code layer by layer to ensure the correct execution order . . . . 195
10.10Generating code for aFilterModuleCondition state . . . 196
10.11Example with conditional superimposition . . . 197
10.12Using filter context to cope with inner calls . . . 199
10.13Setting the inner call context . . . 200
10.14IL structure of the Branch instruction . . . 210
10.15Executing actions on return . . . 212
10.16Executing actions on return . . . 212
10.17Filter module used for the performance measurements . . . 213
11.1 Filter set that leads to a reachability conflicts in the second filter . . . 222
A.1 TheMailSystem class . . . 227
A.2 TheConnection class . . . 227
A.3 TheLogMailconcern . . . 228
A.4 TheLoggerclass . . . 228
A.5 TheBufferMail concern . . . 228
List of Algorithms
8.1 InitializeSignatures() . . . 141
8.2 StartSignatures() . . . 150
8.3 StartSignature(Concern c) . . . 150
8.4 StartSignatureDistinguishable(Concern c) . . . 150
8.5 StartSignatureClass1(Selector p, ExecutionState state) . . . 151
8.6 StartSignatureClass2(Selector p, ExecutionState state, Set typeSet) . . . . 151
8.7 StartSignatureUndistinguishable(Concern c) . . . 152
8.8 StartSignatureClass3(ExecutionState state) . . . 153
8.9 StartSignatureClass4(ExecutionState state, Set signatureSet) . . . 153
8.10 StartSignatureClass6(ExecutionState state, Set signatureSet) . . . 153
8.11 StartSignatureClass7(ExecutionState state, Set signatureSet, Set typeSet) . 153 8.12 FinalSignatures() . . . 154
8.13 FinalSignature(Concern c) . . . 154
8.14 checkDispatchable(Method m, Concern c) . . . 155
8.15 Checking() . . . 156
8.16 CyclicDependencyConflictCheck() . . . 156
8.17 TypeChecking() . . . 157
8.18 CyclicDispatchConflictCheck() . . . 158
8.19 CyclicDispatchConflictCheckInit() . . . 158
8.20 CyclicDispatchConflictCheckProcess() . . . 159
8.21 CyclicDispatchConflictCheckFinal() . . . 160
Part I
Chapter 1
Introduction to AOSD
“The superior man cannot be known in little matters, but he may be entrusted with great concerns. The small man may not be entrusted with great concerns, but he may be known in little matters.” Confucius Chinese philosopher & reformer (551 BC - 479 BC)
The first two chapters have originally been written by seven M. Sc. students [4, 5, 11, 17, 19, 47, 54] at the University of Twente. The chapters have been rewritten for use in the following theses [6, 7, 8, 20, 21, 46, 52, 53]. They serve as a general introduction into Aspect-Oriented Software Development and Compose
?
in particular.1.1
Introduction
The goal of software engineering is to solve a problem by implementing a software system. The things of interest are called concerns. They exist at every level of the engineering process. A recurrent theme in engineering is that of modularization: separation and lo-calization of concerns. The goal of modularization is to create maintainable and reusable software. A programming language is used to implement concerns.
Fifteen years ago the dominant programming language paradigm was procedural programming. This paradigm is characterized by the use of statements that update state variables. Examples are Algol-like languages such as Pascal, C, and Fortran.
Other programming paradigms are the functional, logic, object-oriented, and aspect-oriented paradigms. Figure 1.1 summarizes the dates and ancestry of several important languages [56]. Every paradigm uses a different modularization mechanism for separating concerns into modules.
Functional languages try to solve problems without resorting to variables. These languages are entirely based on functions over lists and trees. Lisp and Miranda are examples of functional languages.
1.1 Introduction
Figure 1.1: Dates and ancestry of several important languages
A shortcoming of procedural programming is that global variables can potentially be accessed and updated by any part of the program. This can result in unmanage-able programs because no module that accesses a global variunmanage-able can be understood independently from other modules that also access that global variable.
The Object-Oriented Programming (OOP) paradigm improves modularity by encap-sulating data with methods inside objects. The data may only be accessed indirectly, by calling the associated methods. Although the concept appeared in the seventies, it took twenty years to become popular [56]. The most well known object-oriented languages are C++, Java, C#, and Smalltalk.
The hard part about object-oriented design is decomposing a system into objects. The task is difficult because many factors come into play: encapsulation, granularity, dependency, adaptability, reusability, and others. They all influence the decomposition, often in conflicting ways [14].
Existing modularization mechanisms typically support only a small set of decompo-sitions and usually only a single dominant modularization at a time. This is known as the tyranny of the dominant decomposition [51]. A specific decomposition limits the ability to implement other concerns in a modular way. For example, OOP modularizes concerns in classes and only fixed relations are possible. Implementing a concern in a class might prevent another concern from being implemented as a class.
Aspect-Oriented Programming (AOP) is a paradigm that solves this problem.
1 p u b l i c c l a s s Add e x t e n d s C a l c u l a t i o n {
Listing 1.1: Modeling addition, display, and logging without using aspects
1.2
Traditional Approach
Consider an application containing an object Add and an object CalcDisplay. Add in-herits from the abstract class Calculation and implements its method execute(a, b). It performs the addition of two integers. CalcDisplay receives an update from Addif a calculation is finished and prints the result to screen. Suppose all method calls need to be traced. The objects use a Tracerobject to write messages about the program execu-tion to screen. This is implemented by a method called write. Three concerns can be recognized: addition, display, and tracing. The implementation might look something like Listing 1.1.
From our example, we recognize two forms of crosscutting: code tangling and code scattering.
The addition and display concerns are implemented in classesAdd and CalcDisplay
respectively. Tracing is implemented in the class Tracer, but also contains code in the other two classes (lines 5, 10, 14, and 20 in (a) and 2, 5, and 9 in (b)). If a concern is implemented across several classes it is said to be scattered. In the example of Listing 1.1 the tracing concern is scattered.
Usually a scattered concern involves code replication. That is, the same code is implemented a number of times. In our example the classesAddandCalcDisplaycontain
1.3 AOP Approach
In class Addthe code for the addition and tracing concerns are intermixed. In class
CalcDisplaythe code for the display and tracing concerns are intermixed. If more then
one concern is implemented in a single class they are said to be tangled. In our example the addition and tracing concerns are tangled. Also display and tracing concerns are tangled. Crosscutting code has the following consequences:
Code is difficult to change
Changing a scattered concern requires us to modify the code in several places. Making modifications to a tangled concern class requires checking for side-effects with all existing crosscutting concerns;
Code is harder to reuse
To reuse an object in another system, it is necessary to either remove the tracing code or reuse the (same) tracer object in the new system;
Code is harder to understand
Tangled code makes it difficult to see which code belongs to which concern.
1.3
AOP Approach
To solve the problems with crosscutting, several techniques are being investigated that attempt to increase the expressiveness of the OO paradigm. Aspect-Oriented Program-ming (AOP) introduces a modular structure, the aspect, to capture the location and behavior of crosscutting concerns. Examples of Aspect-Oriented languages are Sina, AspectJ, Hyper/J, and Compose
?
. A special syntax is used to specify aspects and the way in which they are combined with regular objects. The fundamental goals of AOP are twofold [16]: first, to provide a mechanism to express concerns that crosscut other components. Second, to use this description to allow for the separation of concerns.Join points are well-defined places in the structure or execution flow of a program where additional behavior can be attached. The most common join points are method calls. Pointcuts describe a set of join points. This allows us to execute behavior at many places in a program by one expression. Advice is the behavior executed at a join point. In the example of Listing 1.2 the class Add does not contain any tracing code and
only implements the addition concern. Class CalcDisplay also does not contain tracing code. In our example, the tracing aspect contains all the tracing code. The pointcut
tracedCallsspecifies at which locations tracing code is executed.
The crosscutting concern is explicitly captured in aspects instead of being embedded within the code of other objects. This has several advantages over the previous code.
Aspect code can be changed
Changing aspect code does not influence other concerns;
Aspect code can be reused
The coupling of aspects is done by defining pointcuts. In theory, this low coupling allows for reuse. In practice reuse is still difficult;
Aspect code is easier to understand
A concern can be understood independent of other concerns;
Aspect pluggability
1 p u b l i c c l a s s Add e x t e n d s C a l c u l a t i o n {
Listing 1.2: Modeling addition, display, and logging with aspects
1.3.1 AOP Composition
AOP composition can be either symmetric or asymmetric. In the symmetric approach every component can be composed with any other component. This approach is followed by e.g. Hyper/J.
In the asymmetric approach, the base program and aspects are distinguished. The base program is composed with the aspects. This approach is followed by e.g. AspectJ (covered in more detail in the next section).
1.3.2 Aspect Weaving
The integration of components and aspects is called aspect weaving. There are three approaches to aspect weaving. The first and second approach rely on adding behavior to the program, either by weaving the aspect in the source code, or by weaving directly in the target language. The target language can be intermediate language (IL) or machine code. Examples of IL are Java byte code and Common Intermediate Language (CIL). The remainder of this chapter considers only intermediate language targets. The third approach relies on adapting the virtual machine. Each method is explained briefly in the following sections.
1.3.2.1 Source Code Weaving
1.3 AOP Approach
language output (depending on the compiler-type). The advantages of using source code weaving are:
High-level source modification
Since all modifications are done at source code level, there is no need to know the target (output) language of the native compiler;
Aspect and original source optimization
First, the aspects are woven into the source code. Then, the source code is compiled by the native compiler. The produced target language has all the benefits of the native compiler optimization passes. However, optimizations specific to exploiting aspect knowledge are not possible;
Native compiler portability
The native compiler can be replaced by any other compiler as long as it has the same input language. Replacing the compiler with a newer version or another target language can be done with little or no modification to the aspect weaver.
However, the drawbacks of source code weaving are:
Language dependency
Source code weaving is written explicitly for the syntax of the input language;
Limited expressiveness
Aspects are limited to the expressive power of the source language. For example, when using source code weaving, it is not possible to add multiple inheritance to a single inheritance language.
1.3.2.2 Intermediate Language Weaving
Weaving aspects through an intermediate language gives more control over the exe-cutable program and solves some issues, as identified in Section 1.3.2.1 on source code weaving. Weaving at this level allows for creating combinations of intermediate language constructs that cannot be expressed at the source code level. Although IL can be hard to understand, IL weaving has several advantages over source code weaving:
Programming language independence
All compilers generating the target IL output can be used;
More expressiveness
It is possible to create IL constructs that are not possible in the original program-ming language;
Source code independence
Can add aspects to programs and libraries without using the source code (which may not be available);
Adding aspects at load- or runtime
A special class loader or runtime environment can decide and do dynamic weaving. The aspect weaver adds a runtime environment into the program. How and when aspects can be added to the program depend on the implementation of the runtime environment.
Hard to understand
Specific knowledge about the IL is needed;
More error-prone
Compiler optimization may cause unexpected results. Compiler can remove code that breaks the attached aspect (e .g., inlining of methods).
1.3.2.3 Adapting the Virtual Machine
Adapting the virtual machine (VM) removes the need to weave aspects. This technique has the same advantages of intermediate language weaving and can also overcome some of the disadvantages of intermediate language weaving, mentioned in Section 1.3.2.2. Aspects can be added without recompilation, redeployment, and restart of the applica-tion [40, 41].
Modifying the virtual machine also has its disadvantages:
Dependency on adapted virtual machines
Using an adapted virtual machine requires that every system should be upgraded to that version;
Virtual machine optimization
People have spend a lot of time optimizing virtual machines. By modifying the virtual machine these optimizations should be revisited. Reintegrating changes in-troduced by newer versions of the original virtual machine, might have substantial impact.
1.4
AOP Solutions
As the concept of AOP has been embraced as a useful extension to classic programming, different AOP solutions have been developed. Each solution has one or more implemen-tations to demonstrate how the solution is to be used. As described by [12] these differ primarily in:
How aspects are specified
Each technique uses its own aspect language to describe the concerns;
Composition mechanism
Each technique provides its own composition mechanisms;
Implementation mechanism
Whether components are determined statically at compile time or dynamically at run time, the support for verification of compositions, and the type of weaving.
Use of decoupling
Should the writer of the main code be aware that aspects are applied to his code;
Supported software processes
The overall process, techniques for reusability, analyzing aspect performance of aspects, is it possible to monitor performance, and is it possible to debug the aspects.
1.4 AOP Solutions
1 a s p e c t D y n a m i c C r o s s c u t t i n g E x a m p l e {
2 Log log = new Log () ;
3
4 p o i n t c u t t r a c e M e t h o d s () :
5 e x e c u t i o n( edu . u t w e n t e . t r e s e . * . * ( . . ) ) ;
6
7 b e f o r e() : t r a c e M e t h o d s {
8 log . w r i t e ( " E n t e r i n g " + t h i s J o i n t P o i n t . g e t S i g n a t u r e () ) ;
9 }
10
11 a f t e r() : t r a c e M e t h o d s {
12 log . w r i t e ( " E x i t i n g " + t h i s J o i n t P o i n t . g e t S i g n a t u r e () ) ;
13 }
14 }
Listing 1.3: Example of dynamic crosscutting in AspectJ
1.4.1 AspectJ Approach
AspectJ [24] is an aspect-oriented extension to the Java programming language. It is probably the most popular approach to AOP at the moment, and it is finding its way into the industrial software development. AspectJ has been developed by Gregor Kiczales at Xerox’s PARC (Palo Alto Research Center). To encourage the growth of the AspectJ technology and community, PARC transferred AspectJ to an open Eclipse project. The popularity of AspectJ comes partly from the various extensions based on it, build by several research groups. There are various projects that are porting AspectJ to other languages, resulting in tools such as AspectR and AspectC.
One of the main goals in the design of AspectJ is to make it a compatible extension to Java. AspectJ tries to be compatible in four ways:
Upward compatibility
All legal Java programs must be legal AspectJ programs;
Platform compatibility
All legal AspectJ programs must run on standard Java virtual machines;
Tool compatibility
It must be possible to extend existing tools to support AspectJ in a natural way; this includes IDEs, documentation tools and design tools;
Programmer compatibility
Programming with AspectJ must feel like a natural extension of programming with Java.
AspectJ extends Java with support for two kinds of crosscutting functionality. The first allows defining additional behavior to run at certain well-defined points in the ex-ecution of the program and is called the dynamic crosscutting mechanism. The other is called thestatic crosscutting mechanism and allows modifying the static structure of classes (methods and relationships between classes). The units of crosscutting imple-mentation are called aspects. An example of an aspect specified in AspectJ is shown in Listing 1.3.
1 a s p e c t S t a t i c C r o s s c u t t i n g E x a m p l e {
2 p r i v a t e int Log . t r a c e ( S t r i n g t r a c e M s g ) {
3 Log . w r i t e ( " - - - M A R K - - - " + t r a c e M s g ) ;
4 }
5 }
Listing 1.4: Example of static crosscutting in AspectJ
are calledjoin points. A pointcut has a set of join points. In Listing 1.3 is traceMethods
an example of a pointcut definition. The pointcut includes all executions of any method that is in a class contained by package edu.utwente.trese.
The code that should execute at a given join point is declared in an advice. Advice is a method-like code body associated with a certain pointcut. AspectJ supportsbefore, after andaround advice, which specifies where the additional code is to be inserted. In the example both before and after advice are declared to run at the join points specified by the traceMethods pointcut.
Aspects can contain anything permitted in class declarations including definitions of pointcuts, advice and static crosscutting. For example, static crosscutting allows a programmer to add fields and methods to certain classes as shown in Listing 1.4.
The shown construct is called inter-type member declaration and adds a method
traceto classLog. Other forms of inter-type declarations allow developers to declare the
parents of classes (super classes and realized interfaces), declare where exceptions need to be thrown, and allow a developer to define the precedence among aspects.
With its variety of possibilities, AspectJ can be considered a useful approach for realizing software requirements.
1.4.2 Hyperspaces Approach
TheHyperspacesapproach is developed by H. Ossher and P. Tarr at the IBM T.J. Watson Research Center. The Hyperspaces approach adopts the principle of multi-dimensional separation of concerns [37], which involves:
• Multiple, arbitrary dimensions of concerns;
• Simultaneous separation along these dimensions;
• Ability to dynamically handle new concerns and new dimensions of concern as they arise throughout the software life cycle;
• Overlapping and interacting concerns. It is appealing to think of many concerns as independent or orthogonal, but they rarely are in practice.
We explain the Hyperspaces approach by an example written in the Hyper/J
1.4 AOP Solutions
1 H y p e r s p a c e P a c m a n
2 c l a s s edu . u t w e n t e . t r e s e . p a c m a n .*;
Listing 1.5: Creation of a hyperspace
As a first step, developers create hyperspaces by specifying a set of Java class files that contain the code units that populate the hyperspace. To do this, you create a hyperspace specification, as demonstrated in Listing 1.5.
Hyper/J will automatically create a hyperspace with one dimension—the class file dimension. A dimension of concern is a set of concerns that are disjoint. The initial hyperspace will contain all units within the specified package. To create a new dimension you can specify concern mappings, which describe how existing units in the hyperspace relate to concerns in that dimension, as demonstrated in Listing 1.6.
The first line indicates that, by default, all of the units contained within the package
edu.utwente.trese.pacman address the kernel concern of the feature dimension. The
other mappings specify that any method namedtrace ordebugaddress the logging and debugging concern respectively. These later mappings override the first one.
Hypermodules are based on concerns and consist of two parts. The first part specifies a set of hyperslices in terms of the concerns identified in the concern matrix. The second part specifies the integration relationships between the hyperslices. A hyperspace can contain several hypermodules realizing different modularizations of the same units. Systems can be composed in many ways from these hypermodules.
Listing 1.7 shows a hypermodule with two concerns, kernel and logging. They are related by amergeByNameintegration relationship. This means that units in the different concerns correspond if they have the same name (ByName) and that these corresponding
units are to be combined (merge). For example, all members of the corresponding classes are brought together into the composed class. The hypermodule results in a hyperslice that contains all the classes without the debugging feature; thus no debugmethods will
be present.
The most important feature of the hyperspaces approach is the support for on-demand remodularisation: the ability to extract hyperslices to encapsulate concerns that were not separated in the original code. Which makes hyperspaces especially useful for evolution of existing software.
1.4.3 Composition Filters
Composition Filters is developed by M. Ak¸sit and L. Bergmans at the TRESE group, which is a part of the Department of Computer Science of the University of Twente, The Netherlands. The composition filters (CF) model predates aspect-oriented programming.
1 p a c k a g e edu . u t w e n t e . t r e s e . p a c m a n : F e a t u r e . K e r n e l
2 o p e r a t i o n t r a c e : F e a t u r e . L o g g i n g
3 o p e r a t i o n d e b u g : F e a t u r e . D e b u g g i n g
1 h y p e r m o d u l e P a c m a n _ W i t h o u t _ D e b u g g i n g
2 h y p e r s l i c e s: F e a t u r e . Kernel , F e a t u r e . L o g g i n g ;
3 r e l a t i o n s h i p s: m e r g e B y N a m e;
4 end h y p e r m o d u l e;
Listing 1.7: Defining a hypermodule
It started out as an extension to the object-oriented model and evolved into an
aspect-oriented model. The current implementation of CF is Compose
?
, which covers .NET,Java, and C.
One of the key elements of CF is the message, a message is the interaction between objects, for instance a method call. In object-oriented programming the message is considered an abstract concept. In the implementations of CF it is therefore necessary to reify the message. This reified message contains properties, like where it is send to and where it came from.
The concept of CF is that messages that enter and exit an object can be intercepted and manipulated, modifying the original flow of the message. To do so, a layer called theinterface part is introduced in the CF model, this layer can have several properties. The interface part can be placed on an object, which behavior needs to be altered, and this object is referred to as inner.
Chapter 2
Compose
?
“The difficult part of composition filters is understanding its simplicity.” Lodewijk Bergmans Dutch scientist (1967 - )
Compose
?
is an implementation of the composition filters approach. There are three target environments: the .NET, Java, and C. This chapter is organized as follows, first the evolution of Composition Filters and its implementations are described, followed by an explanation of the Compose?
language and a demonstrating example. In the third section, the Compose?
architecture is explained, followed by a description of the features specific to Compose?
.2.1
Evolution of Composition Filters
Compose
?
is the result of many years of research and experimentation. The following time line gives an overview of what has been done in the years before and during the Compose?
project.1985 The first version of Sina is developed by Mehmet Ak¸sit. This version of Sina
contains a preliminary version of the composition filters concept called semantic networks. The semantic network construction serves as an extension to objects, such as classes, messages, or instances. These objects can be configured to form other objects, such as classes, from which instances can be created. The object manager takes care of synchronization and message processing of an object. The semantic network construction can express key concepts like delegation, reflection, and synchronization [28].
1987 Together with Anand Tripathi of the University of Minnesota the Sina language
is further developed. The semantic network approach is replaced by declarative specifications and the interface predicate construct is added.
Chapter 2 Compose
?
1995 The Sina language with Composition Filters is implemented using
Small-talk [28]. The implementation supports most of the filter types. In the same year, a preprocessor providing C++ with support for Composition Filters is implemented [15].
1999 The composition filters language ComposeJ [57] is developed and implemented.
The implementation consists of a preprocessor capable of translating composi-tion filter specificacomposi-tions into the Java language.
2001 ConcernJ is implemented as part of a M. Sc. thesis [45]. ConcernJ adds the
notion of superimposition to Composition Filters. This allows for reuse of the filter modules and to facilitate crosscutting concerns.
2003 The start of the Compose
?
project, the project is described in further detail in this chapter.2004 The first release of Compose
?
, based on .NET.2005 The start of the Java port of Compose
?
.2006 Porting Compose
?
to C is started.2006 Start of the StarLight project. This project is described in detail in Section 2.7.
2.2
Composition Filters in Compose
?
1 c o n c e r n {
2 f i l t e r m o d u l e {
3 i n t e r n a l s
4 e x t e r n a l s
5 c o n d i t i o n s
6 i n p u t f i l t e r s
7 o u t p u t f i l t e r s
8 }
9
10 s u p e r i m p o s i t i o n {
11 s e l e c t o r s
12 f i l t e r m o d u l e s
13 a n n o t a t i o n s
14 c o n s t r a i n t s
15 }
16
17 i m p l e m e n t a t i o n
18 }
Listing 2.1: Abstract concern template
2.2 Composition Filters in Compose
?
The working of a filter module is depicted in Figure 2.1. A filter module can contain input and output filters. The difference between these two sets of filters is that the first is used to filter on incoming messages, while the second is used to filter on outgoing messages. The return of a method is not considered an outgoing message. A filter has three parts: a filter identifier, a filter type, and one or more filter elements. A filter element exists out of an optional condition part, a matching part, and a substitution part. These parts are shown below:
identif ier
z }| {
stalker f ilter :
f ilter type
z }| {
Dispatch = {
condition part
z }| {
!pacmanIsEvil=>
matching part
z }| {
[∗.getN extM ove]
substitution part
z }| {
stalk strategy.getN extM ove }
A filter identifier is a unique name for a filter in a filter module. Filters match when both the condition part and the matching part evaluate to true. In the demonstrated filter, every message where the selector isgetNextMovematches. If an asterisk (*) is used in the target, every target will match. If the condition part and the matching part are true, the message is substituted with the values provided in the substitution part. How these values are substituted, and how the message continues, depends on the type of filter used.
Chapter 2 Compose
?
At the moment, there are four basic filter types defined in Compose
?
. It is, however, possible to write custom filter types.Dispatch If the message is accepted, it is dispatched to the specified target of the message, otherwise the message continues to the subsequent filter. This filter type can only be used for input filters;
Send If the message is accepted, it is sent to the specified target of the message, otherwise the message continues to the subsequent filter. This filter type can only be used for output filters;
Error If the filter rejects the message, it raises an exception, otherwise the message continues to the next filter in the set;
Meta If the message is accepted, the message is sent as a parameter of another
meta message to an internal or external object, otherwise the message just continues to the next filter. The object that receives the meta message can observe and manipulate the message and can re-activate the execution of the message.
The identifier pacmanIsEvil, used in the condition part, must be declared in the conditions section of a filter module. Targets that are used in a filter can be declared as internal or external. An internal is an object that is unique for each instance of a filter module, while an external is an object that is shared between filter modules.
Filter modules are superimposed on classes using filter module binding, which speci-fies a selection of objects on the one side, and a filter module on the other side. The selec-tion is specified in a selector definiselec-tion. This selector definiselec-tion uses predicates to select objects, such as isClassWithNameInList, isNamespaceWithName, and namespaceHasClass. In addition to filter modules, it is possible to bind conditions, methods, and annotations to classes using superimposition.
The last part of the concern is the implementation part, which can be used to define the behavior of a concern. For a logging concern, for example, we can define specific log functions and use them as internal.
2.3
Demonstrating Example
To illustrate the Compose
?
toolset, this section introduces a Pacman example. ThePacman game is a classic arcade game in which the user, represented by pacman, moves in a maze to eat vitamins. Meanwhile, a number of ghosts try to catch and eat pacman. There are, however, four mega vitamins in the maze that make pacman evil. In its evil state, pacman can eat ghosts. A simple list of requirements for the Pacman game is briefly discussed here:
• One live is taken from pacman when eaten by a ghost;
• A game should end when pacman has no more lives;
• The score of a game should increase when pacman eats a vitamin or a ghost;
• A user should be able to use a keyboard to move pacman around the maze;
2.3 Demonstrating Example
• Ghosts should know where pacman is located;
• Ghosts should hunt or flee from pacman, depending on the state of pacman.
2.3.1 Initial Object-Oriented Design
Figure 2.2 shows an initial object-oriented design for the Pacman game. Note that this UML class diagram does not show the trivial accessors. The classes in this diagram are:
Game This class encapsulates the control flow and controls the state of a
game;
Ghost This class is a representation of a ghost chasing pacman. Its main attribute is a property that indicates whether it is scared or not (depending on the evil state of pacman);
GhostView This class is responsible for painting ghosts;
Glyph This is the superclass of all mobile objects (pacman and ghosts). It contains common information like direction and speed;
Keyboard This class accepts all keyboard input and makes it available to pac-man;
Main This is the entry point of a game;
Pacman This is a representation of the user controlled element in the game. Its main attribute is a property that indicates whether pacman is evil or not;
PacmanView This class is responsible for painting pacman;
RandomStrategy By using this strategy, ghosts move in random directions;
View This class is responsible for painting a maze;
World This class has all the information about a maze. It knows where the vitamins, mega vitamins and most importantly the walls are. Every class derived from classGlyphchecks whether movement in the desired direction is possible.
2.3.2 Completing the Pacman Example
The initial object-oriented design, described in the previous section, does not implement all the stated system requirements. The missing requirements are:
• The application does not maintain a score for the user;
• Ghosts move in random directions instead of chasing or fleeing from pacman.
In the next sections, we describe why and how to implement these requirements in the
Compose
?
language.2.3.2.1 Implementation of Scoring
Chapter 2 Compose
?
2.3 Demonstrating Example
score), World (updating score), Main (painting score). Thus scoring is an example of a crosscutting concern.
To implement scoring in the Compose
?
language, we divide the implementation into two parts. The first part is a Compose?
concern definition stating which filter modules to superimpose. Listing 2.2 shows an example Compose?
concern definition of scoring.1 c o n c e r n D y n a m i c S c o r i n g in P a c m a n {
Listing 2.2: DynamicScoringconcern in Compose
?
This concern definition is calledDynamicScoring(line 1) and contains two parts. The first part is the declaration of a filter module called dynamicscoring (lines 2–11). This filter module contains onemeta filter calledscore_filter (line 6). This filter intercepts five relevant calls and sends the message in a reified form to an instance of classScore. The final part of the concern definition is the superimposition part (lines 12–18). This part defines that the filter moduledynamicscoringis to be superimposed on the classes
World,Game and Main.
The final part of the scoring concern is the so-called implementation part. This part is defined by a classScore. Listing 2.3 shows an example implementation of class Score
. Instances of this class receive the messages sent by score_filter and subsequently perform the events related to the scoring concern. In this way, all scoring events are encapsulated in one class and one Compose
?
concern definition.2.3.2.2 Implementation of Dynamic Strategy
The last system requirement that we need to implement is the dynamic strategy of ghosts. This means that a ghost should, depending on the state of pacman, hunt or flee from pacman. We can implement this concern by using the strategy design pattern. However, in this way, we need to modify the existing code. This is not the case when we use Compose
?
dispatch filters. Listing 2.4 demonstrates this.This concern uses dispatch filters to intercept calls to method getNextMove of the classRandomStrategy. These calls are redirected to eitherStalkerStrategy.getNextMove
Chapter 2 Compose
?
Listing 2.3: Implementation of classScore
first filter, which dispatches the intercepted call to methodStalkerStrategy.getNextMove
2.4 Compose
?
Architecture1 c o n c e r n D y n a m i c S t r a t e g y in P a c m a n {
2 f i l t e r m o d u l e d y n a m i c s t r a t e g y {
3 i n t e r n a l s
4 s t a l k _ s t r a t e g y : p a c m a n . S t r a t e g i e s . S t a l k e r S t r a t e g y ;
5 f l e e _ s t r a t e g y : p a c m a n . S t r a t e g i e s . F l e e S t r a t e g y ;
6 c o n d i t i o n s
7 p a c m a n I s E v i l : p a c m a n . P a c m a n . i s E v i l () ;
8 i n p u t f i l t e r s
9 s t a l k e r _ f i l t e r : D i s p a t c h = {! p a c m a n I s E v i l = >
10 [*. g e t N e x t M o v e ] s t a l k _ s t r a t e g y . g e t N e x t M o v e };
11 f l e e _ f i l t e r : D i s p a t c h = {
12 [*. g e t N e x t M o v e ] f l e e _ s t r a t e g y . g e t N e x t M o v e }
13 }
14 s u p e r i m p o s i t i o n {
15 s e l e c t o r s
16 r a n d o m = { C | i s C l a s s W i t h N a m e ( C ,
17 ’ p a c m a n . S t r a t e g i e s . R a n d o m S t r a t e g y ’ ) };
18 f i l t e r m o d u l e s
19 r a n d o m < - d y n a m i c s t r a t e g y ;
20 }
21 }
Listing 2.4: DynamicStrategyconcern in Compose
?
Chapter 2 Compose
?
2.4
Compose
?
Architecture
An overview of the Compose
?
architecture is illustrated in Figure 2.3. The Compose?
architecture can be divided in four layers [36]: IDE, compile time, adaptation, and runtime.2.4.1 Integrated Development Environment
Some of the purposes of the Integrated Development Environment (IDE) layer are to interface with the native IDE and to create a build configuration. In the build config-uration it is specified which source files and settings are required to build a Compose
?
application. After creating the build configuration the compile time is started.The creation of a build configuration can be done manually or by using a plug-in. Examples of these plug-ins are the Visual Studio add-in for Compose
?
/.NET and the Eclipse plug-in for Compose?
/J and Compose?
/C.2.4.2 Compile Time
The compile time layer is platform independent and reasons about the correctness of the composition filter implementation with respect to the program. This allows the target program to be build by the adaptation.
The compile time ‘pre-processes’ the composition filter specifications by parsing the specification, resolving the references, and checking its consistency. To provide an ex-tensible architecture to facilitate this process, a blackboard architecture is chosen. This means that the compile time uses a general knowledge base, which is called the ‘reposi-tory’. This knowledge base contains the structure and metadata of the program. Differ-ent modules can use this knowledge base to execute their activities. Examples of modules within analysis and validation are the three modules SANE, LOLA and FILTH. These three modules are responsible for (some) of the analysis and validation of the super imposition and its selectors.
2.4.3 Adaptation
The adaptation layer consists of the program manipulation, harvester, and code gener-ator. These components connect the platform independent compile time to the target platform. The harvester is responsible for gathering the structure and the annotations within the source program and adding this information to the knowledge base. The code generation generates a reduced copy of the knowledge base and the weaving specifica-tion. This weaving specification is then used by the weaver, which is contained in the program manipulation component, to weave in the calls to the runtime into the target program. The end result of the adaptation layer is the woven target program. This program interfaces with the runtime.
2.4.4 Runtime
2.5 Platforms
reduced copy of the knowledge base, containing the necessary information for filter eval-uation and execution, is enclosed with the runtime. Method calls are intercepted by the runtime. The runtime evaluates the filter set for the intercepted message and exe-cutes the corresponding filter actions. The runtime also facilitates the debugging of the composition filter implementations.
2.5
Platforms
The composition filters concept of Compose
?
can be applied to any programminglan-guage, given that certain assumptions are met. Currently, Compose
?
supports threeplatforms: .NET, Java and C. For each platform different tools are used for compilation and weaving. They all share the same platform independent compile-time.
Compose
?
/.NET targets the .NET platform. It is the oldest implementation ofCompose
?
. Its weaver operates on CIL byte code. Compose?
/.NET is programminglanguage independent as long as the programming language can be compiled to CIL code. An add-in for Visual Studio is provided for ease of development. Compose
?
/J targets the Java platform and provides a plug-in for integration with Eclipse. Compose?
/C contains support for the C programming language. The implementation is different from the Java and .NET counterparts, because it does not have a run-time environment. The filter logic is woven directly in the source code. Because the language C is not based on objects, filters are woven on functions based on membership of sets of functions. Like the Java platform, Compose?
/C provides a plug-in for Eclipse.2.6
Features Specific to Compose
?
The Composition Filters approach uses a restricted (pattern matching) language to define filters. This language makes it possible to reason about the semantics of the concern. Compose
?
offers three features that use this possibility, which originate in more control and correctness over an application under construction. These features are:Ordering of filter modules
It is possible to specify how the superimposition of filter modules should be ordered. Ordering constraints can be specified in a fixed, conditional, or partial manner. A fixed ordering can be calculated exactly, whereas a conditional ordering is depen-dent on the result of filter execution and therefore evaluated at runtime. When there are multiple valid orderings of filter modules on a join point, partial order-ing constraints can be applied to reduce this number. These constraints can be declared in the concern definition;
Filter consistency checking
When superimposition is applied, Compose
?
is able to detect if the ordering and conjunction of filters creates a conflict. For example, imagine a set of filters where the first filter only lets method m continue and the second filter only accepts for methods a and b. Because only method m can reach the second filter, it neverChapter 2 Compose
?
Reason about semantic problems
When multiple pieces of advice are added to the same join point, Compose
?
canreason about problems that may occur. An example of such a conflict is the
situation where a real-time filter is followed by a wait filter. Because the wait filter can wait indefinitely, the real-time property imposed by the real-time filter may be violated.
The above mentioned conflict analyzers all work on the assumption that the behavior of every filter is well-defined. This is not the case for the meta filter, its user-undefined, and therefore unpredictable, behavior poses a problem to the analysis tools.
Furthermore, Compose
?
is extended with features that enhance the usability. These features are briefly described below:Integrated Development Environment support
The Compose
?
implementations all have an IDE plug-in; Compose?
/.NET forVisual Studio, Compose
?
/J and Compose?
/C for Eclipse;Debugging support
The debugger shows the flow of messages through the filters. It is possible to place breakpoints to view the state of the filters;
Incremental building process
When a project is build and not all the modules are changed, incremental building saves time.
Some language properties of Compose
?
can also be seen as features, being:Language independent concerns
A Compose
?
concern can be used for all the Compose?
platforms, because thecomposition filters approach is language independent;
Reusable concerns
The concerns are easy to reuse, through the dynamic filter modules and the selector language;
Expressive selector language
Program elements of an implementation language can be used to select a set of objects to superimpose on;
Support for annotations
Using the selector, annotations can be woven at program elements. At the moment annotations can be used for superimposition.
2.7
StarLight
In 2006 development began on a lightweight branch of Compose
?
/.NET, called2.7 StarLight
2.7.1 StarLight Architecture
This section describes differences between the StarLight Architecture and the Compose
?
architecture, as shown in Figure 2.3.Integrated Development Environment
StarLight has its own Visual Studio integration. This integration provides the following functionality:
• A project service to open and use StarLight projects in Visual Studio;
• Syntax highlighting and IntelliSense for concern files;
• MSBuild is used to analyze and weave the concerns.
Compile Time
StarLight uses the same compile time as Compose
?
. An inlining engine is added to the compile time. This inlining engine translates the filter set to a platform independent abstract instruction model for each individual message. This abstract instruction model represents a procedural structure. It can be easily translated to program code for a specific procedural platform.Adaptation
StarLight has its own adaptation layer. It contains an analyzer that creates the language model. It also contains a weaver that translates the abstract instruction models for each message to IL code and weaves this code in the appropriate places of the target assemblies.
Runtime
The StarLight version does not have a runtime, because a complete filter set trans-lation is woven in the target program.
2.7.2 Explicit Modeling of the Returning Flow
Originally, composition filters only modeled the calling flow: sending the message from the sender to the target. There is, however, also a returning flow: the action of returning the control to the sender after the target has ended the execution of the message. This returning flow possibly contains a return value, but this is not necessary. In the new StarLight implementation we also want to model this returning flow explicitly. This makes it possible to execute filter actions after the message has been dispatched.
Figure 2.4 shows the returning flow in the composition filters model. Both input filters as output filters have a returning flow.
When is the Flow Returned? Explicit modeling of the returning flow raises ques-tions like when is the flow returned and is there always a returning flow. From the composition filters perspective, it is the filter action that decides whether to return the flow or to continue to the next filter. There are actually three possibilities, as shown in Figure 2.5.
Continue
Chapter 2 Compose
?
Figure 2.4: Explicit modeling of the returning flow
Return
A filter action can return the flow. In this case, the calling flow turns into a returning flow. An example of a filter action that returns the flow is theDispatch
action.
Exit
The third option available for filter actions is to exit the filter set. This equals an exception or abnormal return. An example of a filter action that exits the filter set is theError action.
4-Action Filter Types Because StarLight models the returning flow explicitly, it is possible to execute actions on the returning flow. The actions that are executed, are specified by the filter type. Therefore, instead of 2-action filter types, StarLight has 4-action filter types.
Evaluation of Filters on the Returning Flow? The filters are not evaluated on the returning flow. They are only evaluated on the calling flow. The filter action that is executed by a filter on the returning flow depends on whether the filter accepted or
(a) Continue (b) Return (c) Exit
2.7 StarLight
Calling Flow Returning Flow
Accept accept-call action accept-return action
Reject reject-call action reject-return action
Table 2.1: The four filter actions of a filter type.
rejected on the calling flow.
The actions on the returning flow are executed in reverse order of the filter set.
2.7.3 Defining New Filter Types and Filter Actions
StarLight provides a new way to define new filter types and new filter actions. Ac-tually, there are no primitive filter types and filter actions anymore, as there are in Compose
?
. There are, however, certain common filter types and filter actions provided by the StarLight API. But they are defined in the same way as any other new filter type or filter action.1 [ F i l t e r A c t i o n A t t r i b u t e ( " L o g g i n g I n A c t i o n " , F i l t e r A c t i o n A t t r i b u t e . F i l t e r F l o w B e h a v i o r . C o n t i n u e , F i l t e r A c t i o n A t t r i b u t e .
M e s s a g e S u b s t i t u t i o n B e h a v i o r . O r i g i n a l ) ]
2 p u b l i c c l a s s L o g g i n g I n A c t i o n : F i l t e r A c t i o n
3 {
4 p u b l i c o v e r r i d e v o i d E x e c u t e ( J o i n P o i n t C o n t e x t c o n t e x t )
5 {
6 // Logging - in i m p l e m e n t a t i o n
7 }
8 }
Listing 2.5: Defining theLoggingInfilter actions
New filter actions can be defined by extending the FilterAction class. Listing 2.5 shows an example of a new filter action. By overriding the Executemethod, the action can be implemented. When the action is executed at runtime, a call is done to itsExecute
method. A mandatory custom attribute on the filter action specifies the following information:
• The name of the filter action;
• The flow behavior of the filter action: continue, return or exit;
• The substitution behavior of the filter action. This specifies whether the message continues substituted or not after the filter action. An example of a filter action that leaves the message substituted is theSubstitution action.
1 [ F i l t e r T y p e A t t r i b u t e ( " L o g g i n g " , " L o g g i n g I n A c t i o n " , F i l t e r A c t i o n . C o n t i n u e A c t i o n , " L o g g i n g O u t A c t i o n " , F i l t e r A c t i o n . C o n t i n u e A c t i o n ) ]
2 c l a s s L o g g i n g F i l t e r T y p e : F i l t e r T y p e
3 {
4 // No i m p l e m e n t a t i o n
5 }
Chapter 2 Compose
?
A new filter type can be defined by extending theFilterType class. Listing 2.6 shows an example of a new filter type. This class has no implementation methods, as it is not used at runtime. It is only used to specify a new filter type at compile time. A mandatory custom attribute specifies the following information:
• The name of the filt