Towards Reusable Components With Aspects [ICSE 2008]

Post on 05-Dec-2014

2.185 views 4 download

description

Does obliviousness increase overall modularity? This presents the results of an empirical study that focuses on the tradeoffs between obliviousness and modularity. The results may surprise you!

Transcript of Towards Reusable Components With Aspects [ICSE 2008]

Towards Reusable

Components with Aspects:

Kevin Hoffman / Patrick Eugster

An Empirical Study on Modularity and Obliviousness

Seen code that looks like this?

(Carlo H. Séquin @ Berkeley)

(George W. Hart @ Stony Brook) (George W. Hart @ Stony Brook)

(speedcubing.com)

Roadmap

AOP with AspectJ

Challenges

Cooperative AOP with Explicit Join Points

Empirical Study

How obliviousness can be traded for increased

modularity and reuse through Cooperative AOP

Potential pitfalls and guidelines

Cross-cutting Concerns

(Thanks to AspectJ Dev Tools (AJDT) Visualization Eclipse Perspective)

Cross-cutting Concerns

(Thanks to AspectJ Dev Tools (AJDT) Visualization Eclipse Perspective)

Real Crosscutting Concerns

Logging (canonical example)

Pooling / caching

Dependency injection

Policy / contract enforcement

Security

Transactions

Exception Handling

AOP & Crosscutting Concerns

Separation

Modularization

AOP Stratagems [Filman/Friedman’00]

Obliviousness Increased modularity of base code and aspect code

Parallel and domain-specific development

Better post-mortem extendibility

Quantification (pattern matching)

Reduction in code size and duplicity

Higher level interaction between primary and cross-

cutting concerns

AspectJ

AspectJ [Kiczales ’01]

Join point model

Structural points in ‘base code’

Quantification model (pointcuts)

Lexical patterns and type constraints

Dynamic predicates and control flow

Aspects inject advice before, after, around…

Looks Nice, However…

Aspect Base Code Coupling

Fragile Pointcuts

Base Code Aspect Code

public aspectWafViewTemplateHandlerextendsExceptionGenericAspect

{ pointcut initScrGetResHdlr():

withincode(private voidTemplateServlet. initScreens(

ServletContext, String))

&& call(URL ServletContext.getResource(String));

…}

private void initScreens(

ServletContext ctxt,

String language)

{

screenDefURL =

ctxt.getResource(…);

}

State—Point Separation Issue

Base Code aspect AbortedTranAnalyzer {

}

try {…

} catch (OldExc e) {t.abortTrans(…);throw new

GenExc ("failed");}

//========POSSIBLE========pointcut gotOldExc(OldExc ex):

handler(OldExc) && args(ex);pointcut abortedTran(Tran tr):

call(* *.abortTrans(..))&& target(tr);

pointcut gotGenExc(GenExc ex):initialization(GenExc.new(..))&& target(ex);

//======NOT POSSIBLE=======pointcut get3State(OldExc old,

Tran tr, GenExc gen):gotOldExc(old)&& abortedTran(tr)&& gotGenExc(ex);

State—Point Separation Issue

Base Code aspect AbortedTranAnalyzer {

}

try {…

} catch (OldExc e) {t.abortTrans(…);throw new

GenExc ("failed");}

//========POSSIBLE========pointcut gotOldExc(OldExc ex):

handler(OldExc) && args(ex);pointcut abortedTran(Tran tr):

call(* *.abortTrans(..))&& target(tr);

pointcut gotGenExc(GenExc ex):initialization(GenExc.new(..))&& target(ex);

//======NOT POSSIBLE=======pointcut get3State(OldExc old,

Tran tr, GenExc gen):gotOldExc(old)&& abortedTran(tr)&& gotGenExc(ex);

State—Point Separation Issue

Base Code aspect AbortedTranAnalyzer {

}

try {…

} catch (OldExc e) {t.abortTrans(…);throw new

GenExc ("failed");}

//========POSSIBLE========pointcut gotOldExc(OldExc ex):

handler(OldExc) && args(ex);pointcut abortedTran(Tran tr):

call(* *.abortTrans(..))&& target(tr);

pointcut gotGenExc(GenExc ex):initialization(GenExc.new(..))&& target(ex);

//======NOT POSSIBLE=======pointcut get3State(OldExc old,

Tran tr, GenExc gen):gotOldExc(old)&& abortedTran(tr)&& gotGenExc(ex);

State—Point Separation Issue

Base Code aspect AbortedTranAnalyzer {

}

try {…

} catch (OldExc e) {t.abortTrans(…);throw new

GenExc ("failed");}

//========POSSIBLE========pointcut gotOldExc(OldExc ex):

handler(OldExc) && args(ex);pointcut abortedTran(Tran tr):

call(* *.abortTrans(..))&& target(tr);

pointcut gotGenExc(GenExc ex):initialization(GenExc.new(..))&& target(ex);

//======NOT POSSIBLE=======pointcut get3State(OldExc old,

Tran tr, GenExc gen):gotOldExc(old)&& abortedTran(tr)&& gotGenExc(ex);

State—Point Separation Issue

Base Code aspect AbortedTranAnalyzer {

}

try {…

} catch (OldExc e) {t.abortTrans(…);throw new

GenExc ("failed");}

//========POSSIBLE========pointcut gotOldExc(OldExc ex):

handler(OldExc) && args(ex);pointcut abortedTran(Tran tr):

call(* *.abortTrans(..))&& target(tr);

pointcut gotGenExc(GenExc ex):initialization(GenExc.new(..))&& target(ex);

//======NOT POSSIBLE=======pointcut get3State(OldExc old,

Tran tr, GenExc gen):gotOldExc(old)&& abortedTran(tr)&& gotGenExc(ex);

AspectJ-specific Problems

Abstract aspects can’t share pointcuts

Anonymous advice --> hard to advise aspects

Blocks of code inside methods not advisable

Pointcuts can’t parameterize advice

Lack of Advice Parameterization

Base Code aspect ExceptionPrinter {

}

try {…

} catch (Exc1 e) {println("timeout");

}

try {…

} catch (Exc2 e) {println("net failed");

}

pointcut p1(Exc1 ex): handler(Exc1) && args(ex);

before(Exception ex):p1(ex) || p2(ex)

{ handleExc("timeout", thisJP); }

pointcut p2(Exc2 ex): handler(Exc2) && args(ex);

before(Exception ex):p1(ex) || p2(ex)

{ handleExc(“net failed", thisJP); }

void handleExc(String m,JoinPoint jp) {

println(jp + ":" + m); …}

Some Proposed Techniques

Aspect-aware Interfaces [Kiczales, Mezini ‘05]

Open Modules [Aldrich ‘05]

HyperJ [Ossher, Tarr ‘00]

Classpects [Rajan, Sullivan ‘05]

CaesarJ [Aracic, Gasiunas, Mezini, Ostermann ‘06]

XPIs [Sullivan et. al. ‘05]

Explicit Join Points [Hoffman, Eugster ‘07]

Explicit Join Points (EJPs)

Abstract a cross-cutting concern to its most essential form

Invoke the information hiding principle

Model the abstraction explicitly using named join points instead of implicit join points

Reference EJPs in code explicitly

Or use aspects to inject EJP references obliviously

Aspect Base Code Coupling

Cooperative AOP Methodology

AspectJ: 39 AspectBase couplings

EJPs: 11 AspectInterface, 15 BaseInterface

Comparison of Approaches

AspectJ:

AspectJ with EJPs (Cooperative AOP):

AspectsBase Code EJP Interfaces

AspectsBase Code

Library Interfaces Pluggable Libraries

Scoped EJPs, pointcutargs and thisblock, advice

parameterization by type/value, and policy enforcement

Building Aspect Libraries with EJPs

Define semantic interfaces of cross-cutting concerns using interfaces and EJPs

Package these in a JAR

Define aspects that advise the EJPs to implement the cross cutting concerns

Package each implementation in a different JAR

Write base code or aspects to reference EJPs

Choose aspect implementation JAR at load-time

As the EJPs evolve, use aspects to obliviouslyadapt calls from old EJPs to new EJPs

EJPs Address…

Fragile pointcuts

State—point separation problem

Abstract aspects can’t share pointcuts

Anonymous advice

Blocks of code inside methods not advisable

Pointcuts can’t parameterize advice

Sullivan—Levels of Obliviousness

Language-level (i.e. language support for AOP)

Feature obliviousness—base code unaware of

features in aspects, but do know of preconditions

Designer obliviousness—

Base code programmers blissfully unaware…

Pure obliviousness—

Complete, symmetric separation

This Empirical Study

What are the tradeoffs

between modularity

and obliviousness?

This Empirical Study

Refactored Exception Handling for 3 Java Apps:

Using AspectJ

Using EJPs / Cooperative AOP

Empirical Metrics Measuring:

Coupling & Cohesion

Size & Complexity

Separation of Concerns

Reusability

Revisiting ‘Exceptions and Aspects’ @ FSE06 [Filho et. al.] – Thanks!

Target Applications

Telestrada (Java)

220+ classes and interfaces, 3400 LOC

Java Pet Store (Java)

340+ classes and interfaces,17800 LOC

Health Watcher (AspectJ)

36 aspects, 96 classes and interfaces, 6600 LOC

Aspects for CC, distribution, persistence, some EH

Refactoring Strategy (AspectJ)

BEFORE AFTER

class C {void m() throws … {

try { /*body*/ }catch(E e) { … }

}}

aspect A {pointcut p():

execution(void C.m());void around(): p() {

try {proceed();

} catch (E e) { … }}declare soft: E: p();

}class C {

void m(){ /*body*/ }}

Refactoring Strategy (EJPs)

BEFORE AFTER

class C {void m() throws … {

try { /*body*/ }catch(E e) { … }

}}

aspect A {scoped joinpoint ejpH()

handles E throws …;void around() throws …:

call(ejpScope(ejpH)){

try{ proceed(); }catch(E e) { … }

}}class C {

void m() throws … {A.ejpH(){ /*body*/ }

}}

Empirical Metrics

Coupling Between Modules (CBM)

Coupling on Intercepted Modules (CIM)

Lack of Cohesion of Operations (LCO)

Lines of Code (LOC) / Concern LOC (CLOC)

Number of Operations (NoO)

Concern Diffusion over Modules (CDoM)

Concern Diffusion over Operations (CDoO)

Concern Diffusion over Lines of Code (CDoLOC)

Reusable Operation Use Percentage (ROUP)

Coupling Metrics

Coupling Between Modules (CBM)

Number of other modules referenced via field access

or method call or EJP reference

Extended to include reference to EJP interfaces

[Chidamber, Kemerer 1994]

class C {void m1(B var1, B var2) {

A.ejpH(){ var1.m3(); }C.m2();var2.m4();

}static void m2() { … }

}

CBM = 2

(A and B)

Coupling Between Modules

Coupling Metrics

Coupling on Intercepted Modules (CIM)

# of modules explicitly named in pointcuts

[Ceccato, Tonella 2004]

pointcut initScrGetResHdlr(): withincode(

private voidTemplateServlet. initScreens(

ServletContext, String))

&& call(URL ServletContext.getResource(String));

CIM = 3

Coupling on Intercepted Modules

Cohesion Metric

Lack of Cohesion in Operations (LCO)

# of method pairs accessing different fields minus

# of method pairs accessing common fields

Helps to measure commonality of purpose

class C {Object f1, f2, f3;void m1() { f1=…; }void m2() { f2=…; }void m3() { f3=…; }void m4() { f1=…; f2=…;}

}Disjoint Pairs: m1-m2, m1-m3, m2-m3, m3-m4

Non-Disjoint Pairs: m1-m4, m2-m4

LCO = 2

Lack of Cohesion of Operations

Size / Complexity Metrics

Lines of Code (LOC)

# of lines of code without whitespace / comments

Concern Lines of Code (CLOC)

# of lines of code required to implement the exception

handling concern

Number of Operations (NoO)

Number of declared methods and advice

Lines of Code

Concern Lines of Code

Number of Operations

Separation of Concern Metrics

Concern Diffusion over Modules (CDoM)

# of modules that implement a concern

… OR reference one that does

Concern Diffusion over Operations (CDoO)

# of operations that implement a concern

… OR reference one that does

Concern Diffusion over Modules

Concern Diffusion over Operations

Separation of Concern Metrics

Concern Diffusion over Lines of Code

# of transitions between one concern to another

class C {void m() throws … {

try {/*

body*/

} catch(E e) { … }}

}

class C {void m() throws … {

A.ejpHandler() {/*

body*/

}}

}

Concern Diffusion over LOC

Exception Handler Reuse

% o

f h

an

dle

rs im

ple

me

nte

d b

y

ab

str

ac

t a

sp

ec

ts o

r E

JP

lib

rary

Reusable Exception Handler EJPs

Ignore exception

Print/log exception

Rethrow different exception or its cause

On exception set variable to value

Propagate exception if flagged

16 EJPs covered 74% of all handlers

Empirically, This Happens

Cooperative AOP Can Help

AspectJ: 39 AspectBase couplings

EJPs: 11 AspectInterface, 15 BaseInterface

Conclusions

Explicit Interfaces and EJP references:

Provided effective means for advice

parameterization

Greatly increased aspect reusability

Must be carefully designed, ideally in advance

Greatest software quality achieved when using

combination of obliviousness + EJPs

kjhoffma@cs.purdue.edu

peugster@cs.purdue.edu

Download papers, slides, and compiler at http://www.kevinjhoffman.com/

Future Work

JSR-308 includes

proposal for annotations

on statements

Study interactions

between obliviousness

and software quality in

the presence of multiple

cross-cutting concerns

(SUPPORTING SLIDES)

Addressing EJP Explicitness

Use EJPs only when appropriate

Design EJPs so that their presence is minimal

Use aspects to reference EJPs as appropriate

Aspect-oriented code editors

Fluid AOP [Hon / Kiczales]

AspectsBase Code EJP Interfaces

Empirical Metrics Formulated…

Coupling, Cohesion, Separation of Concerns

On the reuse and maintenance of Aspect-Oriented

software: [Sant’Anna et. al. 2000]

AspectBase Code Coupling

Measuring the effects of software aspectization

[Ceccato/Tonella 2004]

http://aopmetrics.tigris.org/

Empirical Studies Emerging…

Separation of Concerns in Multi-agent Systems: An

Empirical Study [2004]

Modularizing design patterns with aspects: a quantitative

study [AOSD’05]

Composing design patterns: a scalability study of aspect-

oriented programming [AOSD’06]

Exceptions and aspects: the devil is in the details [FSE’06]

On the impact of aspectual decompositions on design

stability: An empirical study [ECOOP’07]

Towards Reusable Components with Aspects [ICSE’08]

Evolving Software Product Lines with Aspects [ICSE’08]

Explicit Join Point Declarations

Optional

Modifiers

Keyword to

Declare EJP

Constraints Acting

Upon Base Code

Explicit Name to Associate

with Abstract Semantics

Explicit Value

Parameterization

abstract aspect TranConcern {scoped joinpoint void enterTrans(int isolation)

throws TranException;}

Referencing EJPs in Base Code

Some policy aspect could

implement/override this EJP

Reference to

EJP in base code

Reference to scoped EJP;

entire block of code is advised

abstract aspect TranConcern {scoped joinpoint void enterTrans(int isolation)

throws TranException;joinpoint int defIso() = 1;

}

void someMethod() throws TranException {TranConcern.enterTrans(TranConcern.defIso()) {

//block of code}

}

Advising EJPs in Aspects

aspect TranConcernViaSomeLibrary {void around(int iso) throws TranException:

call(ejpScope(TranConcern.enterTrans))&& args(iso) {

TransContext t = …;t.beginTrans();try {

proceed(); /* calls original block of code */t.commitTrans();

} catch(Throwable e) {t.abortTrans();throw TranException(e);

}}

Advising EJPs in Aspects

aspect BillingComponentsTranPolicy {int around(): call(ejp(TranConcern.defIso))

&& within (com.me.billing.*){ return 4; } //use a higher isolation level in billing pkg

}

aspect ForceIsolationLevel {int around(): call(ejp(TranConcern.defIso))

&& cflow(call(* CreditCard+.*(..))){ return 5; } //anytime the call stack includes a method

} //from the CreditCard class use iso level 5