Towards Reusable Components With Aspects [ICSE 2008]
-
Upload
kevin-hoffman -
Category
Technology
-
view
2.185 -
download
4
description
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
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