Bridging Java And AspectJ [PPPJ08]

47
Bridging Java and AspectJ through Explicit Join Points Kevin Hoffman and Patrick Eugster PPPJ 2007

description

Through AspectJ, aspect-oriented programming (AOP) is becoming of increasing interest and availability to Java programmers as it matures as a methodology for improved software modularity via the separation of cross-cutting concerns. AOP proponents often advocate a development strategy where Java programmers write the main application, ignoring cross-cutting concerns, and then AspectJ programmers, domain experts in their specific concerns, weave in the logic for these more specialized cross-cutting concerns. However, several authors have recently debated the merits of this strategy by empirically showing certain drawbacks. The proposed solutions paint a different development strategy where base code and aspect programmers are aware of each other (to varying degrees) and interactions between cross-cutting concerns are planned for early on. Herein we explore new possibilities in the language design space that open up when the base code is aware of crosscutting aspects. Using our insights from this exploration we concretize these new possibilities by extending AspectJ with concise yet powerful constructs, while maintaining full backwards compatibility. These new constructs allow base code and aspects to cooperate in ways that were previously not possible: arbitrary blocks of code can be advised, advice can be explicitly parameterized, base code can guide aspects in where to apply advice, and aspects can statically enforce new constraints upon the base code that they advise. These new techniques allow aspect modularity and program safety to increase. We illustrate the value of our extensions through an example based on transactions.

Transcript of Bridging Java And AspectJ [PPPJ08]

Page 1: Bridging Java And AspectJ [PPPJ08]

Bridging Java and AspectJ

through Explicit Join Points

Kevin Hoffman and Patrick Eugster

PPPJ 2007

Page 2: Bridging Java And AspectJ [PPPJ08]

AOP

Page 3: Bridging Java And AspectJ [PPPJ08]

AOP

Page 4: Bridging Java And AspectJ [PPPJ08]

Cross-cutting Concerns

(Thanks to AspectJDev Tools (AJDT) Visualization Eclipse Perspective)

Page 5: Bridging Java And AspectJ [PPPJ08]

Cross-cutting Concerns

(Thanks to AspectJDev Tools (AJDT) Visualization Eclipse Perspective)

Page 6: Bridging Java And AspectJ [PPPJ08]

Aspect-Oriented Programming

Make cross-cutting concerns:

Separated from the “base code”

No coupling between base code / cross-cutting concerns

Aim for lexical and semantic separation

Modular Domain experts write the “hard stuff” once

Page 7: Bridging Java And AspectJ [PPPJ08]

AOP Stratagems

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

Page 8: Bridging Java And AspectJ [PPPJ08]

Toy Tracing Example

class Store {

pointcut logEvents():call(* * Store.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

Page 9: Bridging Java And AspectJ [PPPJ08]

Toy Tracing Example

class Store {

pointcut logEvents():call(* * Store.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

println (…)

println (…)

println (…)

Page 10: Bridging Java And AspectJ [PPPJ08]

Toy Tracing Example

class Store {

pointcut logEvents():call(* * Store.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

println (…)}catch(…){

println(…);…}

try {

println (…)}catch(…){

println(…);…}

try {

println (…)}catch(…){

println(…);…}

try {

Page 11: Bridging Java And AspectJ [PPPJ08]

AspectJ Join Points

class Store {

pointcut logEvents():call(* * Store.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

Page 12: Bridging Java And AspectJ [PPPJ08]

AspectJ Pointcuts

class Store {

pointcut logEvents():call(* * Store+.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

Page 13: Bridging Java And AspectJ [PPPJ08]

AspectJ Advice

class Store {

pointcut logEvents():call(* * Store+.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

}}

Page 14: Bridging Java And AspectJ [PPPJ08]

AspectJ Advice

class Store {

pointcut logEvents():call(* * Store+.*(..));

//advicearound(): logEvents() {

println(“”+thisJoinPoint);Object ret = proceed();println(“ret: “+ret);return ret;

}after() throwing(Exception e):

logEvents() {println(“err: ”+e);throw e;

}

JSP Pages aspect Logging {

…s.buy(_order);…

…s.cancel(o);…

…s.retItem(o);…

}}

void buy(…){

try {…

} catch (…) { }}

void cancel(…){

…throw …;

}

void retItem(…){

…}

Page 15: Bridging Java And AspectJ [PPPJ08]

AspectJ in Action1

Logging (canonical example)

Pooling / caching

Policy / contract enforcement

Business logic

Security

Transactions

Exception Handling

1) AspectJ in Action: Practical aspect-oriented programming, R. Laddad, Manning, 2003

Page 16: Bridging Java And AspectJ [PPPJ08]

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(…);

}

Page 17: Bridging Java And AspectJ [PPPJ08]

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);

Page 18: Bridging Java And AspectJ [PPPJ08]

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);

Page 19: Bridging Java And AspectJ [PPPJ08]

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);

Page 20: Bridging Java And AspectJ [PPPJ08]

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);

Page 21: Bridging Java And AspectJ [PPPJ08]

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);

Page 22: Bridging Java And AspectJ [PPPJ08]

AspectJ in Action1

Logging (canonical example)

Pooling / caching

Policy / contract enforcement

Business logic

Security

Transactions

Exception Handling

1) AspectJ in Action: Practical aspect-oriented programming, R. Laddad, Manning, 2003

Challenges of AspectJ

Hindered by:

• Tight aspectbase code coupling

• Burden of fragile pointcuts

• State—point separation problem

• Lack of advice parameterization

• Inability to share abstract pointcuts

Page 23: Bridging Java And AspectJ [PPPJ08]

Motivation for our Work

How to stop writing

complex, brittle pointcuts,

just to avoid touching the base code,

and start writing

truly reusable aspect libraries

Page 24: Bridging Java And AspectJ [PPPJ08]

Is all this

obliviousness

really worth it?

Page 25: Bridging Java And AspectJ [PPPJ08]

AspectJ Approach

Page 26: Bridging Java And AspectJ [PPPJ08]

What If Join Points Were Explicit?

Page 27: Bridging Java And AspectJ [PPPJ08]

Cooperative AOP

Page 28: Bridging Java And AspectJ [PPPJ08]

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

Enhance EJPs to mitigate the aspect modularity challenges of AspectJ

Page 29: Bridging Java And AspectJ [PPPJ08]

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;}

Page 30: Bridging Java And AspectJ [PPPJ08]

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}

}

Page 31: Bridging Java And AspectJ [PPPJ08]

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);

}}

Page 32: Bridging Java And AspectJ [PPPJ08]

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

Page 33: Bridging Java And AspectJ [PPPJ08]

EJP Pointcut Arguments

Provide mechanisms for pointcuts to be

defined piecewise in base code:

Allow each piece of such a pointcut to be lexically

close to the fragile join points

Fragile code and pointcuts evolve together

Represent the disjunction of all of the pieces as a

pointcut associated with an EJP declaration

Page 34: Bridging Java And AspectJ [PPPJ08]

Declaring EJP Pointcut Arguments

interface CompRecord {void compensate();void log(…);

}abstract aspect TranConcern {

scoped joinpoint void enterTrans(int isolation)pointcutargs ignoreableCompensations()throws TranException;

joinpoint void addCompensation(CompRecord r);}

This pointcut models the join points that reference the

addCompensation EJP but should actually be ignored

Page 35: Bridging Java And AspectJ [PPPJ08]

Defining Pointcutargs Piecewise

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

pointcutargs ignorableCompensations():(call(ejp(*.addCompensation)) &&cflow(call(thisblock)))

{//any references to the addCompensation EJP//in the cflow of this block are included in the full//definition of the pointcut ignorableCompensations

}}

Page 36: Bridging Java And AspectJ [PPPJ08]

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

Page 37: Bridging Java And AspectJ [PPPJ08]

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

Page 38: Bridging Java And AspectJ [PPPJ08]

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

Page 39: Bridging Java And AspectJ [PPPJ08]

EJPs Empirical Case Study [ICSE08]

Page 40: Bridging Java And AspectJ [PPPJ08]

Related Work

XPIs [Sullivan et al]

Define design rules to shape base code so that

implicit join points have a predictable structure

Write pointcuts against stable design rule patterns

Open Modules [Aldrich]

Restrict advisable join points to be only those that

are explicitly exported by a module

Modular reasoning [Kiczales and Mezini]

Inference of cross-cutting interfaces

Page 41: Bridging Java And AspectJ [PPPJ08]

AspectJ + EJPs Deliver

The separation of cross-cutting concerns:

Syntactically

Semantically (Base code EJPs aspects)

Benefits from obliviousness

Increased modularity of base code ̂ and aspect code

Parallel and domain-specific development

Post-mortem extendibility

Benefits from quantification

Reduction in code size and duplicity

Higher level interaction between primary and cross-cutting concerns (matching against semantic interfaces)

(presence of EJPs)

Page 42: Bridging Java And AspectJ [PPPJ08]

AspectJ in Action1

Logging (canonical example)

Pooling / caching

Policy / contract enforcement

Business logic

Security

Transactions

Exception Handling

1) AspectJ in Action: Practical aspect-oriented programming, R. Laddad, Manning, 2003

Goal of Reusable Aspect Libraries

Hindered by:

• Tight aspectbase code coupling

• Burden of fragile pointcuts

• State—point separation problem

• Lack of advice parameterization

• Inability to share abstract pointcuts

Page 43: Bridging Java And AspectJ [PPPJ08]

AspectJ in Action1

Logging (canonical example)

Pooling / caching

Policy / contract enforcement

Business logic

Security

Transactions

Exception Handling

1) AspectJ in Action: Practical aspect-oriented programming, R. Laddad, Manning, 2003

Goal of Reusable Aspect Libraries

Facilitated by:

• AspectEJPbase code decoupling

• Stable pointcuts / pointcutargs

• Scoped EJPs

• Type/value advice parameterization

• Policy enforcement constructs

Page 45: Bridging Java And AspectJ [PPPJ08]

Example Fragile Pointcuts

public aspect WafViewTemplateHandlerextends

ExceptionGenericAspect{

/*** ScreenDefinitionDAO***/

pointcut loadDocument() :

execution(public static Element

ScreenDefinitionDAO.loadDocument(URL));

/*** TemplateServlet***/

pointcut initScreensGetResourceHandler() :

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

withincode(private void

TemplateServlet.initScreens(ServletContext, String));

pointcut internalGetUserTransactionHandler() :

execution(* TemplateServlet.internalGetUserTransaction());

pointcut internalGetRequestDispatcherHandler() :

execution(private void

TemplateServlet.internalGetRequestDispatcher(..));

/*** com.sun.j2ee.blueprints.waf.view.template.tags.InsertTag ***/

public pointcut aroundExceptionDoNothingHandler() :

execution(private void

com.sun.j2ee.blueprints.waf.view.template.tags.InsertTag.intern

alDoStartTag1());

pointcut internalDoStartTag2Handler() :

execution(private void

com.sun.j2ee.blueprints.waf.view.template.tags.InsertTag.intern

alDoStartTag2());

pointcut doEndTagHandler() :

execution(public int

com.sun.j2ee.blueprints.waf.view.template.tags.InsertTag.doEn

dTag());

Page 46: Bridging Java And AspectJ [PPPJ08]

Example Fragile Pointcuts

private void initScreens(ServletContext context, String language) {

URL screenDefinitionURL= null;

screenDefinitionURL= context.getResource("/WEB-INF/screendefinitions_" + language + ".xml");

if (screenDefinitionURL != null) {

Screens screenDefinitions= ScreenDefinitionDAO.loadScreenDefinitions(screenDefinitionURL);

if (screenDefinitions != null) {

allScreens.put(language, screenDefinitions);

} else {

System.err.println("Template Servlet Error Loading Screen Definitions: Confirm that file at URL

/WEB-INF/screendefinitions_" + language + ".xml contains the screen definitions");

}

} else {

System.err.println("Template ServletError Loading Screen Definitions: URL /WEB-

INF/screendefinitions_" + language + ".xml not found");

}

}

Page 47: Bridging Java And AspectJ [PPPJ08]

Example State—Point Separation

/**

* @author [redacted]

*/

public aspect AsyncsenderEjbHandler{

//QueueConnectionqConnect= null;

Map qConnect= new HashMap();

/*** AsyncSenderEJB***/

pointcut sendAMessage() :

execution(public void AsyncSenderEJB.sendAMessage(String));

pointcut createQueueConnectionHandler() :

call(* QueueConnectionFactory.createQueueConnection()) &&

withincode(public void AsyncSenderEJB.sendAMessage(String));

declare soft : JMSException: sendAMessage();

after() returning(QueueConnectionqc) : createQueueConnectionHandler() {

//Save inner method variable to local(multi-thread)

qConnect.put(Thread.currentThread().getName(), qc);

//qConnect= qc;

}

void around() throws EJBException: sendAMessage() {

try {

proceed();

} catch(Exception e) {

e.printStackTrace();

throw new EJBException("askMDBToSendAMessage: Error!",e);

} finally {

try {

QueueConnectionqConnectAux= (QueueConnection)qConnect.get(Thread.currentThread().getName());

if( qConnectAux!= null ) {

qConnectAux.close();

}

} catch(Exception e) {}

}

}

}