1 Introduction
shows the evolution of linguistic constructs aimed at achieving isola-
tion of a concern: in imperative and functional languages procedures
and functions enable top-down reasoning. In object-oriented languages,
class encapsulation limits the effects of change to localised portion of
code and inheritance allows one to incrementally evolve a component
by adding new features or redefining existing features.
However, even optimal functional decompositions omit to encap-
sulate some concerns because they cross-cut the entire system, or parts
of it. As an example, suppose that a Java class is used to describe the
pure functionality of certain objects. Additional separate issues may
include the definition of:
• constraints on sequences of applicable operations (e.g., to get in-
formation from an object one must first apply a setup operation,
and then one of a set of assignment operations);
• synchronisation operations to constrain concurrent access to the
object (e.g., a consumer trying to read a datum from a queue must
be suspended if the queue is empty);
• how objects are distributed on the nodes of a network, either stat-
ically or through dynamic migration;
• security or accounting policies (e.g., to get information from an
object one must first ask some permission).
These kinds of separate issues that cannot be easily encapsulated in
functional modules have been called aspects [37]. Such concerns are
typically scattered over several units of encapsulation. Single mod-
ules become mix-ins of totally different concerns entangled in the same
piece of code. Aspect oriented programming is the emerging research field
born to identify appropriate linguistic means for isolating scattered
concerns.
The basic idea is having an aspect oriented language providing:
1. some syntactic sugar to separate aspect code in novel modular
units;
2. a way to declare join points that are the points in the functional
code where aspect code will merge with it.
An engine, called aspect weaver, is responsible for mixing — either at
compile time or at run time — functional and aspect code in order to
produce a running coherent system.
2
The best known representative proposal in this area is AspectJ [70,
71] from Xerox Parc. The language provides an aspect oriented ap-
proach in which an aspect is a first class entity similar to a Java class.
An aspect can have its own methods and attributes; furthermore it can
insinuate any arbitrary piece of code in the Java classes composing the
system. The join points between aspects and classes are method signa-
tures, that can be denoted also by using pattern matching, thanks to a
regular expression syntax. Insinuated code has to be executed either at
the very beginning or at the very end of the method acting as join point.
However, it may freely manipulate class data to obtain the desired be-
haviour.
This is a very powerful mechanism that puts total control in pro-
grammers’ hands, but it does not completely resolve the main issue
of separation of concern. In fact, classes and aspects have to be de-
signed together, because of the holes introduced in the information hid-
ing boundaries.
This thesis argues that the problem of separating every concerns, still
maintaining all the logical barriers between each of them, is not resolv-
able in its most general case and it proposes the less ambitious goal
of trying to separate some specific, although important, predetermined
concerns. In our vision, aspects should have their own separated pro-
duction cycle. However, breaking encapsulation makes this very diffi-
cult, if not impossible. Thus our proposal gives up some flexibility and
power in favour of understandability and ease of change.
We propose a new language, called Malaj, which has its roots in two
basic assumptions:
1. only a (relatively small) set of possible aspects is worth separating
from functional code;
2. having in mind a well defined aspect, it is possible to provide
ad hoc constructs addressing this particular issue in a disciplined
way.
Malaj constructs, thanks to their ad hoc nature, may have been care-
fully designed with “surgical” visibility on implementation secrets of
functional modules, thus their relation with traditional object oriented
code can be studied a priori. Furthermore, designing an aspect specific
language may provide programmers a new tool embodying appropri-
ate successful guidelines coming from the research work of experts of
the field.
We focus on the particular domain of distributed systems. Accord-
ing to the definition given in [12], a distributed system is a collection
3
1 Introduction
of automata whose distribution is transparent to the users so that the
system appears as one coherent machine. Users are not directly aware
of the network, namely there are several machines, with different loc-
ations, storage replication, load balancing, concurrent processes, net-
work failures, access protection, etc.. This makes it a perfect candidate
for aspect oriented programming, because functionality is perceived as
well separated issue, a goal to be reached notwithstanding any change
in other concerns.
Thus, we concentrate on three aspects of distributed systems:
synchronisation: the ability of coordinating parallel threads of func-
tionality;
network relocation: the ability of relocating the deployment status of
piece of functionality;
exceptional behaviour: the ability of coping with exceptional states
of the system.
The thesis is organised as follows: Chapter 2 presents the related
problems of entangling and scattering of concerns and their relevance
in modern software engineering; Chapter 3 details the approach fol-
lowed by AspectJ and its drawbacks and pitfalls; Chapter 4 surveys
other existing approaches; Chapter 5 describes Malaj, our aspect ori-
ented language; Chapter 6 concludes the thesis by summing up the
contribution of our work and envisioning improvements and further
research.
4
2 Aspects of Complex Systems
The astrologers call the evil influences
of the stars evil aspects.
— Francis Bacon
Separation of concerns is a very general and very powerful prin-
ciple that applies to any large and complex human activity. It is espe-
cially used in software to express the ability to identify, describe, and
handle important and critical facets of a software system separately.
Concerns are always related to a goal a stakeholder1 wants to achieve
with a software system or to anticipations or expectations he or she has
on a system. A concern can be seen as a perspective that is taken by
a stakeholder on a system. This is particularly true for systems which
deploy a number of different technologies. For instance, building the
software for controlling airplanes needs deep understanding of hard-
ware and physical issues.
Well organized software systems are partitioned in modular units
each addressing a well defined concern. Such parts are developed in
relative isolation and then assembled to produce the whole system. A
clean and explicit separation of concerns reduces the complexity of the
description of the individual problems, thereby increasing the compre-
hensibility of the complete system.
Some concerns are only relevant in certain development stages, even
though more often they are relevant during the complete software life
cycle. Separation of concerns supports evolution and maintenance, fa-
cilitates reuse, it also enables consistency checking and correspondence
between specification and implementation.
Even when the system is finished2 having different models corres-
ponding to different perspectives is a powerful tool useful for inferring
1With the term stakeholder we mean any person involved in the life cycle of the sys-
tem: the entrepreneur who organizes the business venture and assumes the risk for
it, the designer who sketches the system architecture, the programmer who writes
the code, one of the marketing people who sell the product, the end-user who ex-
ploits the software in his own business, the customizer who adapts it to specific
demands, etc.
2Of course, the notion of “finished system” is a controversial one: actually, complex
5
2 Aspects of Complex Systems
properties, deducting explanations of observed behaviors, or envision-
ing expected responses [54].
Separation and isolation are crucial if development of single parts
has to be carried out concurrently to reduce time to market: people
involved take different perspectives, exploit different development
strategies, and have different responsibilities [20]. Only elements that
are important for each stakeholder should be visible in her or his own
viewpoint.
During implementation phases programming languages need to
support programmers in isolating concerns and integrating them in co-
herent systems.
Traditionally, programming languages provide constructs to parti-
tion the software in modular units of functionality. Such parts are then
assembled to get the desired functionality of the whole system. Tra-
ditional languages provide procedures and functions. Object-oriented
languages break up programs in objects isolated by class encapsulation:
this boundary limits the influence of pieces of code to localised regions;
moreover, inheritance allows one to incrementally evolve components
by adding new features or redefining existing features.
However, sometimes a concern is not easily factored out in a func-
tional unit, because it cross-cuts the entire system, or parts of it. Syn-
chronization, memory management, network distribution, load balan-
cing, error checking, profiling, security are all aspects of computer prob-
lems that are unlikely to be separated in functional units. During last
years hard research work was carried on concurrency, network distri-
bution, security, etc. and skillful professionals in these areas are now
emerging. Ideally, we would like to entrust such experts to implement
the part of the system that impact on these aspects. However, it is typic-
ally not easy to give responsibility for these concerns to people different
from implementors of other parts of the system, because the relevant
code is often scattered across multiple components, tangled with other
unrelated code.
Scattering is a problem because it hinders the possibility to reason
about a concern in isolation, by temporarily ignoring what is currently
irrelevent. For example, it may be difficult to predict deadlock or to
detect deadlock when it occurs because it requires reasoning about two
or more units at a time. Suppose (see Listing 2.1) we have a method
systems are never completely finished. They can be defective because they do not
implement (or implement imperfectly) all the specifications given before building,
but also because they neither cope with all requirements rose during the building
process, nor with all needs that environmental changes impose. Here finished is to
be taken as “deployed on users’ machines”
6
public void removeUseless(Folder file){
synchronized (file){
if (file.isUseless()){
Cabinet directory = file.getCabinet();
synchronized (directory){
directory.remove(file);
}
}
}
}
Listing 2.1: A method with synchronisation machinery
public void updateFolders(Cabinet dir){
synchronized (dir){
for (Folder f = dir.first(); f != null; f = dir.next(f)){
synchronized (f){
f.update();
}
}
}
}
Listing 2.2: Another method with synchronisation machinery
removeUseless() in a database class. This method is called during the
clean-up phase of the system. It receives a Folder object as parameter:
this object represents some folder in the database system. The method
controls the uselessness of the Folder by calling isUseless(). In
order to act on the Folder, the method acquires the object lock of
the Folder. If the Folder is really useless, the method simply re-
move the Folder from the Cabinet. The Cabinet can be found by
a getCabinet() method, and the Folder can be deleted by invoking
remove(). Just as with the Folder object, before it is possible act on
the Cabinet object it is necessary to lock it. Now, let us suppose we
have another method called updateFolders() (see Listing 2.2. This
method receives a Cabinet object that represents a cabinet in the sys-
tem. In order to act on this Cabinet, its object lock is needed. The act
of updating the Cabinet is done by looping through all the Folders
in the Cabinet and calling the update() method. Again, the updating
of Folders needs the Folder lock.
7
2 Aspects of Complex Systems
This code can cause a deadlock. Suppose that a thread T1 calls the
method updateFolders(). It acquires the lock L1 of the Cabinet.
Now assume the removeUseless() method is called by a thread T2. It
locks the Folder with a lock L2 and, after determining that it is indeed
useless, it proceeds in locking the Cabinet with its lock L1 in order to
delete the Folder. At this point T2 blocks and waits for the releasing
of the Cabinet object lock.
But when the Folder on which removeUseless() is working is
now accessed by updateFolders(), it tries to grab the object lock L2.
The deadlock situation arises because the removeUseless() method
has the Folder lock L2 and it is waiting for the Cabinet lock L1 to be
freed. In parallel the updateFolders() method holds the Cabinet
lock L1 and it is waiting for the Folder lock L2 to be freed. This an-
omalous situation is hard to detect, but it is common to find code like
this if it is written by people with no knowledge of each other work. A
solution to the deadlock situation involves a major redesign of the sys-
tem, with collaboration between the writer of removeUseless() and
the one of updateFolders().
The problem dual to scattering is code tangling: modular units are
often mixins of pieces of code that addresses different, unrelated con-
cerns. For example, Listing 2.3 shows a synchronised Stack class. The
core code is composed by statements that implement the stack func-
tionality: a data structure in which elements can be accessed according
the last in, first out policy. But the class contains also statements (indic-
ated by // synch) that implement synchronisation among methods:
no concurrent calls to pop() and push() are allowed, calls to pop() wait
for elements in the stack, calls to push() wait for empty slots.
In the example functionality and synchronisation are tangled to-
gether, thus they have to be written at the same time. The same problem
applies to the code in Listings 2.1 and 2.2. Moreover, because between
the two there are no barriers of any kind, every change to one may have
side effects to the other.
Thus, the question is: “how non functional aspects, generally scattered
and tangled within functional component can be managed as components as
well?”. Information hiding and encapsulation is the traditional way
to introduce division of labor in software production, limiting the ef-
fects of change to localized portions of code, that become the bricks for
building the whole system, but we need some new ideas to consider
aspects real components, i.e., units of third-party composition [63].
8
package stack;
interface Constants{
final int StackSize = 10;
}
public class Stack implements Constants{
synchronized // synch
public void push(Object o){
while (top == StackSize−1) try{ // synch
wait(); // synch
} catch (InterruptedException e){ // synch
e.printStackTrace(); // synch
}
elements[++top] = o;
if (top == 0) notifyAll(); // synch
}
synchronized // synch
public Object pop(){
Object ris;
while (top == −1) try{ // synch
wait(); // synch
} catch (InterruptedException e){ // synch
e.printStackTrace(); // synch
}
ris = elements[top−−];
if (top == StackSize−2) notifyAll(); // synch
return ris;
}
private int top = −1;
private Object[] elements = new Object[StackSize];
}
Listing 2.3: Scattering of the synchronisation concern
9