Upload
eugene-spencer
View
214
Download
0
Embed Size (px)
Citation preview
Aspect-Oriented Programming w/AspectJ™
Cristina Lopes, Gregor Kiczales
Xerox PARC
© Copyright 1998, Xerox Corporation. All Rights Reserved.
www.parc.xerox.com/aop
2
• using AOP and AspectJ to:– improve the design and source code modularity
of systems that involve cross-cutting concerns
• aspects are:– a new kind of programming construct, that
• supports a new kind of module• enables concerns that cross-cut the system to be
captured in clean units
• AspectJ is:– is an aspect-oriented extension to Java™ that
supports general-purpose aspect programming
this tutorial is about...
3
an example systema distributed digital library
4
clean modular design
title: stringauthor: stringisbn: intpdf: pdftiff: tiff
LibraryBookholds* *
cooperates**
User
accesses
*
*
Printeruses* *
5
public class PrinterImpl { String status = “Idle” Vector jobs;
public PrinterImpl() {} pubilc get_status() { return status } public add_job(int j) { jobs.add(j); }}
Book
Printer
User Libraryclass Library { Hashtable books; Library(){ books = new Hashtable(100); } public Book getBook(User u, String title) { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b; } } return null; }}
class User { private String name; Library theLibrary; Printer thePrinter;
public User(String n) { name = n; }
public boolean getBook (String title) { Book aBook = theLibrary.getBook(this, title); thePrinter.print(this,aBook); return true; }}
class Book { private String title; private String author; private String isbn; private PostScript ps; private User borrower;
public Book(String t, String a, String i, PostScript p) { title = t; author = a; isbn = i; ps = p; }
public User get_borrower() {return borrower;} public void set_borrower(User u) {borrower = u;} public PostScript get_ps() { return ps; }}
clean modular code
6
benefits of good modularity
• aka “clean separation of concerns”• each decision in a single place
– easy to understand– easy to modify– easy to unplug
7
but...
• some issues don’t localize to objects– global coordination– interactions among many objects– resource sharing– error handling– performance optimizations– synchronization– . . .
8
Book
Printer
User Libraryclass Book { private BookID id; private PostScript ps; private UserID borrower;
public Book(String t, String a, String i, PostScript p) { id = new BookID(t,a,i); ps = p; }
public UserID get_borrower() {return borrower;} public void set_borrower(UserID u) {borrower = u;} public PostScript get_ps() { return ps; } public BookID get_bid() { return id; }}
class BookID { private String title; private String author; private String isbn;
public BookID(String t, String a, String i) { title = t; author = a; isbn = i; } public String get_title() {return title;}}
class User { private UserID id; Library theLibrary; Printer thePrinter;
public User(String n) { id = new UserID(n); }
public boolean getBook (String title) { BookID aBook=null; try{ aBook = theLibrary.getBook(id, title); } catch (RemoteException e) {} try { thePrinter.print(id, aBook); } catch (RemoteException e) {} return true; } public UserID get_uid() { return id; }}
class UserID { private String name;
public UserID(String n) { name = n; } public String get_name() { return name; }}
interface LibraryInterface extends Remote { public BookID getBook(UserID u, String title) throws RemoteException;
public PostScript getBookPS(BookID bid) throws RemoteException;}
class Library extends UnicastRemoteObject implements LibraryInterface { Hashtable books; Library() throws RemoteException { books = new Hashtable(100); } public BookID getBook(UserID u, String title) throws RemoteException { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b.get_bid(); } } return null; } public PostScript getBookPS(BookID bid) throws RemoteException { if (books.containsKey(bid.get_title())) { Book b = (Book)books.get(bid.get_title()); if (b != null) return b.get_ps(); } return null; }
}
interface PrinterInterface extends Remote { public boolean print (UserID u, BookID b) throws RemoteException;}
public class Printer extends UnicastRemoteObject implements PrinterInterface { private Vector jobs = new Vector(10, 10); private Library theLibrary;
public Printer() throws RemoteException{} public boolean print (UserID u, BookID b) throws RemoteException{ PostScript ps=null; try{ ps = theLibrary.getBookPS(b); } catch (RemoteException e) {} Job newJob = new Job (ps, u); return queue(newJob); } boolean queue(Job j) { //... return true; }}
digital library with optimization
9
Book
Printer
User Libraryclass Book { private BookID id; private PostScript ps; private UserID borrower;
public Book(String t, String a, String i, PostScript p) { id = new BookID(t,a,i); ps = p; }
public UserID get_borrower() {return borrower;} public void set_borrower(UserID u) {borrower = u;} public PostScript get_ps() { return ps; } public BookID get_bid() { return id; }}
class BookID { private String title; private String author; private String isbn;
public BookID(String t, String a, String i) { title = t; author = a; isbn = i; } public String get_title() {return title;}}
class User { private UserID id; Library theLibrary; Printer thePrinter;
public User(String n) { id = new UserID(n); }
public boolean getBook (String title) { BookID aBook=null; try{ aBook = theLibrary.getBook(id, title); } catch (RemoteException e) {} try { thePrinter.print(id, aBook); } catch (RemoteException e) {} return true; } public UserID get_uid() { return id; }}
class UserID { private String name;
public UserID(String n) { name = n; } public String get_name() { return name; }}
interface LibraryInterface extends Remote { public BookID getBook(UserID u, String title) throws RemoteException;
public PostScript getBookPS(BookID bid) throws RemoteException;}
class Library extends UnicastRemoteObject implements LibraryInterface { Hashtable books; Library() throws RemoteException { books = new Hashtable(100); } public BookID getBook(UserID u, String title) throws RemoteException { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b.get_bid(); } } return null; } public PostScript getBookPS(BookID bid) throws RemoteException { if (books.containsKey(bid.get_title())) { Book b = (Book)books.get(bid.get_title()); if (b != null) return b.get_ps(); } return null; }
}
interface PrinterInterface extends Remote { public boolean print (UserID u, BookID b) throws RemoteException;}
public class Printer extends UnicastRemoteObject implements PrinterInterface { private Vector jobs = new Vector(10, 10); private Library theLibrary;
public Printer() throws RemoteException{} public boolean print (UserID u, BookID b) throws RemoteException{ PostScript ps=null; try{ ps = theLibrary.getBookPS(b); } catch (RemoteException e) {} Job newJob = new Job (ps, u); return queue(newJob); } boolean queue(Job j) { //... return true; }}
“cross-cutting”
this optimization cross-cuts the primary structure of the program
10
effects of cross-cutting
• “tangled code”• code duplication,• difficult to reason about
– why is this here?– what does this connect to?– difficult to make changes
• essentially, it destroys modularity
11
the AOP idea
• many cross-cuts aren’t random!– they serve specific purposes:
• performance optimizations, interactions among objects, enforcing consistency, tracking global state…
– they have well-defined structure:• lines of dataflow, boundary crossings, points
of resource utilization, points of access…
• so… let’s capture the cross-cutting structure in a modular way...
12
class User { private UserID id; Library theLibrary; Printer thePrinter;
public User(String n) { id = new UserID(n); }
public boolean getBook (String title) { BookID aBook=null; try{ aBook = theLibrary.getBook(id, title); } catch (RemoteException e) {} try { thePrinter.print(id, aBook); } catch (RemoteException e) {} return true; } public UserID get_uid() { return id; }}
class UserID { private String name;
public UserID(String n) { name = n; } public String get_name() { return name; }}
interface LibraryInterface extends Remote { public BookID getBook(UserID u, String title) throws RemoteException;
public PostScript getBookPS(BookID bid) throws RemoteException;}
class Library extends UnicastRemoteObject implements LibraryInterface { Hashtable books; Library() throws RemoteException { books = new Hashtable(100); } public BookID getBook(UserID u, String title) throws RemoteException { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b.get_bid(); } } return null; } public PostScript getBookPS(BookID bid) throws RemoteException { if (books.containsKey(bid.get_title())) { Book b = (Book)books.get(bid.get_title()); if (b != null) return b.get_ps(); } return null; }
}
interface PrinterInterface extends Remote { public boolean print (UserID u, BookID b) throws RemoteException;}
public class Printer extends UnicastRemoteObject implements PrinterInterface { private Vector jobs = new Vector(10, 10); private Library theLibrary;
public Printer() throws RemoteException{} public boolean print (UserID u, BookID b) throws RemoteException{ PostScript ps=null; try{ ps = theLibrary.getBookPS(b); } catch (RemoteException e) {} Job newJob = new Job (ps, u); return queue(newJob); } boolean queue(Job j) { //... return true; }}
class Book { private BookID id; private PostScript ps; private UserID borrower;
public Book(String t, String a, String i, PostScript p) { id = new BookID(t,a,i); ps = p; }
public UserID get_borrower() {return borrower;} public void set_borrower(UserID u) {borrower = u;} public PostScript get_ps() { return ps; } public BookID get_bid() { return id; }}
class BookID { private String title; private String author; private String isbn;
public BookID(String t, String a, String i) { title = t; author = a; isbn = i; } public String get_title() {return title;}}
public class PrinterImpl { String status = “Idle” Vector jobs;
public PrinterImpl() {} pubilc get_status() { return status } public add_job(int j) { jobs.add(j); }}
class Library { Hashtable books; Library(){ books = new Hashtable(100); } public Book getBook(User u, String title) { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b; } } return null; }}
class User { private String name; Library theLibrary; Printer thePrinter;
public User(String n) { name = n; }
public boolean getBook (String title) { Book aBook = theLibrary.getBook(this, title); thePrinter.print(this,aBook); return true; }}
class Book { private String title; private String author; private String isbn; private PostScript ps; private User borrower;
public Book(String t, String a, String i, PostScript p) { title = t; author = a; isbn = i; ps = p; }
public User get_borrower() {return borrower;} public void set_borrower(User u) {borrower = u;} public PostScript get_ps() { return ps; }}
portal Printer { void print(Book book) { book: Book: {direct pages;}
}portal Library { Book find (String title){ return: Book: {copy title, author, isbn;} }}
this aspect localizes the cross-cutting in one place
aspects: a cross-cutting module
13
aspect-oriented programming
• “aspects” are program constructs that cross-cut classes1 in well-defined, principled ways– the programmer uses classes to
implement the primary module structure– the programmer uses aspects to
implement the cross-cutting module structure
1 or other appropriate primary program construct, i.e. procedures if you added AOP to Scheme
14
aspect weaving
• weaver combines classes and aspects– compiler / pre-processor / interpreter– unambiguously coordinates cross-cutting
aspect weaver
public class PrinterImpl { String status = “Idle” Vector jobs;
public PrinterImpl() {} pubilc get_status() { return status } public add_job(int j) { jobs.add(j); }}
class Library { Hashtable books; Library(){ books = new Hashtable(100); } public Book getBook(User u, String title) { System.out.println("REQUEST TO GET BOOK " + title); if(books.containsKey(title)) { Book b = (Book)books.get(title); System.out.println("getBook: Found it:" + b); if (b != null) { if (b.get_borrower() == null) b.set_borrower(u); return b; } } return null; }}
class User { private String name; Library theLibrary; Printer the; Printer
public User(String n) { name = n; }
public boolean getBook (String title) { Book aBook = theLibrary.getBook(this, title); thePrinter.print(this,aBook); return true; }}
class Book { private String title; private String author; private String isbn; private PostScript ps; private User borrower;
public Book(String t, String a, String i, PostScript p) { title = t; author = a; isbn = i; ps = p; }
public User get_borrower() {return borrower;} public void set_borrower(User u) {borrower = u;} public PostScript get_ps() { return ps; }}
portal Printer { void print(Book book) { book: Book: {direct pages;}
}
portal Library { Book find (String title){ return: Book: {copy title, author, isbn;} }}
classes
aspects
executable code
15
benefits of using AOP
• good modularity, even in the presence of cross-cutting concerns– less tangled code, more natural code,
smaller code– easier maintenance and evolution
• easier to reason about, debug, change
– more reusable• plug and play
16
AspectJ™ is…
• the first general-purpose AO language– “lexical” cross-cutting (current)– concern-specific libraries (current)– other kinds of cross-cutting (future)
• an extension to Java• freely available implementation
– user community will drive future design
17
looking ahead
• AOP works because cross-cutting modularities aren’t random
• aspects are used to capture the structure of the cross-cutting in clean ways
AspectJ mechanisms Part II:
the kinds of cross-cutting mechanisms AspectJ can capture
problem structure Part IV:
how to use AspectJ to capture the higher-level cross-cutting in your systems
18
outline• I AOP overview
– motivation, what is the AOP idea
• II AspectJ overview– basic concepts, kinds of cross-cutting
• III key technical details– running the aspect weaver, source files, emacs support
• IV using AspectJ– more realistic examples, kinds of aspects, elaboration of
language semantics
• V style issues– when to use aspects, aspect coding style issues
• VI related work– survey of other activities in the AOP world
Part IIBasic Concepts of AspectJ
20
goals of this chapter
• present the basic concepts of AspectJ– the different kinds of cross-cutting– when aspect code runs– what context aspect code runs in
• using a single simple example
• the next chapters elaborate on– environment, tools– details of the semantics– what can be written using AspectJ
21
overview
class instances
code (methods)and some state
state
whereas the underlying Java™ language supports classes and objects
22
overview
class
aspect
AspectJ extends this with aspects that cross-cut the code in classes in various ways…
23
overview
class class
aspect
AspectJ extends this with aspects that cross-cut the code in classes in various ways…
24
overview
class class
aspect aspect
AspectJ extends this with aspects that cross-cut the code in classes in various ways…
25
overview
class class
aspect aspect
and aspect instances that cross-cut the state in objects in various ways
26
a first example
class Point { int _x = 0; int _y = 0;
void set (int x, int y) { _x = x; _y = y; }
void setX (int x) { _x = x; }
void setY (int y) { _y = y; }
int getX() { return _x; }
int getY() { return _y; }}
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
27
what it does
Point p1 = new Point();Point p2 = new Point();
p1p2
The screen
p1.set(3, 3); W
Wp2.setX(3);
28
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
Point
ShowAccesses
one aspect, one class
this “advise weave” affects several methods in the class
static: no aspect instances involved
before: do this before method body
29
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
presentation note!
the syntax shown here is slightly simplified, mostly to save room on the slides
the full syntax for advise weaves on methods is presented in part iv
(also look at the SpaceWar code)
30
class Point { int _x = 0; int _y = 0;
void set (int x, int y) { System.out.println(“W”); _x = x; _y = y; } void setX (int x) { System.out.println(“W”); _x = x; } void setY (int y) { System.out.println(“W”); _y = y; } . .
here’s what we would have had to write in plain Java
without AspectJ
• what was in one place is instead in 3
• what was separated is now mixed in
31
Point p1 = new Point();Point p2 = new Point();
p1p2
The screen
p1.set(3, 3); W
Wp2.setX(3);
Rint x = p1.getX();
extend to writes and reads...
32
one aspect with two weaves
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
Point
just as a class can have n methods
an aspect can have n weaves
ShowAccessesaspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); } static before Point.getX, Point.getY,{ System.out.println(“R”); }}
33
one aspect, two classes
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
Point
ShowAccessesaspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); } static before Line.set, Line.setX1, ... { System.out.println(“W”); }}
Line
34
one aspect, two classes
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY { System.out.println(“W”); }}
Point
ShowAccesses
Line
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY, Line.set, Line.setX1, ... { System.out.println(“W”); }}
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY, Line.set, Line.setX1, ... { System.out.println(“W”); } static before Point.getX, Point.getY, Line.getX1, ... { System.out.println(“R”); } }
35
reads, writes and constructorsaspect ShowAccesses { static before Point.set, Point.setX, Point.setY Line.set, Line.setX1, ... { System.out.println(“W”); } static before Point.getX, Point.getY, Line.getX1, ... { System.out.println(“R”); } static before Point(*), Line(*) { System.out.println(“C”); } }
methods
constructors
36
summary so far
ShowAccesses
Pointone aspect, one class
ShowAccesses
Pointone aspect, two classes
Line
static advise weaves allow aspect to:
• extend existing class definitions independent of any aspect instances
• by modifying existing methods/constructors
• nxm cross-cutting is coming up
37
aspects are special classesclass Point { … }
class Line { … }
class Circle { … }
aspect LogNewInstances { static DataOutputStream log = null; static { // initializer try { log = new DataOutputStream( new FileOutputStream(“log”)); } catch (IOException e) {} } static after Point(*), Line(*), Circle(*) { long time = System.currentTimeMillis(); try { log.writeBytes(“New instance at ” + time + “ ” + thisObject.toString() + “\n”); } catch (IOException e) {} }}
LogNewInstances
Point
Line
Circle
the objectaspect var
38
what it does
Point p1 = new Point(); p1
New instance at 3252384 Point321
p2
New instance at 3252451 Point322
Point p2 = new Point();
l1
New instance at 3252653 Line611
Line l1 = new Line();l2
New instance at 3255678 Line612
Line l2 = new Line();l3
New instance at 3258765 Line613
Line l3 = new Line();c1
New instance at 4532805 Circle901
Circle c1 = new Circle();
39
per-object aspect-related state
make Points go back to 0,0 after every 100 sets
Point
AutoReset
aspect AutoReset { static new int Point.count = 0; static after Point.set, Point.setX, Point.setY { if ( ++count >= 100 ) { count = 0; _x = 0; _y = 0; } }}
“new weaves” add definitions
• new fields, methods and constructors
• must be static
object’s scope
available
40
a peek ahead to style issues
make Points go back to 0,0 after every 100 sets
Point
AutoReset
aspect AutoReset { static new int Point.count = 0; static after Point.set, Point.setX, Point.setY { if ( ++count >= 100 ) { count = -1; set(0, 0); } }}
“new weaves” add definitions
• new fields, methods and constructors
• must be static
what is the class /
aspect inferace?
41
how this runs
count=0
Point p1 = new Point(); p1
count=0
Point p2 = new Point(); p2
p2.setY(7);
count=1
p1.set(3, 3);
count=1
p1.setX(3);
count=2
42
summary so farShowAccesses
Pointn aspects, m classes
Line
Point
AutoReset
adding state to each object
• advise weaves allow aspect to modify existing class definitions
• new weaves allow aspect to add to existing class definitions
• static means independent of any aspect instances
• nxm cross-cutting
43
aspect instances
Point
AutoReset
aspect can be instantiated and aspect instances can then be
associated with objects
as a first step, move the counter from objects to
aspect instances
44
simple use of aspect instancesaspect AutoReset { int count = 0; after Point.set, Point.setX, Point.setY { if (++count >= 100) { count = -1; set(0, 0); } }}
non-static
not a weave
45
what it does
Point p1 = new Point(); p1
Point p2 = new Point(); p2
AutoReset a1 = new AutoReset();
count=0
a1.addObject(p1);
count=0
AutoReset a2 = new AutoReset();
a2.addObject(p2);
p1.set(3, 3);
count=1
p1.setX(3);
count=2
p2.setY(7);
count=1
after instantiation of every Point object, instantiate an
AutoReset aspect and associate the two
46
one aspect instance, many objects
Point p1 = new Point(1, 1);
p1
Point p2 = new Point(1, 1);
p2
Point p3 = new Point(1, 1);
p3a1.addObject(p1);
a1.addObject(p3);
a1.addObject(p2);
the goal here is to get all of the Points to share a single AutoReset counter, so that they all reset, when as a group they have been set 100 times
AutoReset a1 = new AutoReset();
47
one aspect instance, many objects
aspect AutoReset { int count = 0; after Point.set, Point.setX, Point.setY { if (++count >= 100) { Vector objects = thisAspect.getObjects(); Enumeration e = objects.elements(); while (e.hasMoreElements()) { Point p = (Point)e.nextElement(); count = -1; p.set(0, 0); } } }}
aspect.getObjects() returns a vector of the objects
object.getAspects() returns a vector of the aspects
48
aspects compose
Line
ShowAccesses
Point Circle
LogNewInstances
AutoReset
• all three aspects apply to the class Point– more on controlling this in next part
• code retains good separation of concerns
49
here’s the complete code written with AspectJ
with AspectJclass Point { int _x = 0; int _y = 0;
void set (int x, int y) { _x = x; _y = y; }
void setX (int x) { _x = x; }
void setY (int y) { _y = y; }
int getX() { return _x; }
int getY() { return _y; }}
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY Line.set, Line.setX1, ... { System.out.println(“W”); } static before Point.getX, Point.getY, Line.getX1, ... { System.out.println(“R”); } static before Point(*), Line(*) { System.out.println(“C”); } }
aspect LogNewInstances { static DataOutputStream log = null; static { // initializer try { log = new DataOutputStream( new FileOutputStream(“log”)); } catch (IOException e) {} } static after Point(*), Line(*), Circle(*) { long time = System.currentTimeMillis(); try { log.writeBytes(“New instance at ” + time + “ ” + this.toString() + “\n”); } catch (IOException e) {} }}
aspect AutoReset { int counter = 0; after Point.set, Point.setX, Point.setY { if (++counter >= 100) { count = -1; set(0, 0); } }}
50
class Point { int _x = 0; int _y = 0;
Point () { System.out.println("C"); long time = System.currentTimeMillis(); try { log.writeBytes("New instance at " + time +
" " + Point.this.toString() + "\n"); } catch (IOException e) { } }
void set(int x, int y) { System.out.println("W"); _x = x; _y = y; doAutoResets(); }
void setX(int x) { System.out.println("W"); _x = x; doAutoResets(); }
void setY(int y) { System.out.println("W"); _y = y; doAutoResets();
}
int getX() { System.out.println("R"); return _x; }
int getY() { System.out.println("R"); return _y; }
here’s a well-written version of what we would have had to write in plain Java
without AspectJ
Vector autoResets = new Vector(); void doAutoResets() { java.util.Enumeration enumeration = autoResets.elements(); AutoReset reset; while (enumeration.hasMoreElements()) { reset = (AutoReset)enumeration.nextElement();
if(++ reset.count >= 100){ System.out.println("Reseting " + this + "."); reset.count = - 1; set(0,0);}
} }
static DataOutputStream log = null; static { try { log = new DataOutputStream(new FileOutputStream("log")); } catch (IOException e) { } } }
class AutoReset { int count = 0; public void addToScope(Point object) { object.autoResets.addElement(this); } public void removeFromScope(Point object) { object.autoResets.removeElement(this); } }
51
benefits of using AOP
• good modularity, even in the presence of cross-cutting concerns– less tangled code, more natural code,
smaller code– easier maintenance and evolution
• easier to reason about, debug, change
– more reusable• plug and play
52
summary
• advise weaves, new weaves• different kinds of cross-cutting
static and non-static• weave code runs in mixed scope
Line
ShowAccesses
PointCircle
LogNewInstances
AutoReset
53
looking ahead
• AOP works because cross-cutting modularities aren’t random
• aspects are used to capture the structure of the cross-cutting in clean ways
AspectJ mechanisms Part II: the low-level kinds of cross-
cutting AspectJ can capture
problem structure Part III: how to use AspectJ to capture
the higher-level kinds of cross-cutting in your systems
Part IIIUsing AspectJ, the tool
55
source files
Point
ShowAccess
“.ajava” files contain:
• regular Java source code• aspect source code
56
processing aspects
Point
ShowAccess
aspectweaver
.ajava files
bytecode
.classfiles
57
intermediate files
Point
ShowAccess
aspectweaver
.ajava files
bytecode
.classfiles
.javafiles
Javacode
pre-processor:• simplicity• transition strategy
58
programming environment
• source files– extension– contents
• look&feel of ajava mode for emacs– what aspects apply where
• weaving• woven code and tracking errors
59
demo of the AspectJ tools
Part IV - Using Aspects
AspectJ mechanisms Part II:
the kinds of cross-cutting mechanisms AspectJ can capture
problem structure Part IV:
how to use AspectJ to capture the higher-level cross-cutting in your systems
61
purpose and structure
• styles of aspects presented with examples:– common implementations
– interaction (protocols)
– shared state
– specialized aspect libraries
interleavedwithmore detailaboutAspectJ
62
notation
Class object
Aspect
Interface
aspectinstance
slide containingsemantic rulesor conventions
piece of implementation
63
terminology
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY Line.set, Line.setX1, ... { System.out.println(“W”); }}
designators
weave mode
weave declaration(“weave”, for short)
this syntax is slightly simplified, mostly to save room on the slides
see next slide for the full syntax for designators
64
designators
• methods: ResultType Type.Id ( Formals )
• constructors: Type ( Formals )
• initializers: Type
• fields: Type.Id
examples:
void Point.set(int x, int y) * Point.set(*) shapes.util.Point(*) Point(int x, int y) shapes.Point.x
methods
field
constructors
ResultType: Type | *Formals: TrueFormals | *
65
outline
• using aspects to localize :– common implementations– interaction (protocols)– shared state
• specialized aspect libraries
66
example: plug & play tracing
want to print out a trace message before and afterevery method of these classes;want to be able to turn it off
class Customer { … }
class Order { … }
class Product { … }
67
with an aspect
Customer
Order
Product
aspect Trace { static before * Customer.*(*), * Order.*(*), * Product.*(*) { System.out.println(“ENTERING ” + thisMethodID); }
static after * Customer.*(*), * Order.*(*), * Product.*(*) { System.out.println(“EXITING ” + thisMethodID); }
}
68
plug and play (plug and debug)
• plug in:
• plug out:
– turn debugging on/off without editing classes
– debugging disabled with no runtime cost– can save debugging code between uses
ajweaver Customer.ajava Order.ajava Product.ajava Trace.ajava
ajweaver Customer.ajava Order.ajava Product.ajava
69
special vars, scope rules
• specials:– thisAspect– thisObject– thisClassName– thisMethodName
• scope rules in body of weaves:1. formals2. aspect3. object
strings
70
an exception aspect
XeroxServer
Status getStatus()void registerClient(Client c)
in all implementationsof this interface, want to log exceptions
71
aspect XeroxServerCatcher {
static catch XeroxServer.getStatus, XeroxServer.registerClient (Exception e) { System.err.println(“Exception ” + e + “ at ” + System.currentTimeMillis()); throw e; }}
an exception aspect
type name
aspect XeroxServerCatcher {
static catch XeroxServer.getStatus, XeroxServer.registerClient (Exception e) { System.err.println(“Exception ” + e + “ at ” + System.currentTimeMillis()); throw e; }}
72
types
• in Java:– Interface = Type (no implementation)– Class = Type + implementation
• designators refer to types– aspects apply to interfaces too!
• weaves apply to all implementations
of the designated types
73
aspects for interfaces
XeroxServer XeroxServerCatcher
Class1 Class2 Class3 ...
implementsimplementsimplements
XeroxServerCatcher affectsClass1, Class2, Class3, ...
74
aspects that localize common code
A
B
C
A
B
C
Aspect
used in• set-ups/clean-ups• tracing• logging • method guards• ...
75
aspects that localize common code
• there is an abstraction occurring in several classes
• this abstraction can be implemented by a common piece of code
• inheritance is not best model or gets in the way of other code reuse
• aspect contains the common piece of code
76
outline
• using aspects to localize :– common implementations– interaction (protocols)
• aspects as repositories of related implementations
• design patterns
– shared state
• specialized aspect libraries
77
example: tic-tac-toe
Player
public: run() getTurn() endGame(Player, Result) actionPerformed(ActionEvent) mouseClicked(MouseEvent)protected: addStatusDisplay()
TicTacToe
public: run() startGame() newPlayer(Player) putMark(Player, Position) getBoard() getNPlayers() getCurrentPlayer() isOver() getResult() attach(TTTObserver) detach(TTTObserver)protected: waitForPlayers() waitForMove() endGame() notify() on state changes
TTTObserver
public: actionPerformed(ActionEvent) aboutWindow() update()protected: addMenu() addBoardDisplay() addStatusDisplay() finish()
2
TicTacToe
public: run() startGame() newPlayer(Player) putMark(Player, Position) getBoard() getNPlayers() getCurrentPlayer() isOver() getResult() attach(TTTObserver) detach(TTTObserver)protected: waitForPlayers() waitForMove() endGame() notify() on state changes
TTTObserver
public: actionPerformed(ActionEvent) aboutWindow() update()protected: addMenu() addBoardDisplay() addStatusDisplay() finish()
plus other classes...
78
view of the update protocol
Player
TicTacToe
public: ... attach(TTTObserver) detach(TTTObserver)protected: notify() on state changes
TTTObserver
public: update()
Canvas
Label
BoardDisplayupdate()
StatusDisplayupdate()
6 tightly relatedmethods andcalls to notify()are spread all over
79
the protocol in an aspect
TicTacToe
StatusDisplay
TTTObserver
BoardDisplay
aspect TTTDisplayProtocol { static new Vector TicTacToe.observers = new Vector(); static new Tictactoe TTTObserver.game;
static new void TicTacToe.attach(TTTObserver obs) { observers.addElement(obs); } static new void TicTacToe.detach(TTTObserver obs) { observers.removeElement(obs); } static new void TTTObserver.update() { board.update(game); status.update(game); } static new void BoardDisplay.update(TicTacToe game), StatusDisplay.update(TicTacToe game) { theGame = game; repaint(); } // all methods that change state static after TicTacToe.startGame, TicTacToe.newPlayer, TicTacToe.putMark, TicTacToe.endGame { for (int i = 0; i < observers.size(); i++) ((Observer)observers.elementAt(i)).update(); }}
aspect TTTDisplayProtocol { static new Vector TicTacToe.observers = new Vector(); static new Tictactoe TTTObserver.game;
static new void TicTacToe.attach(TTTObserver obs) { observers.addElement(obs); } static new void TicTacToe.detach(TTTObserver obs) { observers.removeElement(obs); } static new void TTTObserver.update() { board.update(game); status.update(game); } static new void BoardDisplay.update(TicTacToe game), StatusDisplay.update(TicTacToe game) { theGame = game; repaint(); } // all methods that change state static after TicTacToe.startGame, TicTacToe.newPlayer, TicTacToe.putMark, TicTacToe.endGame { for (int i = 0; i < observers.size(); i++) ((Observer)observers.elementAt(i)).update(); }}
aspect TTTDisplayProtocol { static new Vector TicTacToe.observers = new Vector(); static new Tictactoe TTTObserver.game;
static new void TicTacToe.attach(TTTObserver obs) { observers.addElement(obs); } static new void TicTacToe.detach(TTTObserver obs) { observers.removeElement(obs); } static new void TTTObserver.update() { board.update(game); status.update(game); } static new void BoardDisplay.update(TicTacToe game), StatusDisplay.update(TicTacToe game) { theGame = game; repaint(); } // all methods that change state static after TicTacToe.startGame, TicTacToe.newPlayer, TicTacToe.putMark, TicTacToe.endGame { for (int i = 0; i < observers.size(); i++) ((Observer)observers.elementAt(i)).update(); }}
aspect TTTDisplayProtocol { static new Vector TicTacToe.observers = new Vector(); static new Tictactoe TTTObserver.game;
static new void TicTacToe.attach(TTTObserver obs) { observers.addElement(obs); } static new void TicTacToe.detach(TTTObserver obs) { observers.removeElement(obs); } static new void TTTObserver.update() { board.update(game); status.update(game); } static new void BoardDisplay.update(TicTacToe game), StatusDisplay.update(TicTacToe game) { theGame = game; repaint(); } // all methods that change state static after TicTacToe.startGame, TicTacToe.newPlayer, TicTacToe.putMark, TicTacToe.endGame { for (int i = 0; i < observers.size(); i++) ((Observer)observers.elementAt(i)).update(); }}
aspect TTTDisplayProtocol { static new Vector TicTacToe.observers = new Vector(); static new Tictactoe TTTObserver.game;
static new void TicTacToe.attach(TTTObserver obs) { observers.addElement(obs); } static new void TicTacToe.detach(TTTObserver obs) { observers.removeElement(obs); } static new void TTTObserver.update() { board.update(game); status.update(game); } static new void BoardDisplay.update(TicTacToe game), StatusDisplay.update(TicTacToe game) { theGame = game; repaint(); } // all methods that change state static after TicTacToe.startGame, TicTacToe.newPlayer, TicTacToe.putMark, TicTacToe.endGame { for (int i = 0; i < observers.size(); i++) ((Observer)observers.elementAt(i)).update(); }}
aspect containsfields, methods and proceduresof protocol
80
observer pattern
Subject attach(Observer) detach(Observer) notify() on state changes
Observer update()
ConcreteObserverupdate()
ConcreteSubject
• protocol is spread in many classes• not so good in Java - uses up only inheritance link
With inheritance
observers.addElement(obs);
observers.removeElement(obs);
81
observer pattern
Observerupdate()
ConcreteObserverupdate()
implements
Subjectattach(Observer)detach(Observer)
implements
ConcreteSubject attach(Observer) detach(Observer)notify() on state changes
• protocol is spread in many classes• some replication of code in the concrete subjects
with interfaces
observers.addElement(obs);
observers.removeElement(obs);
82
observer patternwith aspects
ConcreteObserverimplements
implementsConcreteSubject
• protocol implementation is encapsulated in the aspects
Subjectattach(Observer)detach(Observer)
Observerupdate()
SubjectObserverstatic new Observer.subjectstatic new Subject.observersstatic new Subject.attach(Observer)static new Subject.detach(Observer)
observers.addElement(obs);
observers.removeElement(obs);
ConcreteObserverUpdatesstatic new ConcreteObserver.update()static after ConcreteSubject.m1, ConcreteSubject.m2, ...
for (int i = 0; i < observers.size(); i++) { obs = observers.elementAt(i); obs.update();}
83
aspects that localize interaction
A
B
C
1
2 3
4 A
B
C
Aspect
used in• multi-class protocols
– GUIs– notification– ...
• many design patterns
84
aspects that localize interaction
• there is an interaction (protocol) involving several classes
• methods of the protocol are more coupled with each other than with the classes where they “sit”
• aspect captures the whole protocol
85
outline
• using aspects to localize :– common implementations– interaction (protocols)– shared state
• static vs non-static state• when to use aspect instances
• specialized aspect libraries
86
static vs. non-staticaspect Count1 { static int count = 0; static before Point.set, Point.setX, Point.setY { count++; }}
aspect Count2 { int count = 0; before Point.set, Point.setX, Point.setY { thisAspect.count++; }}
class Point { int _x = 0; int _y = 0;
void set (int x, int y) { _x = x; _y = y; }
void setX (int x) { _x = x; }
void setY (int y) { _y = y; }
int getX() { return _x; }
int getY() { return _y; }}
87
static vs. non-static fieldsaspect Count1 { static int count = 0; static before Point.set, Point.setX, Point.setY { count++; }}
one incarnation of count (same as static in class fields)
one incarnation of count per aspect instance(same as non-static in class fields)
Point p0 = new Point(0,0);Point p1 = new Point(1,1);
aspect Count2 { int count = 0; before Point.set, Point.setX, Point.setY { thisAspect.count++; }}
Point p0 = new Point(0,0);Point p1 = new Point(1,1);
88
static vs. non-static weaves
two issues:
1. objects affected by weave2. the existence of “thisAspect”
related issue: the existence of “thisObject”
89
static vs. non-static weaves (1)
aspect Count2 { int count = 0; before Point.set, Point.setX, Point.setY { thisAspect.count++; }}
Point p0 = new Point(0,0);Point p1 = new Point(1,1);p0.set(3,3); p1.set(4,4);
aspect Count1 { static int count = 0; static before Point.set, Point.setX, Point.setY { count++; }}
Point p0 = new Point(0,0);Point p1 = new Point(1,1);p0.set(3,3); p1.set(4,4);
static weave affectsall Point objects, always
Count2 count = new Count2();count.addObject(p0);p0.set(3,3);
non-static weave affects justthe Point objects associated with an instance of Count2
90
static vs. non-static weaves (2)aspect Count1 { static int count = 0; static before Point.set, Point.setX, Point.setY { count++; }}
like a static method, thisweave is executed withoutreference to a particularaspect instance
may be omitted
aspect Count2 { int count = 0; before Point.set, Point.setX, Point.setY { thisAspect.count++; }}
like a non-static method, thisweave is executed withrespect to particularaspect instance(s), thisAspect
thisAspect available in non-static weaves
91
static vs. non-static (methods)aspect Count1 { static int count = 0; static before Point.set, Point.setX, Point.setY { count++; }}
class Point { int _x = 0; int _y = 0;
void set (int x, int y) { _x = x; _y = y; } void setX (int x) { _x = x; } void setY (int y) { _y = y; } int getX() { return _x; } int getY() { return _y; }}
non-static methods
no particular aspect instance(no thisAspect)buta particular Point object, thisObject
thisObject available in static and non-static weaves of non-static methods
92
static vs. non-static (specials)
static weave
non-static weave
static method
non-staticmethod
available: thisObject thisAspect thisClassName thisMethdName
available: thisObject thisClassName thisMethodName
available: thisClassName thisMethodName
93
special methods
• for aspects– void addObject(Object o)– void removeObject(Object o)– Vector getObjects()
• for objects– Vector getAspects()
94
example: a shared log file
want to log new instances of these classes in one log fileCustomer
Order
Product
95
static aspect state
Customer
Order
Product
aspect LogNewInstances { static DataOutputStream log = null; static { // initializer try { log = new DataOutputStream( new FileOutputStream(“log”)); } catch (IOException e) {} } static after Customer(*), Order(*), Product(*) { long time = System.currentTimeMillis(); try { log.writeBytes(“New instance at ” + time + “ ” + thisObject.toString() + “\n”); } catch (IOException e) {} }}
globallyshared byCustomer,Order,Product
96
example: monitoring
DBServer
PrinterServer
WebServer
DocumentServer
ServiceMonitoring
97
example: monitoringpublic class DBServer { public Result query(Query q) { monitor.anEvent(new SEvent(this)); try { return processQuery(q); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally { monitor.anEvent(new OKEvent(this)); }
} public Status getStatus() { monitor.anEvent(new IEvent(this)); return status;
} ...}
public class PrinterServer { public void print(Path p) { monitor.anEvent(new SEvent(this)); try { printit(p); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public Status getStatus() { monitor.anEvent(new IEvent(this)); return status;
} ...}
public class WebServer { public void post(Data d) { monitor.anEvent(new SEvent(this)); try { postit(d); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public Data get(Path p) { monitor.anEvent(new SEvent(this)); try { return getit(p); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} ...}
public class DocumentServer { public Doc convert(Doc d,Format f) { monitor.anEvent(new SEvent(this)); try { return convertData(d, f); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public void newDocument(Doc d) { monitor.anEvent(new SEvent(this)); try { addDocument(d); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} ...}
98
example: monitoringpublic class DBServer { public Result query(Query q) { monitor.anEvent(new SEvent(this)); try { return processQuery(q); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally { monitor.anEvent(new OKEvent(this)); }
} public Status getStatus() { monitor.anEvent(new IEvent(this)); return status;
} ...}
public class PrinterServer { public void print(Path p) { monitor.anEvent(new SEvent(this)); try { printit(p); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public Status getStatus() { monitor.anEvent(new IEvent(this)); return status;
} ...}
public class WebServer { public void post(Data d) { monitor.anEvent(new SEvent(this)); try { postit(d); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public Data get(Path p) { monitor.anEvent(new SEvent(this)); try { return getit(p); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} ...}
public class DocumentServer { public Doc convert(Doc d,Format f) { monitor.anEvent(new SEvent(this)); try { return convertData(d, f); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} public void newDocument(Doc d) { monitor.anEvent(new SEvent(this)); try { addDocument(d); } catch (Exception e) { monitor.anEvent(new XEvent(this)); } finally {monitor.anEvent(new OKEvent(this));}
} ...}
monitor.anEvent(new SEvent(this));try { return processQuery(q); }catch (Exception e) { monitor.anEvent(new XEvent(this));} finally { monitor.anEvent(new OKEvent(this));}
99
aspect as monitor (static state)
aspect ServiceMonitoring { static Vector serversInfo = new Vector(5); static int totalServiceRequests, totalExceptions, totalCompletions;
static before DBServer.query, WebServer.post, WebServer.get, PrinterServer.print, DocumentServer.convert, DocumentServer.newDocument { anEvent(new SEvent(thisObject)); } more befores, afters, catches, finally
static void anEvent(Event e) { update aspect state; sometimes sendCommand } static protected void sendCommand(Server srv, Command c) { srv.anOrder(c); }}
100
one monitor for all servers (static state)
DBServer
PrinterServer
WebServer
DocumentServer
ServiceMonitoringstatic state
staticweaves
• only one incarnation of the static variables
• weaves statically associated with classes (they affect all objects)
101
the need for aspect instances
• need different instances of aspect state – e.g. different monitors for different
groups of servers
• need a handle for the aspect state– e.g. remote service monitor
102
service monitorsaspect ServiceMonitoring { static Vector serversInfo = new Vector(5); static int totalServiceRequests, totalExceptions, totalCompletions;
static before DBServer.query, WebServer.post, WebServer.get, PrinterServer.print, DocumentServer.convert, DocumentServer.newDocument { anEvent(new SEvent(thisObject)); } more befores, afters, catches, finally
static void anEvent(Event e) { update aspect’s state; sometimes sendCommand } static protected void sendCommand(Server srv, Command c) { srv.anOrder(c); }}
103
monitors for groups of servers
ServiceMonitoring
DBServer
PrinterServer
WebServer
DocumentServer
state
state
non-staticweaves
aServiceMonitor.addObject(aServer)
104
inheritance of aspects
class MyDocumentServer extends DocumentServer {
protected boolean checkConsistency() { ... }
public Doc convert(Doc d, Format f) { Doc newDoc = super.convert(d, f); if (checkConsistency(newDoc)) return newDoc; else return null; }
}
override
extension
105
inheritance of aspects
SuperClass
SubClass
Aspect
extends
overriding of methods in sub: • aspect still applies and • doesn’t repeat in calls to super
SuperType applies to all implementationsof designated types
Aspect subclass implementsSuperType
106
inheritance of aspects
A foo()
B foo()
extends
Aspect1 before A.foo {...}
what code runs in•(new A()).foo() ? Aspect1’s before, A.foo
•(new B()).foo() ? Aspect1’s before, Aspect2’s before, B.foo
• if B.foo calls super.foo() ? same as previous; Aspect1’s before does not repeat
Aspect2 before B.foo {...}
107
the effective method
{ try { all befores < call to method containing original body > all afters } all catches all finallys}
108
aspect as monitor (static state)
aspect ServiceMonitoring { static Vector serversInfo = new Vector(5); static int totalServiceRequests, totalExceptions, totalCompletions;
static before DBServer.query, WebServer.post, WebServer.get, PrinterServer.print, DocumentServer.convert, DocumentServer.newDocument { anEvent(new SEvent(thisObject)); } more befores, afters, catches, finally
static void anEvent(Event e) { update aspect state; sometimes sendCommand } static protected void sendCommand(Server srv, Command c) { srv.anOrder(c); }}
109
inheritance between aspects
aspect VerboseMonitor extends ServiceMonitoring { ObjectOutputStream logStream;
VerboseMonitor() { initialize logStream }
protected void log(Event e) { logStream.writeObject(e); }
public void anEvent(Event e) { System.out.println(e.toString()); log(e); super.anEvent(e); }
}
override
extension
110
inheritance between aspectsSuperAspect
SubAspect
extends• similar to inheritance between classes:– subaspect inherits methods, fields
and weaves of superaspect– subaspect can extend superaspect
with new methods, fields and weaves– subaspect can override methods and
fields of superaspect (for now)
111
aspects that localize shared state
A
B
C
O
A
B
C
Aspect
used in• multi-class protocols with state
– authentication– monitoring– object management– ...
• encapsulation of state relating to groups of objects
112
aspects that localize shared state
• state needs to be encapsulated• an object / a class would do, but• interactions that affect the state of
such object are well localized
• aspect contains that state and the weaves for interaction
113
outline
• using aspects to localize :– common implementations– interaction (protocols)– shared state
• specialized aspect libraries– cross-cutting abstractions– code-intensive, repetitive actions
(goal of all libraries)
114
the cross-cutting nature of concurrency control
Problem: a square and a circle are manipulated by several concurrent threads. They can both change color. However, they can’t both be red at the same time; a changeColor(red) request on one of them must wait if the other is currently red.
Not OKOK
115
cross-cutting is unavoidable
the cross-cutting nature of concurrency control
class Square extends Shape { Pos pos; int size; Color color; fields for coordination
public void changeColor(Color c){ some coordination code here color = c; some coordination code here } …}
class Circle extends Shape { Pos pos; int r; Color color; fields for coordination
public void changeColor(Color c){ some coordination code here color = c; some coordination code here } …}
code may be more or less encapsulated, but
116
aspect NoSimultaneousRed { Shape redShape = null; // holds the reference to // the red shape or null
before void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color == Color.red) { synchronized (thisAspect) { while (!(redShape == null || redShape == thisObject)) { try { wait(); } catch (InterruptedException e) {} } redShape = thisObject; } }
} after void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color != Color.red) { synchronized (thisAspect) { if (thisObject == redShape) // coming out of red redShape = null; notifyAll(); } }
}}
using an aspectaspect NoSimultaneousRed { Shape redShape = null; // holds the reference to // the red shape or null
before void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color == Color.red) { synchronized (thisAspect) { while (!(redShape == null || redShape == thisObject)) { try { wait(); } catch (InterruptedException e) {} } redShape = thisObject; } }
} after void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color != Color.red) { synchronized (thisAspect) { if (thisObject == redShape) // coming out of red redShape = null; notifyAll(); } }
}}
117
using an aspectaspect NoSimultaneousBlue { Shape blueShape = null; // holds the reference to // the blue shape or null
before void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color == Color.blue) { synchronized (thisAspect) { while (!(blueShape == null || blueShape == thisObject)) { try { wait(); } catch (InterruptedException e) {} } blueShape = thisObject; } }
} after void Square.changeColor(Color color), void Circle.changeColor(Color color) { if (color != Color.blue) { synchronized (thisAspect) { if (thisObject == blueShape) // coming out of blue blueShape = null; notifyAll(); } }
}}
can pull outcommoncodeinto asuper
118
using coolibaspect NoSimultaneousRed extends Coordinator { Shape redShape = null; before void Square.changeColor(String color), void Circle.changeColor(String color) { if (color == Color.red) guardedEntry(thisMethodID, new Condition() { boolean checkit() { return (redShape == null || redShape == thisObject);}}, new CoordinationAction() { void doit() { redShape = thisObject; } });
} after void Square.changeColor(String color), void Circle.changeColor(String color) { if (color != Color.red) guardedExit(thisMethodID, new CoordinationAction() { void doit() {if (thisObject==redShape) redShape = null;}});
}}
aspect NoSimultaneousRed extends Coordinator { Shape redShape = null; before void Square.changeColor(String color), void Circle.changeColor(String color) { if (color == Color.red) guardedEntry(thisMethodID, new Condition() { boolean checkit() { return (redShape == null || redShape == thisObject);}}, new CoordinationAction() { void doit() { redShape = thisObject; } });
} after void Square.changeColor(String color), void Circle.changeColor(String color) { if (color != Color.red) guardedExit(thisMethodID, new CoordinationAction() { void doit() {if (thisObject==redShape) redShape = null;}});
}}
119
coolib
• avoid writing ad-hoc coordination code in the classes: use coordination aspects
• provide high-level constructs for programming coordination
120
coolib
• mutual exclusion• coordination state• conditional suspension/notification• coordination actions
121
coolib
Coordinator
guardedEntry(String methName, boolean condition, CoordinationAction act)guardedExit(String methName, CoordinationAction act)addSelfex(String methName)addMutex(String[] methNames)
some other variants ofguardedEntry and guardedExit
CoordinationAction
doit()
Condition
checkit()
included in distribution of AspectJ
122
summary• aspects capture design abstractions
that involve several classes• many different flavors of aspects
– common implementations– protocols– state plus protocol
• aspects build on existing practices and techniques for OO– instantiation, inheritance– libraries for programming aspects
(e.g. coolib)
Part VStyle Issues
124
outline
• when to use aspects– what existing knowledge tells us
• coding style issues– aspect/class interface– recursion
125
good designs
• capture “the story” well• may lead to good
implementations, measured by– code size– tangling– coupling– etc.
learned through experience,
influenced by taste and style
126
3 examples
• pull out access tracing • pull out error handling• pull out X and Y
127
a good aspect (access tracing)class Point { ... void set (int x, int y) {...} void setX (int x) { ... } void setY (int y) { ... } int getX() { ... } int getY() { ... }}
aspect ShowAccesses { static before Point.set, Point.setX, Point.setY, Line.set, Line.setX1, Line.setY1, Line.setX2, Line.setY2 { System.out.println(“W”); }}
“access tracing” is a good abstraction on its own
class Line { ... void set(int x1,int y1, int x2,int y2) {...} void setX1 (Point p) {...} void setY1 (Point p) {...} void setX2 (Point p) {...} void setY2 (Point p) {...}}
128
a good aspect (access tracing)class Point { int _x = 0; int _y = 0;
void set (int x, int y) { System.out.println(“W”); _x = x; _y = y;
} void setX (int x) { System.out.println(“W”); _x = x;
} void setY (int y) { System.out.println(“W”); _y = y;
} int getX() { return _x;
} int getY() { return _y;
}}
alternative implementation:
• more tangled
• redundant information
• commonality is lost
• consistent change is harder
• harder to unplug
harder to maintain
129
a good aspect (errors)class DocumentServer { Doc convert(Doc d,Format f){ search locally and remotely } DocID lookupDoc (…) { search locally and remotely } void storeDoc (…) { store locally, notify remotely } void scanDoc (…) { get document from scanner }}
aspect DocumentServerErrors { static catch (RemoteException e) DocumentServer.lookupDoc, DocumentServer.storeDoc { pop up message offering to change the default server } static catch (DeviceError e) DocumentServer.convert, DocumentServer.scanDoc { pop up message offering to call the key operator }}
“how to deal with errors in the document server” is a good abstraction on its own
130
a good aspect (errors)class DocumentServer { Doc convert(Doc d,Format f){ try { search locally and remotely } catch (DeviceError e) { pop up message offering to call the key operator } } DocID lookupDoc (…) { try { search locally and remotely } catch (RemoteException e) { pop up message offering to change the default server } } void storeDoc (…) { try { store locally, notify remotely } catch (RemoteException e) { pop up message offering to change the default server } } ...
alternative implementation:
• more tangled
• redundant information
• commonality is lost
• consistent change is harder
• harder to unplug
harder to maintain
even single-class aspects can help, if they separate out some reasonable design concern
131
two questionable aspects
class Point {}
aspect XPart { static new Point._x = 0; static new int Point.getX() { return _x; } static new void Point.setX(int x) { _x = x; }}aspect YPart { static new int Point._y = 0; static new int Point.getY() { return _y; } static new void Point.setY(int y) { _y = y; }}
points havethese 2 parts,butdoes it reallymake sense toseparate them?
where does set go? other methods that need x and y?
do these 2 aspects improve the code?
132
two questionable aspects
class Point { int _x = 0; int _y = 0; void setX (int x) { _x = x; } void setY (int y) { _y = y; } int getX() { return x; } int getY() { return y; } void set (int x, int y) { _x = x; _y = y; }}
the Point class is a goodimplementation of thepoint abstraction:
• no redundant information• no lost commonalties• no tangling• no difference in size• plug in/out: what’s a point without the X part?
not really
do these 2 aspects improve the code?
133
when to use aspects
• is there a concern that:– cross-cuts the structure of several
classes or methods– is beneficial to separate out
134
… cross-cutting
• a design concern that involves several classes or methods
• implemented without AOP would lead to distant places in the code that– do the same thing
• (i.e. println(“W”), logging)• try grep to find these [Griswald]
– do a coordinated single thing• (i.e. observer pattern, synchronization)• harder to find these
135
… beneficial to separate out
does it improve the code in real ways?– separation of concerns
• think about service without logging
– clarifies interactions, reduces tangling• all the log.writeBytes are really the same• checking that the “other object” isn’t red
– easier to modify / extend• change “W” to “a variable was set”• aspect frameworks
– plug and play• can turn off aspect easily• debugging aspects unplugged but not deleted
136
benefits of using AOP
• good modularity, even in the presence of cross-cutting concerns– less tangled code, more natural code,
smaller code– easier maintenance and evolution
• easier to reason about, debug, change
– more reusable• plug and play
137
one-to-one association trick
obj
aspSomeAspect asp = new SomeAspect();SomeClass obj = new SomeClass();asp.addObject(obj);
aspect SomeAspect { static after SomeClass(*) { SomeAspect asp = new SomeAspect(); asp.addObject(thisObject); } ...}
SomeClass obj = new SomeClass();// aspect instance is associated with obj
make aspect know about class more than the other way
Part VIReferences, Related Work
139
AOP and AspectJ on the web
• http://www.parc.xerox.com/aop
• http://www.parc.xerox.com/aop/aspectj
140
workshops
• ECOOP’97– http://wwwtrese.cs.utwente.nl/aop-ecoop97
• ICSE’98– http://www.parc.xerox.com/aop/icse98
• ECOOP’98– http://wwwtrese.cs.utwente.nl/aop-ecoop98
141
AOP publications
• in proceedings of ECOOP’97• tech reports from PARC
– http://www.parc.xerox.com/aop
• workshop papers– see workshop pages
142
work we know best
• subject-oriented programming @ IBM
– [Ossher, Harrison]
• adaptive programming @ Northeastern U
– [Lieberherr]
• composition filters @ U Twente
– [Aksit]
• assessment of SE techniques @ UBC– [Murphy]
• information transparency @ UCSD– [Griswald]