Lecture 21: Crosscutting AspectOriented Programming "...feels like I've died and gone to Java Heaven." Don Asumu, Planning Tools Technical Development Leader, BT Laboratories on StructureBuilder, commercial tool based on Aspect-Oriented Programming, at http://www.webgain.com/Products/Structure_Builder/press_reviews.htm CS655: Programming Languages David Evans University of Virginia http://www.cs.virginia.edu/~evans Computer Science Menu • Self-Application Answer • Sorting in Linda • Aspect-Oriented Programming “Separation of Concerns” • Examples – D (Distributed Language) – AspectJ – Adaptive Programming 27 July 2016 University of Virginia CS 655 2 Programming Paradigms • All the languages we have seen so far are “Generalized Procedure” languages: – Programs organized into procedures – Procedures may be grouped and associated with data • Some programs have properties (“aspects”) that cross-cut procedures – Examples: communication, synchronization, handling interrupts, security, loop fusion, etc. 27 July 2016 University of Virginia CS 655 3 Aspect-Oriented Programming Components (Functional Units) Aspect Weaver Procedural Program Aspects (Non-functional Units) Compiler Executable 27 July 2016 University of Virginia CS 655 4 Slide adapted from Gregor Kiczales ICSE Workshop Talk AO programming vs. idioms class Book { private String title; private String author; private String isbn; private PostScript ps; private User borrower; public class PrinterImpl { String status = “Idle” public Book(String t, String a, String i, PostScript p) { Vector jobs; title = t; public PrinterImpl() {} author = a; pubilc get_status() { return status } isbn = i; public add_job(int j) { ps = p; jobs.add(j); } } } public User get_borrower() {return borrower;} public void set_borrower(User u) {borrower = u;} public PostScript get_ps() { return ps; } class User { } class Library { private String name; Hashtable books; Library theLibrary; Library(){ Printer the; Printer books = new Hashtable(100); } public User(String n) { name = n; } public Book getBook(User u, String title) { System.out.println("REQUEST TO GET BOOK " + title); public boolean getBook (String title) { if(books.containsKey(title)) { Book aBook = theLibrary.getBook(this, title); Book b = (Book)books.get(title); thePrinter.print(this,aBook); System.out.println("getBook: Found it:" + b); return true; if (b != null) { } if (b.get_borrower() == null) } b.set_borrower(u); return b; } } return null; } portal Library { portal Printer { } Book find (String title){ void print(Book book) { return: book: Book: {direct pages;} Book: {copy title, author, isbn;} } } } class Book { private BookID id; private PostScript ps; private UserID borrower; class User { private UserID id; Library theLibrary; Printer thePrinter; public Book(String t, String a, String i, PostScript p) { id = new BookID(t,a,i); ps = p; } 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; } 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; } Classes } class BookID { private String title; private String author; private String isbn; Aspect Weaver public BookID(String t, String a, String i) { title = t; author = a; isbn = i; } public String get_title() {return title;} class UserID { private String name; public UserID(String n) { name = n; } public String get_name() { return name; } } } interface PrinterInterface extends Remote { public boolean print (UserID u, BookID b) throws RemoteException; } interface LibraryInterface extends Remote { public BookID getBook(UserID u, String title) throws RemoteException; public PostScript getBookPS(BookID bid) 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; } Aspects } } 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; } { } class Book { private BookID id; private PostScript ps; private UserID borrower; class User { private UserID id; Library theLibrary; Printer thePrinter; public Book(String t, String a, String i, PostScript p) { id = new BookID(t,a,i); ps = p; } public User(String n) { id = new UserID(n); } public boolean getBook (String title) { BookID aBook=null; try{ aBook = theLibrary.getBook(id, title); UserID get_borrower() {return borrower;} } catch (RemoteException e) {} void set_borrower(UserID u) {borrower = u;} try { PostScript get_ps() { return ps; } thePrinter.print(id, aBook); BookID get_bid() { return id; } } catch (RemoteException e) {} } return true; } class BookID { public UserID get_uid() { return id; } private String title; } private String author; private String isbn; class UserID { private String name; public BookID(String t, String a, String i) { interface PrinterInterface extends Remote { interface LibraryInterface extends Remote { title = t; public UserID(String n) { name = n; } public boolean print (UserID u, BookID b) public BookID getBook(UserID u, String title) throws RemoteException; author = a; public String get_name() { return name; } throws RemoteException; isbn = i; } } public PostScript getBookPS(BookID bid) throws RemoteException; } } public String get_title() {return title;} public class Printer extends UnicastRemoteObject } implements PrinterInterface class { Library extends UnicastRemoteObject implements LibraryInterface { private Vector jobs = new Vector(10, 10); Hashtable books; private Library theLibrary; Library() throws RemoteException { books = new Hashtable(100); public Printer() throws RemoteException{} } public boolean print (UserID u, BookID b) public BookID getBook(UserID u, String title) throws RemoteException{ throws RemoteException { PostScript ps=null; System.out.println("REQUEST TO GET BOOK " + title); try{ if(books.containsKey(title)) { ps = theLibrary.getBookPS(b); Book b = (Book)books.get(title); } catch (RemoteException e) {} System.out.println("getBook: Found it:" + b); Job newJob = new Job (ps, u); if (b != null) { return queue(newJob); if (b.get_borrower() == null) } b.set_borrower(u); boolean queue(Job j) { return b.get_bid(); //... } return true; } } 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; } public public public public 27 July 2016 University of Virginia CS 655 5 } Join Points • Aspects defined at “Join Points” – Typically, method invocations, entry and exits – Not limited to that – defined by aspect language • Aspect languages are domain specific – Define code or transformations to occur at join points 27 July 2016 University of Virginia CS 655 6 What is an aspect? • Definition 1: – A modular unit that has no stand-alone function • That is, it requires a context in order to function – And, that effects other modular units • Definition 2: – Something that implements properties that are not well-captured by functional units 27 July 2016 University of Virginia CS 655 7 D • Jcore – component language (Java subset, removed synchronization) • Cool – aspect language for expressing coordination of threads • Ridl – aspect language for expressing remote access strategies • Join Points – method invocations – Weaver just needs to put code at method entrances and exits – Can produce Java source code to compile normally 27 July 2016 University of Virginia CS 655 8 Cool • Define coordinators that specify locking semantics: – selfexclusive (method executed by at most one thread at a time) – mutexclusive (methods in same set cannot be executed concurrently) – Guarded suspension – define monitor-like abstractions separate from implementations • Can access, but not modify instance variables 27 July 2016 University of Virginia CS 655 9 Cool Example coordinator BoundedStackCoord : BoundedStack { selfexclusive { pop, push }; mutexclusive { pop, push }; cond boolean full = false; cond boolean empty = true; push : requires !full; on_exit { empty = false; if (sp == MAX) full = true; } pop : requires !empty; on_exit { full = false; if (sp == 0) empty = true; } } } Ties aspect to specific implementation! 27 July 2016 University of Virginia CS 655 10 Ridl • Control remote method invocations – Gives programmer control over copying semantics in parameter passing (can elect to copy just parts of object graph) remote Printer { Static print (Document: copy *.int, *.String, documentRepository)); ... Only copy int and String fields, and } reference to documentRepository. 27 July 2016 University of Virginia CS 655 11 AspectJ • crosscuts identify events – Can be associated with method calls, exception handlers • advice defines code associated with crosscuts 27 July 2016 University of Virginia CS 655 12 Tangled Example: Java API package java.io; public class File implements java.io.Serializable { private String path; ... public boolean exists() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } return exists0(); } public boolean canRead() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } return canRead0(); } Exact same code, repeated 8 times in java.io.File Why not make it a procedure? • Performance (method call overhead) • Better error messages (?) 27 July 2016 University of Virginia CS 655 13 Cross-cuts • crosscut name(context): event list crosscut fileReaders(java.io.File f): java.io.File & (boolean exists() | boolean canRead() | boolean canRead() | boolean isDirectory() | long lastModified() | long length() | String[] list() | String[] list(FilenameFilter filter)) • Wildcards crosscut nonStatics (): java.io.* & !static * (..); crosscut intMethods (): MyClass & public * (int); crosscut returnObject (): * & Object * (..); crosscut ioHandlers (): Account & * (..) & catch (IOException); 27 July 2016 University of Virginia CS 655 14 Advice • advice (Parameters) : crosscut advice checkRead (java.io.File f): fileReaders (f) { before { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead (path); } } } 27 July 2016 University of Virginia CS 655 15 AspectJ File Implementation • Aspects shown on previous slides • Simple implementation with security code removed package java.io; public class File implements java.io.Serializable { private String path; ... public boolean exists() { return exists0(); } public boolean canRead() { return canRead0(); } 27 July 2016 University of Virginia CS 655 16 Is AspectJ version better? • Advantages – Compact program – Separate concerns without any performance cost • Easier to change and reuse • Easier to understand and analyze policy • Disadvantages – Have to learn a new language – Extra compilation step – Debugging may be difficult (but special-purpose tools) 27 July 2016 University of Virginia CS 655 17 Example: Exception Handling • [Lippert & Lopes, 99] • Exception handling code clutters (and often dwarves) normal code • Tangled exceptions limit reuse – Cannot change exceptional behavior without overriding entire method • Tangled exceptions lose abstraction – All methods must handle network failures by retrying is scattered throughout code (and easy to forget) 27 July 2016 University of Virginia CS 655 18 JWAM Experiment • Java framework • Designed around design-by-contract with explicit calls to Contract.require and Contract.ensure • Use AspectJ to abstract out crosscut for contracts and exception handling • 11% of original code dealt with exception detection and handling; many handlers do the same thing 27 July 2016 University of Virginia CS 655 19 Bank Account public class Account { public Account (String owner, int accNo) { Contract.require (owner != null && owner.length() > 0); Contract.require (accNo > 0); this.owner = owner; this.accNo = accNo; } public void deposit (float amount) { Contract.require (amount > 0.0); balance += amount; } ... } 27 July 2016 University of Virginia CS 655 20 Aspect-Oriented Implementation class AccountContract { static advice Account & new(String s, int n) { before { Contract.require (s != null && s.length () > 0); Contract.require (n > 0); } } static advice Account & deposit (float f) { before { Contract.require (f > 0.0); } } } 27 July 2016 University of Virginia CS 655 21 Handling Library Exceptions • Typical code: try { registry.put (name, this); } catch (RemoteException e) { ErrorLog.print (“Remote call failed: ” + e); } • Replace with: crosscut remoteHandlers () : * & * *(..) & catch (RemoteException); static advice remoteHandlers () { catch (RemoteException e) { ErrorLog.print (“Remote call failed in ” + thisMethodName + “:” + e); } 27 July 2016 University of Virginia CS 655 22 Results • Reduced exception detection and handling code by a factor of 4.5 • Removed many simple conditions (null tests of results and arguments in contracts), common exception handlers • Is resulting code likely to be more reliable and maintainable? 27 July 2016 University of Virginia CS 655 23 Adaptive Programming: Demeter • Instance of AOP [Lieberherr92] • Aspects are traversal strategies • Separate the program text and the class structure – Program is independent of class graph • Accomplish tasks by traversals – Specification for what parts of received object should be traversed – Code fragments for what to execute when specific object types are encountered 27 July 2016 University of Virginia CS 655 24 Law of Demeter • Law of Demeter: a method should talk only to its friends: – arguments and part objects (computed or stored) – newly created objects • Dilemma: – Small method problem of OO (if followed) – Unmaintainable code (if not followed) • Traversal strategies are the solution to this dilemma • Demeter = Greek Goddess of Agriculture (grow software from small blocks) 27 July 2016 University of Virginia CS 655 25 Slide adapted from Karl Lieberherr talk AP Example: UML Class Diagram busStops BusRoute BusStopList buses 0..* BusStop BusList 0..* waiting passengers Bus PersonList Person 7/27/2016 AOP/Demeter 0..* 26 Slide adapted from Karl Lieberherr talk Collaborating Classes Find all persons waiting at any bus stop on a bus route busStops BusRoute buses BusList 0..* BusStopList OO solution: one method for each red class BusStop waiting passengers Bus PersonList Person 7/27/2016 0..* AOP/Demeter 0..* 27 Java Solution (excerpt) class BusRoute { BusStopList busstops; void printWaitingPassengers () { busstops->printWaitingPassengers (); } } class BusStopList { BusStop stops[]; void printWaitingPassengers () { for (int i = 0; i < stops.length; i++) stops[i].printWaitingPassengers (); } } 27 July 2016 University of Virginia CS 655 28 Java Solution (cont.) class BusStop { PersonList waiting; void printWaitingPassengers () { waiting.print (); } } class PersonList { Person people[]; void print () { for (int i = 0; i < people.length; i++) people[i].print (); } } class Person { String name; void print () { System.stdout.println (name); } } 27 July 2016 University of Virginia CS 655 29 Demeter Approach • Devise a traversal strategy • Specify code for different types of objects reached on a traversal • Example: code prints name if object is a Person • Independent of class graph 27 July 2016 University of Virginia CS 655 30 Slide adapted from Karl Lieberherr talk Traversal Strategy First try: from BusRoute to Person busStops BusRoute BusStopList buses 0..* BusStop BusList 0..* waiting passengers Bus PersonList Person 7/27/2016 AOP/Demeter 0..* 31 Slide adapted from Karl Lieberherr talk Traversal Strategy from BusRoute through BusStop to Person busStops BusRoute BusStopList buses 0..* BusStop BusList 0..* waiting passengers Bus PersonList Person 7/27/2016 AOP/Demeter 0..* 32 Slide adapted from Karl Lieberherr talk Writing Adaptive Programs with Strategies strategy: from BusRoute through BusStop to Person BusRoute { traversal waitingPersons(PersonVisitor) { through BusStop to Person; } // from is implicit void printWaitingPersons() // traversal/visitor weaving instr. = waitingPersons(PrintPersonVisitor); PrintPersonVisitor { before Person () < do printing >} 7/27/2016 AOP/Demeter 33 Slide adapted from Karl Lieberherr talk Robustness of Strategy from BusRoute bypassing Bus to Person BusRoute buses BusList 0..* villages VillageList 0..* Village busStops 0..* BusStop waiting passengers Bus PersonList Person 7/27/2016 BusStopList AOP/Demeter 0..* 34 Slide adapted from Karl Lieberherr talk Filter out noise in class diagram •only three out of seven classes are mentioned in traversal strategy! from BusRoute through BusStop to Person replaces traversal methods for the classes BusRoute VillageList Village BusStopList BusStop PersonList Person 7/27/2016 AOP/Demeter 35 Summary • Aspect-Oriented Programming and Adaptive Programming provide programmers with new expressive options • Active research area (Separation of Concerns Workshops at OOPSLA, ICSE, ECOOP, etc.) – Many directions to explore • Practical use still remains to be seen – Kiczales paper claims like OO in 1980 (by induction) • But some commercial success – Tendril Software (Adaptive programming tools for Java), bought by BEA Systems. http://www.webgain.com/Products/Structure_Builder/ 27 July 2016 University of Virginia CS 655 36 Charge • Next time: John Thornley • Project Progress – If your implementation will not be complete by Monday morning, schedule a meeting with me now 27 July 2016 University of Virginia CS 655 37