Previous paper

Next paper


Aspect Refinement – Unifying AOP and Stepwise Refinement

Sven Apel, Department of Informatics and Mathematics, University of Passau
Christian Kästner, School of Computer Science, University of Magdeburg
Thomas Leich, Department of Applied Informatics, Metop Research Institute
Gunter Saake, School of Computer Science, University of Magdeburg

 

space REFEREED
PAPER


PDF Icon
PDF Version

Abstract

Stepwise refinement (SWR) is fundamental to software engineering. As aspectoriented programming (AOP) is gaining momentum in software development, aspects should be considered in the light of SWR. In this paper, we elaborate the notion of aspect refinement that unifies AOP and SWR at the architectural level. To reflect this unification to the programming language level, we present an implementation technique for refining aspects based on mixin composition along with a set of language mechanisms for refining all kinds of structural elements of aspects in a uniform way (methods, pointcuts, advice). To underpin our proposal, we contribute a fully functional compiler on top of AspectJ, present a non-trivial, medium-sized case study, and derive a set of programming guidelines.


1 INTRODUCTION

Aspect-oriented programming (AOP) is a novel programming paradigm to implement complex software in a modular way [16]. Aspects, the main abstraction mechanism of AOP, modularize crosscutting concerns. Without aspects, the implementation of such concerns would result in code scattering, tangling, and replication.

AOP is gaining momentum and pervading more and more phases and parts of software engineering [12]. This paper relates AOP to stepwise refinement (SWR), a fundamental approach to software development [33, 13, 26, 8]. By adding new program details in a stepwise manner, the programmer breaks down complex software into manageable pieces (modules). The resulting program structure is supposed to be more comprehensible, reusable, and customizable – compared to a monolithic structure [33, 26, 8]. By its incremental development methodology SWR, facilitates software evolution, where each increment in program functionality reflects an evolutionary development step. Sometimes these increments are called refinements [8, 25].

Since most software is developed, adapted, and evolved in a more or less incremental way [29], it is desirable that modern programming paradigms reflect this by explicit support of SWR. That is, the principles of SWR should be taken into account when designing AOP languages, e.g., all software artifacts are subject of the subsequent refinement [8]. To this day, aspects have not been not adequately studied and understood with respect to SWR, with some notable exceptions [24, 21].

In this paper, we develop the idea of aspect refinement (AR), which is the application of SWR principles to AOP [4]. It is a design methodology to incrementally develop, adapt, and evolve aspects by means of SWR. Our study of AR explores the differences of AOP and SWR and proposes a convergence of both. AR unifies aspects and classes with respect to SWR and improves the reusability and customizability of aspects, e.g., it enables the adaptation of aspects to changed requirements or to a modified base program.

In prior work we outlined the mere possibility of refining aspects and examined the consequences for software development [4, 5, 1]. In this paper we explore how to support AR at the language level by taking AspectJ1 as an archetype as well as by a non-trivial case study. We introduce the notion of mixin-based inheritance [11] to AOP. Mixin-based aspect inheritance explicitly supports SWR at the language level by introducing mixin capabilities to aspects. Though most aspect languages such as AspectJ support a limited form of aspect inheritance, they do not do so flexibly enough to express refinements of aspects and their structural elements, as we will discuss. Mixin-based aspect inheritance overcomes this limitation and enables programmers to freely compose aspects and their refinements. This provides the required flexibility to reuse, customize, and evolve aspects in the sense of SWR.

Furthermore, we propose a uniform approach for refining all kinds of structural elements of aspects. Specifically, we propose mechanisms for refining pointcuts (pointcut refinement) and advice (named advice, advice refinement), which are tailored to AspectJ-like languages.

We demonstrate the practical applicability of our language proposal by providing a fully functional compiler on top of AspectJ. We use our compiler to apply AR to a non-trivial, medium-sized case study. Based on this study, we propose a set of programming guidelines for applying AR. In this paper we make the following contributions:

  • an elaboration of the idea of AR that unifies AOP and SWR
  • the mechanism of mixin-based aspect inheritance, which is accompanied by language mechanisms for refining aspects, pointcuts, and advice
  • a fully functional compiler on top of AspectJ that implements our proposal
  • a case study and a set of programming guidelines

2 ASPECTS AND STEPWISE REFINEMENT

AHEAD – The Big Picture

AHEAD (Algebraic Hierarchical Equations for Application Design) is an architectural model for SWR and a framework for large-scale program synthesis based on features [8]. A feature is an increment in program functionality and corresponds to a development step. The goal of AHEAD is to synthesize software (individual programs) by composing a series of desired features.

AHEAD unifies several approaches of SWR and scales them to programming in the large. First, AHEAD generalizes the operations a feature performs on a given base program: (a) the introduction of new functionality and (b) the modification of existing functionality. Second, AHEAD scales the idea of program refinement to arbitrary kinds of software artifacts (e.g., code, test cases, documentation, makefiles), which is captured by the underlying principle of uniformity: features are implemented by a diverse selection of software artifacts and any kind of software artifact can be subject to subsequent refinement [8].

In AHEAD, features are modeled as functions. A constant function (a.k.a. constant) represents a base program. All other functions take a program as input and return a modified program as output. That is, functions represent program refinements that implement program features. For example, ‘Add • X’ adds feature Add to program X, where ‘•’ denotes function composition. A generated program is represented by a named feature expression, e.g., ‘Prog = Add • Base’.

In AHEAD, each feature is represented by a containment hierarchy, which is a directory that exhibits a subdirectory structure to store the feature’s artifacts. Composing features means composing containment hierarchies and, to this end, composing respective artifacts by mixin composition [11, 31]. Hence, for each artifact type, a different implementation of the composition operator has to be provided. For example, Figure 1 depicts a Java class Add (Lines 1–3), which is part of feature Base and a refinement (Lines 4–7), which is part of feature Buffer. The refinement adds a new field (Line 5) and extends an existing method via overriding (Line 6); calling super invokes the refined method. The refinement is implemented in Jak [8], a Java language extension that introduces basically the keyword refines.

Figure 1: A class and a refinement in Jak.

Aspects – Just Another Kind of Software Artifact

In AHEAD, a feature is implemented by a collection of collaborating software artifacts of varying types. In this sense, an aspect is just another kind of software artifact. The AHEAD principle of uniformity has an interesting consequence: since aspects are artifacts as any others, it is natural to refine them in a SWR manner as well. That is, a feature may not only extend and modify classes via subsequent refinement but also aspects, which we call AR. Hence, AR is the consequential application of SWR principles to the world of AOP.

With AR, aspects can be adapted, customized, evolved, as can all other software artifacts. In each development step, aspects may be refined, i.e., extended and modified. We focus on three use cases of AR, which may overlap in parts:

  1. Adapting aspects to the changes made to a base program, e.g., join points have changed or new join points occur.
  2. Tailoring aspects to changing user requirements, e.g., the user needs an aspect to implement a new design decision.
  3. Decomposing aspects to decouple them from a specific configuration of the base program, e.g., a base program in different configurations demands aspects in different variants.

Applying AR to deal with the above situations means decomposing and subsequently composing an aspect out of a base aspect and a series of refinements. Refinements should be freely combinable – of course, in the limits of desired program behavior. This flexibility facilitates reuse of aspect code. The user-driven composition of aspects and refinements customizes aspect-specific functionality. AR enables a similar improvement in reusability and customizability of aspect code as the analogous object-oriented mechanisms do for classes [11, 31, 8].

AR unifies classes and aspects with respect to subsequent refinement. An advantage of this view is that several ideas of class refinement can be mapped directly to aspects, as we will show. But more interesting is the fact that it becomes possible to refine also aspect-specific constructs, in particular pointcuts and advice, which opens new possibilities of aspect reuse and customization.

An Example of Aspect Refinement

Figure 2 illustrates the evolution of a program developed using aspects. The program contains classes for buffers and sockets as well as aspects for synchronizing concurrent access. The evolution spans four steps shown in four subfigures (Base, Sync, Stack, Socket). Each development step is explained in terms of its Java/AspectJ code and in diagram form; refinements of aspects are implemented as subaspects; aspect weaving is denoted by dashed arrows.

Base: Buffer objects store sets of data items; the class Buffer provides the methods put and get for accessing the stored items.

Sync: The aspect BufferSync synchronizes the access to the methods put and get of Buffer by invoking the methods lock and unlock.

Stack: The class Stack is introduced; in order to synchronize the access to Stack objects, the aspect StackSync refines the aspect BufferSync and broadens the set of intercepted method executions by push and pop; for that it overrides and extends the pointcut syncPC of aspect BufferSync.

Socket: The class Socket is introduced; a Socket object uses several Buffer and Stack objects. The aspect SocketSync limits the set of synchronized methods to those that are inside the control flow of Socket, i.e., method executions are synchronized only when they are initiated directly or indirectly by a Socket object. This is achieved by overriding the pointcut syncPC and restricting the set of captured join points via the pointcut cflow.

This example illustrates the usefulness of refining aspects in a stepwise manner over several development steps. Aspect refinement is a logical consequence of applying SWR principles to AOP. The incremental development process makes the evolution of the program explicit. Design decisions are encapsulated and can be modified in separation as well as combined and reused in different variants. A reasonable wish is to derive different customized program variants that share common features and reuse invariant code, e.g., ‘Sync • Base’ or ‘Socket • Stack • Sync • Base’.

Figure 2: Four steps in the evolution of a program using aspects.

Limited Language-Level Support for Aspect Refinement

Beside the advantages of AR, our example also demonstrates the shortcomings of AspectJ in supporting SWR:

Aspect inheritance: While aspect inheritance enables the refinement of aspects to some degree, it lacks flexibility to interchange and reuse refinements. Using aspect inheritance, a refinement (subaspect) is tied to a specific base aspect. Hence, refinements cannot be combined flexibly in different permutations for customization and adaptation purposes. For example, we are not able to derive different variants of our buffer example without changing code invasively.

Constrained aspect extension: Using traditional aspect inheritance in AspectJ, an aspect has to be declared abstract to be able to be refined. This means that adding a subaspect requires the programmer to modify the parent aspect. This and similar requirements2 cause a fundamental problem regarding SWR: implementing an aspect in a particular development step forces the programmer to decide whether the aspect will be refined in a subsequent step. While declaring the aspect as abstract makes it necessary to add later at least one concrete subaspect, declaring it as concrete (without modifier) the programmer prevents the subsequent refinement of the aspect.

Advice is not first-class: Advice is one of the main mechanisms of AOP [23]. A piece of advice is invoked implicitly, i.e., it executes code when an associated pointcut matches. This prevents other advice or methods from invoking it explicitly. Since advice has no name it cannot be overridden and extended by another piece of advice, inside a subsequent refinement. This prevents reusing and customizing advice code. It has been shown that advising advice is also not an adequate solution [9].

The problems sketched above show that current AOP languages, as exemplified by AspectJ, do not support SWR appropriately. Consequently, we propose an alternative approach implementing AR along with a set of language constructs.

3 MIXIN-BASED ASPECT INHERITANCE

In order to implement AR at the language level, we introduce the notion of mixinbased aspect inheritance. It supports SWR at the language level by introducing mixin capabilities to aspects. Traditional aspect inheritance is not flexible enough to express refinements of aspects and their structural elements. Mixin-based aspect inheritance overcomes this limitation by allowing refinements to be freely combined and applied to a base aspect. A set of accompanying language mechanisms enables to refine all the kinds of structural elements of an aspect, in particular pointcut refinement and advice refinement, which are tailored to AspectJ-like languages.

We use the Jak language as the archetype for expressing AR at the language level. This emphasizes the uniformity of classes and aspects with respect to refinement. Figure 3 shows a synchronization aspect (Lines 1–4) and a refinement (Lines 5–14) extending the aspect. Refinements may introduce new structural elements as well as extend existing ones, as we will explain soon. They can be applied to abstract and concrete aspects as well as to other refinements. This eliminates the dilemma of anticipating subsequently applied refinements by declaring base aspects as abstract. Moreover, it allows a series of refinements to be applied to an aspect in different permutations.

Figure 3: Adding members and extending methods via AR.

Notably, refining aspects is conceptually different from weaving aspects. Weaving two aspects modifies the base program in two independent steps. In our example this would lead to two different instances of the aspect Sync. Instead, AR results in two aspect fragments that are merged via mixin composition. That is, an aspect together with all of its refinements constitutes a final aspect that is woven once to the base program.

Adding Members and Extending Methods.

A refinement may add new members to an aspect. As shown in Figure 3, the refinement adds a field (Line 6), a pointcut (Lines 9–10), and a piece of advice (Lines 11–13). Refinements may also extend methods to reuse existing functionality (Lines 7 and 8). A method extension usually overrides and calls the overridden method via the keyword super.

Pointcut Refinement

A refinement may refine the pointcuts of an aspect. Recall our example aspect that synchronizes the access to Buffer (Fig. 2). Two refinements (Stack, Socket) override the pointcut syncPC, reuse its expression, and add new pointcut expressions that extend or constrain the set of matched join points.

In AspectJ, pointcuts have to be accessed by their fully qualified name, in our example, BufferSync.syncPC. Thus, the programmer is forced to hard-wire the aspect to be refined and the subaspect, which decreases reusability. Using mixinbased aspect inheritance the refined pointcut is accessed via super (Fig. 4). Hence, the programmer need not be aware of the actual sequence of refinements applied to a base aspect. While with standard inheritance the refinement order is fixed, with mixin-based inheritance the order is variable.

Figure 4: Altering the set of locked methods via pointcut refinement.

Semantics of pointcut refinement. The semantics of pointcut refinement is as follows: the most refined (specialized) pointcut in a series of pointcut refinements specifies when advice is executed. Which pieces of advice are executed can be defined all along the refinement chain, i.e., in every refinement of an aspect advice may be connected to a pointcut.

Figure 5 illustrates what happens when refining a pointcut (solid arrow): the most refined pointcut alters the set of matched join points (dotted arrows) and triggers the advice (dashed arrow). After refinement, the advice advises an extended set of join points (dot-dashed arrows).

Figure 5: The most refined pointcut triggers connected advice.

Advice Refinement

Named advice. Before explaining advice refinement it is necessary to introduce named advice. Named advice is a named version of advice. It can be overridden and referred to from advice inside subsequent refinements. Figure 6 depicts an aspect for synchronization that contains named advice (Lines 3–5). Named advice is defined by an optional result type (Object), an advice type (around), a name (syncMethod), an argument list (empty), an exception list (empty), a binding to a pointcut (syncPC), and an advice body. One can think of named advice as a pair of an unnamed advice and a separate method (advice method), in which the advice calls the method. The difference to this analogy is that named advice has full access to the dynamic context (proceed and join point API). Though named advice can be implemented differently (cf. Sec. 5), this view is helpful for understanding the semantics of advice refinement.

Figure 6: An aspect with named advice.

Refining named advice. In contrast to traditional advice, named advice can be refined in subsequent development steps. The key idea is to treat named advice in subsequent refinements similarly to a method. This is possible since named advice has a name, a result type, and an argument list. According to our analogy, an advice refinement simply refines the advice method by method overriding, i.e., by defining a method with the same name and signature as the named advice to be refined.

Figure 7 depicts an aspect that refines our synchronization aspect by extending its named advice. The refinement contains an advice method syncMethod (Lines 3–5) that overrides the parent named advice by counting the number of threads. The refining method must have the same name and the same signature as the parent advice. The keyword super is used to refer to the parent advice (Line 4).

Figure 7: Refining named advice.

Semantics of advice refinement. The semantics of named advice is equivalent to a virtual method, which passes the control flow to the most specialized descendant method of the inheritance chain. Mapped to advice refinement this means that, when the associated pointcut matches, the most specialized advice method is invoked. Figure 8 shows an advice method that refines a named advice (solid arrow). It is executed (dashed arrows) when the pointcut syncPC matches (dotted line). Programmers use super to navigate the refinement chain upwards. The root of the refinement chain binds the pointcut to the piece of advice.

Figure 8: Semantics of advice refinement.

Accessing contextual information. An issue that we did not address yet is which information of the exposed context of a join point should be visible to descendant advice methods. This issue arises because programmers may access the context using proceed or runtime variables as thisJoinPoint. Thus, one may use information that is not passed explicitly via the advice interface. Should refinements have unlimited access to context information and proceed?

We argue that an advice refinement should only be permitted to access those pieces of context information that are passed via the advice interface, and thus are part of the advice method signature. This would preclude invoking proceed or accessing thisJoinPoint from within a refinement of an advice method. While this restriction is not necessary for our proposal, we believe it preserves simplicity and robustness of the aspect language. Furthermore, we do not allow named advice to be invoked directly by other advice and methods – such a mechanism is out of scope
of this paper and addressed elsewhere (cf. Sec. 5).

Discussion

AR and its implementation via mixin-based aspect inheritance offer the following benefits: they allow a base aspect to be composed with a series of refinements, thus enabling to customize and reuse aspect code. Pointcut refinement decouples refinements from their immediate base aspects, thus enhancing the composability of aspects and refinements. Advice refinement promotes reuse in the same way as method extension between classes. Named advice can be reused in different variants of an aspect, thus supporting the customization of advice code.

At the beginning of the paper we identified three beneficial use cases of AR, which we now want to revisit:

  1. A programmer applies a refinement to adapt an aspect to the changes made to a base program. For example, Figure 9 shows an aspect that counts the updates of Buffer objects (Lines 1–5) and a refinement that adapts the aspect to count also executions of clear (Lines 6–8) that updates the Buffer object state as well; this is achieved by pointcut refinement (Line 7).

Figure 9: Counting the updates of Buffer objects.

  1. A programmer customizes an aspect to react to a changed user requirement. Suppose a new design decision that requires our UpdateCounter aspect to inform a listener when an update operation was performed. Figure 10 shows a refinement of UpdateCounter (Fig. 9) that implements this design decision via advice refinement.

Figure 10: Notify a listener when Buffer objects are updated.

  1. A programmer decomposes an aspect to decouple it from a specific configuration of the base program. For example, Figure 11 shows an aspect that introduces a new interface Serializable to a set of target classes (Buffer, Stack). Figure 12 shows the result of decomposing this aspect into a base and two refinements, where each refinement introduces the interface to one target class. Before composing and compiling the final program, a programmer or a tool selects only those refinements that target classes that are actually present in the program configuration, e.g., when Stack is present then also the according refinement is present (Lines 5–7).

The use cases discussed have one thing in common: aspect code can be reused in different variants of a program; aspects can be customized to the specific needs of a programmer or to fit the structure of the base program.

Figure 11: Introducing the interface Serializable to Buffer and Stack.

Figure 12: Decomposed Serialization aspect.

It is worth noting, that without the AHEAD model for SWR, it would be difficult to realize AR. The layered AHEAD structure assigns to each aspect an enclosing feature (layer), which is associated with a development step. This information helps to organize and compose refinements and their base aspects (see [15]).

With regard to AHEAD, mixin-based aspect inheritance is a composition operator that is invoked when aspects (and their refinements) of different development steps are composed. Hence, this aspect composition operator corresponds to the class composition operator, which composes classes using mixin-based inheritance.

Tool Support

ARJ. ARJ is a language extension of AspectJ that supports AR. It has been implemented as a modular extension to the abc compiler framework [7]. It extends the abc parser enabling it to recognize our new syntactical elements and it adds several frontend and backend passes for implementing a syntax tree transformation. ARJ is implemented to work in concert with the AHEAD Tool Suite and Jak to integrate AR into program features: ARJ expects a feature expression in the form of an AHEAD equation file. Feature containment hierarchies contain the associated aspects, classes, and refinement files (class and aspect refinements). Further details about ARJ are explained elsewhere [15]. The current version of ARJ supports all language constructs proposed here. The compiler as well as several documents and examples can be downloaded from the ARJ Web site3.

FeatureC++. FeatureC++4 is a Jak-like language extension of C++ with aspect support [3]. The FeatureC++ compiler supports a limited form of AR. Aspects can be refined (using ‘refines’) by adding members, extending methods, and refining pointcuts. There is no support for named advice or advice refinement.

Table 1: Refined aspects in P2P-PL.

4 CASE STUDY

We applied AR to a non-trivial case study, a product line for peer-to-peer overlay networks (P2P-PL). P2P-PL was developed as part of a comprehensive study that explored the relationship of program features and aspects [1]. Here, we discuss the results relevant for AR.

P2P-PL was implemented to experiment with advanced overlay network features such as query evaluation optimization and meta-data propagation. Hence, there was a desire for a highly customizable design that allows features to be reused in different configurations. One goal was to improve the structuredness of the P2P-PL design as well as the reusability and customizability of the contained aspects.

The code base of P2P-PL is about 6,426LOC. In summary, it contains 14 aspects (406LOC; 6 %). We applied AR to 7 aspects, i.e., we decomposed each of the 7 aspects into several pieces (one base aspect and several refinements). Table 1 gives an overview of the refactored aspects and the number of refinements.

Example. Connection pooling is a mechanism to save time and resources for frequently establishing and shutting down connections. Figure 13 lists the aspect Pooling; it uses a Pool for storing references to connections (Line 2). The pointcuts close (Lines 3–4) and open (Lines 5–6) match the join points that are associated with shutting down and opening connections. Named advice putPool (Lines 7–9) intercepts the shutdown process of connections and instead stores the associated ClientConnection objects in a Pool object. Named advice getPool (Lines 10–13) recovers open connections (if available) and passes them to clients that request a new connection. This aspect makes use of the built-in pointcut this to limit the advised calls to those that originate from MessageSender objects.

We refined the aspect Pooling twice, as shown in Figure 14. The first refinement (Lines 1–4) refines the pointcut open to limit the matched joint points to those that occur in the control flow of Peer. This excludes join points associated with helper and experimentation classes that use ClientConnection objects as well. Pointcut refinement decouples the aspect refinement from a fixed base aspect and thus increases the flexibility to combine this refinement with other refinements.

The second refinement is more sophisticated (Lines 5–12). It refines both pieces of advice (putPool, getPool) to guarantee thread safety. Since the pooling activities are implemented via named advice, a refinement can add synchronization code.

Figure 13: Connection pooling aspect (excerpt).

Figure 14: Encapsulating design decisions using AR.

Other refinements. In summary, we applied the notion of AR to 7 of 14 aspects of P2P-PL (cf. Tab. 1). On average, there were 7 refinements per base aspect and 1/2 of all aspects were decomposed via AR. While the predominant role of AR was to add new structural elements, i.e., advice, pointcuts, methods, and fields, we refined 3 named advice and 1 pointcut.

The application of AR increased the total number of features in P2P-PL considerably. However, the fine-grained decomposition of aspects (which results in 48 refinements applied to 7 aspects) did not only structure the design and implementation of P2P-PL, but it also increased the configuration space, i.e., the tailored variants of P2P systems that can be derived by the configuration process. For example, the aspect Serialization shown in Figure 12 has as many variants as different sets of target classes are possible in P2P-PL (theoretically 210). The aspect Pooling comes in fewer variants (4) because it has only 2 optional refinements, which can be combined freely (22).

Beside an improvement in customizability we were able to reuse aspect code amongst different variants of P2P-PL. In our study, all derivable variants of aspects share common functionality, thus reusing aspect code. For example, each of the 4 Pooling variants reuse code of the base aspect and of possibly another refinement. On average, each variant of each of the 7 decomposed aspects reuses code of 1.5 aspects and refinements. This is because, for most aspects, all variants can be freely combined, i.e., refinements are optional and can be applied standalone, in combination with some other refinements, or in combination with all other refinements.

Finally, we did not find many use cases for advice refinement (3×) and pointcut refinement (1×). We believe that this small number originates from the refactoring approach we chose, i.e., we decomposed each considered aspect retroactively into a base aspect and several refinements. Developing software from scratch with aspects and refinements in mind enables one to plan aspect reuse more systematically.

Discussion and Programming Guidelines

In our study we identified two main use cases of AR (cf. Sec. 2). First, we decoupled 4 aspects from a particular set of classes to be extended/advised, which maps to use case 3 (decoupling an aspect from a specific program configuration). For that, we decomposed aspects into several pieces to enable the programmer to combine these pieces in different combinations. For example, we decomposed the aspect Serialization to be able to choose only those pieces for adding serialization functionality that are actually needed in a particular P2P-PL configuration. This allows us to use the aspect in varying contexts, i.e., in different configurations of P2P-PL that contains different sets of classes to be serialized.

Second, we encapsulated the effects of different design decisions of 3 aspects into refinements, which maps to use case 2 (tailoring an aspect to a changed requirement). Decomposing aspects along design decisions facilitates customization, i.e., selecting different subsets of the overall set of refinements that are desired for a particular situation. Thereby, aspects can be tailored to different base programs and to differing requirements. For example, we decomposed the aspect Pooling into 3 pieces that encapsulate design decisions such as synchronization. By doing that, we were able to add and remove functionality and to select alternative implementations. This facilitates reuse of invariant aspect code and enables customization and tailoring of aspects to different P2P-PL configurations and to different requirements, e.g., performance.

Finally, we did not find applications for use case 1 (adapting aspects to changes in a base program). This is not surprising since this situation occurs typically when a program evolves, which was not covered by our case study.

5 RELATED WORK

Aspect refinement and AHEAD. The idea of AR emerged from our prior work on aspect-oriented and feature-oriented product lines and AHEAD [5]. Aspectual mixin layers (AMLs) integrate aspects and features in the sense of the AHEAD model [5, 1]. Aspect refinement based on mixin-based aspect inheritance enhances AMLs toward a unified integration of features and aspects with regard to SWR. While in prior work we outlined just the possibility of refining aspects and examined the consequences for the architectural model of AHEAD [4, 5], in this paper we focused on the language level support for AR, its implementation in the form of a fully functional compiler, and a non-trivial case study. We have shown how the idea of AR unifies the refinement of classes and aspects at the language level.

Implementation of aspect refinement. Beside mixin-based aspect inheritance, a further possibility arises to refine aspects: an aspect could be refined itself via advice and inter-type declarations of another aspect. In this case aspects themselves are part of a base program and the programmer has the choice to refine them via Jak-like refinements or via aspect weaving. However, it has been observed that advising advice can lead to logical errors and infinite loops [9].

AR is closely related to superimposition [10]. However, AR superimposes aspects, not just superimposes object-oriented structures using aspects [30].

Higher-order functions, pointcuts, and advice. Aspects are refinements and can be modeled as functions [21, 20, 6]. It is interesting, that in this view, AR is related to higher-order functions. A higher-order function expects a function as input and returns another function as output. Since aspects can be modeled as functions a refinement of an aspect can be understood as a function that applies to a function, which is a higher-order function, e.g., R[A](P), where P is a program, A is an aspect, and R is a refinement of A. It remains open how high-order functions fit with current algebraic models [21, 20, 6].

Tucker and Krishnamurthi integrate advice and pointcuts into languages with higher-order functions and model them as first-class entities [32]. This way, it becomes possible to implement higher-order pointcuts and advice. Pointcuts can be passed to other pointcuts as arguments. Thus, they can be modified, combined, and extended. In this respect, our approach of aspect and pointcut refinement is similar. We can combine, modify, and extend pointcuts by applying subsequent refinements.

Due to the opportunity of refining named advice, we can also modify and extend advice using subsequent advice. This corresponds to higher-order advice that expects a piece of advice as input and returns a modified piece of advice. Named advice can be passed to other advice – usually to advice that refines other advice. Thus, refining advice is similar to passing a piece of advice to higher-order advice.

Unification of advice and method. Using the annotation-based programming style of AspectJ, aspects are implemented as classes and advice is implemented as method and declared as such via annotation. In this programming style advice is already named and can be refined by method overriding. However, it is not obvious how this relates to other mechanisms for refinement, e.g., pointcut refinement.

Rajan and Sullivan propose classpects that combine capabilities of aspects and classes [28]. A classpect associates a method that is executed for advising a particular join point to each piece of advice. Moreover, classpects unify aspects and classes with respect to instantiation. Since advice is implemented via methods, it could be refined. However, the authors of classpects do not make a statement about the relationship to SWR nor about its consequences.

Aspects and genericity. Several recent approaches enhance aspects with genericity, e.g., Sally [14], Generic Advice [19], LogicAJ [17], Framed Aspects [22]. This improves reusability of aspects in different application contexts. Aspect refinement and mixin-based aspect inheritance provide an alternative way to customize aspects, i.e., by composing a base aspect and a series of refinements. However, ideas on generic aspects can be combined with our compositional approach, just as generic feature modules combine features with generics [2].

AspectJ design patterns. Hanenberg et al. discuss the benefits of inheritance in the context of AOP [14]. They argue that aspect inheritance improves aspect reuse and propose design patterns that exploit structural elements specific to AspectJ. Their patterns pointcut method, composite pointcut, and chained advice suggest to refine pointcuts in subsequent development steps to improve customizability, reusability and extensibility. AR can enhance these patterns by simplifying the composition of aspects. The pattern template advice can be improved by using named advice because it becomes possible to refine advice directly.

Feature-optionality problem. In FOP, features may interact with other features that are optional [27, 18]. In order to be reliable with regard to putting in and removing optional features, Prehofer proposes to split features into slices, i.e., into a base feature and several so called lifters [27]. Lifters encapsulate those pieces of code that depend on (and interact with) other features. When composing a program from features, a programmer or a tool selects for each feature the base feature and those lifters that refer to features that actually participate in the current configuration. Liu et al. lay an algebraic foundation for this methodology [18].

Our method of splitting aspects into pieces to resolve dependencies between aspects and the classes of a base program is similar to their approach: our refinements correspond to lifters, but in the context of AOP.

6 CONCLUSION

AR is the incarnation of SWR in AOP. It follows directly from the integration of aspects and stepwise development methodology of AHEAD. AR unifies classes and aspects with respect to subsequent refinement. We have illustrated three use cases where AR improves reuse and customization of aspect code. To introduce the principles of SWR at the programming language level, we proposed mixin-based aspect inheritance and a set of accompanying language constructs that facilitate SWR: pointcut refinement, named advice, and advice refinement.

To underpin our proposal, we developed a fully functional compiler on top of AspectJ. The compiler implements all proposed language constructs. We used the compiler to apply our approach to a non-trivial case study. The study demonstrated that technically our approach is realizable and applicable to a medium-sized software project. Our study indicated that the proposed language mechanisms facilitate aspect reuse and customization in a SWR manner. Furthermore, the study revealed guidelines when aspect refinement is useful: (1) for decoupling aspects from fixed configurations of the base program, and (2) for structuring aspect-oriented designs along design decisions. By composing refinements that encapsulate design decisions or interactions with the base program, one customizes aspects to a specific context. Our language mechanisms facilitate this composition.

Finally, AR as embodied in our ARJ compiler can be understood as an AHEAD composition operator for aspects, thus queuing in a long line of research on program synthesis and SWR [8].

Acknowledgements

We thank Don Batory, William Cook, Christian Lengauer, and Roberto Lopez-Herrejon for their insightful comments on earlier drafts of this paper. This work was sponsored in parts by the German Research Foundation (DFG), project number SA 465/32-1.

FOOTNOTES

1 http://eclipse.org/aspectj/

2 E.g., refining a pointcut in AspectC++ requires to declare the parent pointcut as virtual.

3 http://wwwiti.cs.uni-magdeburg.de/iti db/arj/

4 http://wwwiti.cs.uni-magdeburg.de/iti db/fcc/


REFERENCES

[1] S. Apel and D. Batory. When to Use Features and Aspects? A Case Study. In Proceedings of the International Conference on Generative Programming and Component Engineering, pages 59–68. ACM Press, 2006.

[2] S. Apel, M. Kuhlemann, and T. Leich. Generic Feature Modules: Two-Staged Program Customization. In Proceedings of the International Conference on Software and Data Technologies, pages 127–132. INSTICC Press, 2006.

[3] S. Apel, T. Leich, M. Rosenmüller, and G. Saake. FeatureC++: On the Symbiosis of Feature-Oriented and Aspect-Oriented Programming. In Proceedings of the International Conference on Generative Programming and Component Engineering, volume 3676 of LNCS, pages 125–140. Springer, 2005.

[4] S. Apel, T. Leich, and G. Saake. Aspect Refinement and Bounded Quantification in Incremental Designs. In Proceedings of the Asia-Pacific Software Engineering Conference, pages 796–804. IEEE Computer Society, 2005.

[5] S. Apel, T. Leich, and G. Saake. Aspectual Mixin Layers: Aspects and Features in Concert. In Proceedings of the International Conference on Software Engineering, pages 122–131. ACM Press, 2006.

[6] S. Apel and J. Liu. On the Notion of Functional Aspects in Aspect-Oriented Refactoring. In Proceedings of the ECOOP Workshop on Aspects, Dependencies, and Interactions, pages 1–9. Computing Department, Lancaster University, 2006.

[7] P. Avgustinov, A. S. Christensen, L. Hendren, S. Kuzins, J. Lhotak, O. Lhotak, O. de Moor, D. Sereni, G. Sittampalam, and J. Tibble. abc: An Extensible AspectJ Compiler. In Proceedings of the International Conference on Aspect-Oriented Software Development, pages 87–98. ACM Press, 2005.

[8] D. Batory, J. N. Sarvela, and A. Rauschmayer. Scaling Step-Wise Refinement. IEEE Transactions on Software Engineering, 30(6):355–371, 2004.

[9] E. Bodden, F. Forster, and F. Steimann. Avoiding Infinite Recursion with Stratified Aspects. In Proceedings of the International Net.ObjectDays Conference, pages 49–64. Gesellschaft f¨ur Informatik, 2006.

[10] L. Bouge and N. Francez. A Compositional Approach to Superimposition. In Proceedings of the International Symposium on Principles of Programming Languages, pages 240–249. ACM Press, 1988.

[11] G. Bracha and W. R. Cook. Mixin-Based Inheritance. In Proceedings of the International Conference on Object-Oriented Programming, Systems, Languages, and Applications and the European Conference on Object-Oriented Programming, pages 303–311. ACM Press, 1990.

[12] S. Clarke and E. Baniassad. Aspect-Oriented Analysis and Design: The Theme Approach. Addison-Wesley, 2005.

[13] E. W. Dijkstra. A Discipline of Programming. Prentice Hall, 1976.

[14] S. Hanenberg, A. Schmidmeier, and R. Unland. AspectJ Idioms for Aspect-Oriented Software Construction. In European Conference on Pattern Languages of Programs, pages 617–644. Universitätsverlag Konstanz, 2003.

[15] C. Kästner, S. Apel, and G. Saake. Implementing Bounded Aspect Quantification in AspectJ. In Proceedings of the ECOOP Workshop on Reflection, AOP and Meta-Data for Software Evolution, pages 111–122. School of Computer Science, University of Magdeburg, 2006.

[16] G. Kiczales, J. Lamping, A. Mendhekar, C. Maeda, C. V. Lopes, J.-M. Loingtier, and J. Irwin. Aspect-Oriented Programming. In Proceedings of the European Conference on Object-Oriented Programming, volume 1241 of LNCS, pages 220–242. Springer, 1997.

[17] G. Kniesel and T. Rho. A Definition, Overview and Taxonomy of Generic Aspect Languages. L’Objet, 11(3):9–39, 2006.

[18] J. Liu, D. Batory, and C. Lengauer. Feature-Oriented Refactoring of Legacy Applications. In Proceedings of the International Conference on Software Engineering, pages 112–121. ACM Press, 2006.

[19] D. Lohmann, G. Blaschke, and O. Spinczyk. Generic Advice: On the Combination of AOP with Generative Programming in AspectC++. In Proceedings of the International Conference on Generative Programming and Component Engineering, volume 3286 of LNCS, pages 55–74. Springer, 2004.

[20] R. Lopez-Herrejon, D. Batory, and W. R. Cook. Evaluating Support for Features in Advanced Modularization Technologies. In Proceedings of the European Conference on Object-Oriented Programming, volume 3586 of LNCS, pages 169–194. Springer, 2005.

[21] R. Lopez-Herrejon, D. Batory, and C. Lengauer. A Disciplined Approach to Aspect Composition. In Proceedings of the International Symposium on Partial Evaluation and Semantics-Based Program Manipulation, pages 68–77. ACM Press, 2006.

[22] N. Loughran and A. Rashid. Framed Aspects: Supporting Variability and Configurability for AOP. In Proceedings of the International Conference on Software Reuse, volume 3107 of LNCS, pages 127–140. Springer, 2004.

[23] H. Masuhara and G. Kiczales. Modeling Crosscutting in Aspect-Oriented Mechanisms. In Proceedings of the European Conference on Object-Oriented Programming, volume 2743 of LNCS, pages 2–28. Springer, 2003.

[24] T. Mens, K. Mens, and T. Tourwé. Aspect-Oriented Software Evolution. ERCIM News, 1(58):36–37, 2004.

[25] C. Morgan. Programming from Specifications. Prentice Hall, 2nd edition, 1994.

[26] D. L. Parnas. Designing Software for Ease of Extension and Contraction. IEEE Transactions on Software Engineering, SE-5(2):264–277, 1979.

[27] C. Prehofer. Feature-Oriented Programming: A Fresh Look at Objects. In Proceedings of the European Conference on Object-Oriented Programming, volume 1241 of LNCS, pages 419–443. Springer, 1997.

[28] H. Rajan and K. J. Sullivan. Classpects: Unifying Aspect- and Object-Oriented Language Design. In Proceedings of the International Conference on Software Engineering, pages 59–68. ACM Press, 2005.

[29] V. Rajlich. Changing the Paradigm of Software Engineering. Communications of the ACM, 49(8):67–70, 2006.

[30] M. Sihman and S. Katz. Superimpositions and Aspect-Oriented Programming. The Computer Journal, 46(5):529–541, 2003.

[31] Y. Smaragdakis and D. Batory. Mixin Layers: An Object-Oriented Implementation Technique for Refinements and Collaboration-Based Designs. ACM Transactions on Software Engineering and Methodology, 11(2):215–255, 2002.

[32] D. Tucker and S. Krishnamurthi. Pointcuts and Advice in Higher-Order Languages. In Proceedings of the International Conference on Aspect-Oriented Software Development, pages 158–167. ACM Press, 2003.

[33] N. Wirth. Program Development by Stepwise Refinement. Communications of the ACM, 14(4):221–227, 1971.

About the authors



 

Sven Apel is a post-doctoral associate at the Chair of Programming at the University of Passau, Germany. He received a Ph. D. in Computer Science from the University of Magdeburg in 2007. His research interests include advanced programming paradigms, software product lines, and algebra for software construction. He can be reached at apel@uni-passau.de. See also http://www.infosun.fim.uni-passau.de/cl/staff/apel/.



  Christian Kästner is a Ph. D. student in Computer Science at the University of Magdeburg, Germany. His research interests are languages and architectures for software product lines. He can be reached at ckaestne@ovgu.de. See also http://wwwiti.cs.uni-magdeburg.de/~ckaestne/.


  Thomas Leich is the head of the Department of Applied Informatics at the Metop Research Institute in Magdeburg, Germany and a Ph.D. student in Computer Science at the University of Magdeburg, Germany. His research interests are tailor-made and embedded data management and software product lines. He can be reached at thomas.leich@metop.de. See also http://www.metop.de.


 

Gunter Saake is a full professor of Computer Science. He is the head of the Database and Information Systems Group at the University of Magdeburg, Germany. His research interests include database integration, tailor-made data management, object-oriented information systems, and information fusion. He can be reached at saake@iti.cs.uni-magdeburg.de. See also http://wwwiti.cs.uni-magdeburg.de/~saake/.


Cite this article as follows: Sven Apel, Christian Kästner, Thomas Leich, Gunter Saake: “Aspect Refinement – Unifying AOP and Stepwise Refinement”, in Journal of Object Technology, vol. 6, no. 9, Special Issue. TOOLS EUROPE 2007, October 2007, pp. 13–33, http://www.jot.fm/issues/issue_2007_10/paper1/


Previous paper

Next paper