Upload
kevin-hoffman
View
1.700
Download
2
Embed Size (px)
DESCRIPTION
We demonstrate how to safely and effectively implement transactions following an aspect-oriented paradigm using our proposed extension of AspectJ, Explicit Join Points.
Citation preview
Aspect-oriented Transactions
via Explicit Join Points
Kevin Hoffman / Patrick Eugster
-2-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Outline
Review of Transactions
Review of Aspects
AOP Methods for Transactions
Discussion of problems
New Approach
The good, the bad, and the unknown
The next step
-3-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Review of Transactions
Transactions provide:
(A)tomicity – all or nothing
(C)onsistency – deterministic data changes
(I)solation – appears concurrency not present
(D)urability – finished results never lost
-4-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Review of Transactions [2]
Implementation approaches: Language approach
+ Safety
+ Elegance
- Flexibility
- Interoperability
Library approach- Safety
- Elegance
+ Flexibility
+ Interoperability
Can we do better?
-5-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Introducing AOP
Aspect Oriented Programming can do it! (or can it?)
AOP Motivation: Programs naturally decompose in multiple ways
(functional, information flow, …)
Programs cannot be captured by one way of decomposition
Cross-cutting concerns result from superimposing logic tied to one method of decomposition on top of another
(…Examples from the audience…)
-6-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Review of Aspects
AOP Solution:
Expose structural points of the program’s
primary decomposition as first class language
features (join points)
Provide first class language mechanisms
(aspects) to superimpose logic (advice)
systematically on top of the primary program
decomposition (via pointcuts)
-7-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
AspectJ: AOP’s “mother tongue”
Extension of Java
Primary program decomposition is functional
Join point model (100% implicit): Method calls, object construction, thrown exceptions
Member variable access
Pointcuts expressed via pattern matching
Philosophy of obliviousness Write your program ignoring cross cutting concerns
Add logic for these concerns ad hoc
Pray that you haven’t broken anything!
-8-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Simple AspectJ Example
class Bank {public void deposit(Account A, Account B, double amount){
...}
}
aspect Auditer {OutputStream m_logger = System.out;pointcut auditPoints(): call(public * Bank.*(..));around(): auditPoints() {
m_logger.println("BEFORE: "+thisJoinPoint.);proceed();m_logger.println("AFTER: "+thisJoinPoint.);
}}
-9-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Aspects and Transactions
AOP approach to transactions:+ Elegance
Separates base code and transactionalizing mechanisms
+ Flexibility
Easy to change implementation details anytime
+ Interoperability
Compiles to Java 2 bytecode
- Safety
Relies on “skill” of aspect programmer to maintain
-10-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Safety Issues with AOP
Why there is no safety for transactions: Advice introduces new error paths that the base code is not
constrained to handle
Implies there is a semantic coupling
But aspects provide only implicit, one-way communication from classes to aspects
Aspects can change anything at any time
Aspects superimposes stricter semantics upon classes, introducing problems (unsolvable deadlock)
Advice has a narrow focus (they affect one joinpoint at a time)
Changes in classes break aspects (mitigated by tools)
-11-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ AspectJ [1]
//First we naively write this class
class Bank {
public void transfer(Account A, Account B, double amount){
if (A.getBal() < amount){ … }
A.withdraw(amount);
B.deposit(amount);
}
public void waitAndTransfer(Account A, Account B, double amount)
{
A.deposit(amount);
while (B.getBalance() <= amount){}
B.withdraw(amount);
}
}
-12-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ AspectJ [2]
//And we blissfully write this simple class
class BankSystem {
public void mutualExch(Bank bank, Account A, Account B,
double a1, double a2){
Threads T = new Threads();
T.add(new Thread(bank.waitAndTransfer(A,B,a1)));
T.add(new Thread(bank.waitAndTransfer(B,A,a2)));
T.joinAll();
}
}
-13-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ AspectJ [3]
//The bank also serves as a travel agency…
class Agent {
…
void createTrip(Person p, Flight f, Hotel h) {
f.ReserveSeat(p);
f.ReserveRoom(p);
m_CC.Debit(p.getCC(), ...);
}
}
-14-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ AspectJ [4]
//Now we create a transactionalizing mechanisms using aspects
public abstract aspect TransactionalMethods {
abstract public pointcut MethodsToWrap();
void around() : MethodsToWrap() {
Object TARGET = thisJoinPoint.getTarget();
TransMan.beginTrans(TARGET);
boolean aborted = false;
try{ proceed(); } catch (Exception e){
TransMan.abortTrans(TARGET);
aborted=true; throw e;
} finally {
if (!aborted){ TransMan.commitTrans(TARGET); }
}
}
}
-15-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ AspectJ [5]
//Now we transactionalize these three classes post-mortem using aspects
aspect MakeBankTran extends TransactionalMethods {
public pointcut MethodsToWrap() :
call (public * Bank.deposit*(..));
}
aspect MakeBankSystemTran extends TransactionalMethods {
public pointcut MethodsToWrap() :
call (public * BankSystem.mutualExch(..));
}
aspect MakeAgentTran extends TransactionalMethods {
public pointcut MethodsToWrap() :
call (public * Agent.createTrip(..));
}
-16-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
New Error Paths
Can throw AbortException or CommitException, but there is no way
to constrain the base code to catch these exceptions…
-17-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
New Error Paths [2]
Oblivious code has no idea that the end of the method call could
result in an exception. Even wrapping m_CC.Debit with finally does
not help [exception is thrown after method exits].
-18-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Stricter Semantics --> Problems
The TransactionalMethods aspect wraps a transaction around
mutualExch, causing all data access to become serialized… Also, advice
is narrow and is not aware that scope of transaction contains 2 threads.
-19-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Stricter Semantics --> Problems [2]
<thread 1>
public void waitAndTransfer(Account Alice,
Account Bob, double amount)
{
Alice.deposit(amount);
while (Bob.getBalance() <= amount){}
Bob.withdraw(amount);
}
<thread 2>
public void waitAndTransfer(Account Bob,
Account Alice, double amount)
{
Bob.deposit(amount);
while (Alice.getBalance() <= amount){}
Alice.withdraw(amount);
}
Thread 1 acquires lock on Alice and Bob, preventing thread 2 from
depositing the amount needed to unblock thread 1 (because the aspect
does not know the 2 threads are part of the same transaction and forces
each to acquire separate locks). Unsolvable deadlock!
-20-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Our Proposed Solution
Make aspects an explicit part of mainline code
Mainline code tells aspect how to apply itself
Impose constraints on the interaction (bi-directional)
Explicitness allows for new types of interaction and structure
Define notion of explicit join points (EJPs): Explicitly declared (think of it as a new type)
Declaration defines properties, parameters, and constraints
Declared globally or within an [abstract] aspect
-21-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Introduction to EJPs
aspect AspectName {
[scoped] joinpoint AJPName (parameters) [throws ClassList];
}
class ClassName {
public void methodName(type1 parm1, type2 parm2, ...){
joinpoint AspectName.AJPName(parm2,parm2*parm1);
joinpoint AspectName.AJPNameNested(parm2,parm2*parm1){
…
}
}
}
-22-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ EJPs
//Actually write the aspect first (could be an abstract aspect…)
aspect Transactionalizer {
scoped joinpoint jpTrans(Serializable[] objects) throws CommitException;
pointcut pcTrans(Serializable[] objects) : joinpoint(jpTrans) && args(objects);
before(Serializable[] objs): pcTrans(objs) {
TransMan.begin(objs, ...);
}
after() returning: pcTrans() {
TransMan.commit(...);
}
after() throwing: pcTrans() {
TransMan.abort(...);
}
}
-23-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions w/ EJPs [2]
//Now write the code that is semantically tied to the (possibly) abstract aspectclass Agent implements Serializable {
void createTrip(Person p, Flight f, Hotel h) {boolean bCharged = false;try {
Serializable[] objects = {this, p, f, h};Transactionalizer.jpTrans(objects) {
f.ReserveSeat(p);f.ReserveRoom(p);m_CC.Debit(p.getCC(), ...);bCharged = true;
}} catch (CommitException e) {
if (bCharged){ m_CC.Credit(p.getCC(), ...); }}
}}
-24-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
EJP Error Path Constraints
CommitException
must be caught or a
compiler error is
generated
-25-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
EJP Scope Constraints
Aspect understands
scope of transaction
-26-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Explicit Join Pointspublic aspect TranMethods pertarget {
scoped jpTrans (pointcut)
throws CommitException;
public pointcut JPTransNewThread() :
within(jpTrans) &&
call (public * Thread.Thread(..));
public pointcut JPTransEnterScope() :
enterScope(jpTrans);
public pointcut JPTransExitScope() :
exitScope(jpTrans);
Threads m_T = new Threads();
void after() : JPTransNewThread() {
m_T.add(thisJoinPoint.getTarget());
}
void before() : JPTransEnterScope() {
TransMan.beginTrans(…);
}
void after() : JPTransExitScope() {
m_T.joinAll();
TransMan.commitTrans(…);
}
}
class Bank {
public void depositWait(Account A, Account B,
double amount)
{
A.deposit(amount);
while (B.getBalance() <= amount){}
B.withdraw(amount);
}
}
class BankSystem {
public void mutualExch(Bank bank
Account A, Account B,
double a1, double a2)
throws CommitException
{
TranMethods.jpTrans(
public * Bank.deposit*(..))
{
Threads T = new Threads();
T.add(new Thread(
bank.waitDeposit(A,B,a1)));
T.add(new Thread(
bank.waitDeposit(B,A,a2)));
T.joinAll();
}
}
}
-27-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Explicit Join Points
Why is it different than a function call?
Aspect(s) may apply advice at the explicit join point
Aspect instance can be specific to instance of target
method
Other joinpoints may be passed as parameters
Scoped explicit join points provide additional structure
Makes pointcuts more deterministic
-28-Kevin Hoffman and Patrick Eugster, DSN’06 (Handout)
Transactions via EJPs
+ Elegance Separates base code and transactionalizing mechanisms
+ Flexibility Easy to change implementation details anytime
+ Interoperability Compiles to Java 2 bytecode
+ Safety EJP enforces constraints on base code at compile time