AspectJ TM programming techniques and use in Eclipse AOSD Seminar presented by Hagai Cibulski.
-
Upload
kerry-webster -
Category
Documents
-
view
221 -
download
0
Transcript of AspectJ TM programming techniques and use in Eclipse AOSD Seminar presented by Hagai Cibulski.
AspectJTM programming techniques and use in Eclipse
AOSD Seminar
presented by
Hagai Cibulski
Agenda
1. Examples of using AspectJ for solving real-world problems
2. AspectJ/Eclipse integration
Part 1: Problem Solving Examples
Design by contract. Flexible access control. Persistence and Dirty Tracking. Resource-pool management. Policy Enforcement. Semantic based crosscutting behavior.
Contract Enforcement Design by contract.
Precondition defines the client's responsibility.
Postcondition is the supplier's part of the contract.
Putting pre- and post-condition assertions directly into application code? Lack of code modularity. Tangled code. Mixes
business-logic code with assertions.
Contract Enforcement - Example
class Point {int _x, _y;void setX(int x) { _x = x; } // precondition: x >= MIN_Xvoid setY(int y) { _y = y; }int getX() { return _x; }int getY() { return _y; }
}
class Client { Client(Point p) {
p.setX(-1); p.setY(50); // postcondition: p.getY() ==
50 }}
Pre-ConditionUsing Before Advice
aspect PointBoundsPreCondition {before(int newX):call(void Point.setX(int)) && args(newX) {
assert(newX >= MIN_X);assert(newX <= MAX_X);
}
before(int newY): call(void Point.setY(int)) && args(newY){assert(newY >= MIN_Y);assert(newY <= MAX_Y);
}private void assert(boolean v) {
if ( !v ) throw new RuntimeException();
}}
Post-ConditionUsing After Advice
aspect PointBoundsPostCondition {after(Point p, int newX) returning:
call(void Point.setX(int)) && target(p) && args(newX) {
assert(p.getX() == newX);}after(Point p, int newY) returning:
call(void Point.setY(int)) && target(p) && args(newY) {
assert(p.getY() == newY);}private void assert(boolean v) {
if ( !v ) throw new RuntimeException();
}}
Flexible Access Control Enforce an access-control crosscutting concern. Java's access controls: public, private, package, and
protected. Not enough control in many cases. Example: Factory design-pattern implementation:
We want only the factory to create the product objects. Other classes should be prohibited from accessing
constructors of any product class. Marking constructors with package access? Only when the factory resides in the same package as the
manufactured classes - inflexible solution!
Flexible Access Control (continued)
C++'s friend mechanism controls access to a class from other specified classes and methods.
With AspectJ we can implement such functionality in Java.
Even more fine-grained and expressive access control!
Flexible Access Control - Example Product class has a constructor and a configure() method. We also declare a nested FlagAccessViolation aspect. Pointcut 1 detects constructor calls not from ProductFactory or its
subclasses. Pointcut 2 detects configure() calls not from ProductConfigurator or its
subclasses. We declare either such violations as compile-time errors.
public class Product {public Product() { /* constructor implementation */ }public void configure() { /* configuration implementation */ }
static aspect FlagAccessViolation { pointcut factoryAccessViolation()
: call(Product.new(..)) && !within(ProductFactory+); pointcut configuratorAccessViolation() : call(* Product.configure(..)) && !within(ProductConfigurator+);
declare error : factoryAccessViolation() ||
configuratorAccessViolation() : "Access control violation"; }}
must be a “static pointcut”
If any of the join points in the pointcut possibly exist in the program, the compiler should emit an error of String.
Flexible Access Control - Example
public class ProductFactory { public Product createProduct() { return new Product(); // ok } public void configureProduct(Product product) { product.configure(); // error }}
The ProductFactory class calls the Product.configure() method.
Compile-time error with a specified "Access control violation" message.
Persistent Objects A ValueObject represents a persistent entity.
A Data Access Object (DAO) accesses the actual Data Store.
DAO getData() and setData() loads and stores the ValueObject.
Interfaces implemented in persistence layer:interface ValueObject {};
interface DAO { public ValueObject getData(); public void setData(ValueObject v);};
Persistence:Data Access Sequence
Business Object
DataAccessObject
Data StoreValue Object
create
getdata fetchdata
create
setproperty
setproperty
setdatagetproperty
getproperty
storedata
Dirty Tracking Persistent Objects
DAO setData() stores the ValueObject.
Let’s store ValueObject only if it’s dirty.
Intercept ADO.setData().
aspect DirtyTracking { public boolean ValueObject.dirty = false;
pointcut dirtyingAction(ValueObject v) : set(* *) && target(v) && !within(DirtyTracking);
after(ValueObject v) returning : execution(*.new(..)) && this(v) { v.dirty = false; }
after(ValueObject v) returning : dirtyingAction(v) { v.dirty = true; }
void around(ValueObject v) : execution(void *.setData(ValueObject)) && this(DAO) && args(v) { if (v.dirty) // Let’s store ValueObject only if it’s dirty { proceed(v);
v.dirty = false; } }}
Dirty Tracking - Implementation
Resource-pool management Optimize resource usage, by recycling previously
created resources. Threads.
Database connections.
But in what phase of development? Up front design of when and how to obtain resources from
the pool and how to put them back. Over design? Complicated system!
Optimization after profiling. Under design? Many modifications!
The architect’s dilemma
Resource-pool management example:
A multi-threaded server
/** a simple TCP/IP service for converting requested strings to uppercase. */
public class UppercaseServer {public static void main(String[] args) throws Exception { int portNum = Integer.parseInt(args[0]); ServerSocket serverSocket = new ServerSocket(portNum);
while (true) {
Socket requestSocket = serverSocket.accept();
/* The server creates a new thread each time a new connection request arrives. */
Thread serverThread = new Thread(
new UppercaseWorker(requestSocket));
serverThread.start();
}
}
}
Thread creation
Thread startJoin points
Resource-pool managementA worker thread
class UppercaseWorker implements Runnable {
private Socket _requestSocket;
public UppercaseWorker(Socket requestSocket) throws IOException {
_requestSocket = requestSocket;
}
public void run() {BufferedReader requestReader = null;Writer responseWriter = null;
try {
requestReader = new BufferedReader(
new InputStreamReader(_requestSocket.getInputStream()));
responseWriter = new OutputStreamWriter(
_requestSocket.getOutputStream());
while (true) {
String requestString = requestReader.readLine();
if (requestString == null) break;
responseWriter.write(requestString.toUpperCase() + "\n");
responseWriter.flush(); } } catch(IOException ex) {} finally {/* cleanup */}
}/* Once a thread completes serving a connection, it terminates. */
Session Join point
Resource-pool managementThread Pooling Aspect
ThreadPooling aspect adds thread pooling to the server.
The thread pool is implemented as a stack. We intercept the creation of new thread objects.
public aspect ThreadPooling {
Stack pool = new Stack();
pointcut threadCreation(Runnable runnable) : call(Thread.new(Runnable)) && args(runnable);
Advise the threadCreation() pointcut to first check the thread pool for available threads.
If a thread is available, reuse it.
If no thread is available, create a new Thread.
Thread around(Runnable runnable) : threadCreation(runnable) { Thread availableThread = null;
if (pool.empty())
availableThread = new Thread(); else
availableThread = (Thread)pool.pop();
availableThread.setRunnable(runnable); return availableThread;}
Resource-pool managementThread Pooling Aspect
Can You see the problem here?
Resource-pool managementThread Pooling Aspect
Pointcut session() captures the run() method's execution of any Thread objects.
pointcut session(Thread thread) : execution(void Thread.run()) && this(thread);
Putting session() inside a while(true) loop, advises session() to never finish servicing.
The result: A thread, once created, never dies.
Once a request is processed, put the thread back into the pool in a waiting state.
void around(Thread thread) : session(thread) { while (true) { proceed(thread); pool.push(thread); synchronized(thread) { try { thread.wait(); } catch(InterruptedException ex) {} } } }
Pointcut threadStart() captures a call to Thread.start()
pointcut threadStart(Thread thread) : call(void Thread.start()) && target(thread);
void around(Thread thread) : threadStart(thread) { if (thread.isAlive()) { // wake it up synchronized(thread) { thread.notifyAll(); } } else { proceed(thread); }}
Resource-pool managementThread Pooling Aspect
isAlive() checks if the thread previously started:Thread obtained from a pool, now in a waiting state.
Wake up the thread by notifying it.
If the thread has not started yet:
Freshly created thread.
proceed with starting the thread.
Resource-pool managementDatabase Connections
Same technique.
Capture joinpoints that create new connections.
Advise them to use one from a connection pool instead, if available.
Capture joinpoints that close connections.
Advise them to put those objects back in the pool.
Policy Enforcement Swing MVC pattern includes unenforced assumptions:
Models let you add a listener object more than once. Duplicate work if an event-notification method carries an expensive operation.
Forgetting to remove listeners before destroying a view.
Memory leak!
Let’s enforce a policy to ensure no duplicate listener objects, and listeners do not loiter around.To implement policy enforcement using OOP, you must use a combination of documentation, code reviews, and so on…
Both concerns requires capturing joinpoints that add listeners to models.
Share the code via a base abstract EventListenerManagement aspect.
addListenerCall() pointcut captures calls to methods adding a listener.
call(void *.add*Listener(EventListener+))
declare precedence : EventListenerWeakening, EventListenerUniqueness;
Policy EnforcementBase aspect: EventListenerManagement
Policy EnforcementUniqueness concern
Abstract method
Problem:
Models let you add a listener object more than once. Duplicate notification.
Solution:
Before adding a listener, check whether it was previously added. If that listener is already present, the operation does not proceed.
Otherwise, add the listener.
EventListenerUniqueness aspect extends EventListenerManagement and ensures no duplicate listener objects in a model. void around(. . .) : addListenerCall(model, listener) {
EventListener[] listeners = getCurrentListeners(model); if (! Utils.isInArray(listeners, listener)) proceed(model, listener); }
Policy EnforcementImplementing Uniqueness concern for tables and lists
The concrete aspect TableModelListenerUniqueness extends EventListenerUniqueness to apply the aspect to TableModel.
Another concrete aspect, ListDataListenerUniqueness, does the same for list-related classes.
Problem: Forgetting to remove listeners before destroying a view.
Memory leak!
Solution:
Instead of adding a listener to a model directly, wrap it as a referent in a WeakReference object and add it.
void around(Object model, EventListener listener) : addListenerCall(model, listener) { proceed(model, getWeakListener(listener)); }
Policy EnforcementNo loitering-views concern
Abstract method
Policy EnforcementImplementing a no loitering-views concern
The RemoveGarbageCollectedListeners aspect removes WeakEventListener from the model when it detects that the referent is garbage collected.
abstract static aspect RemoveGarbageCollectedListeners { pointcut eventNotification(WeakEventListener weakListener, EventObject event) : execution(void WeakEventListener+.*(EventObject+)) && this(weakListener) && args(event) && lexicalScopeMatch();
abstract pointcut lexicalScopeMatch(); public abstract void removeListener(EventObject event, EventListener listener); void around(WeakEventListener weakListener, EventObject event) : eventNotification(weakListener, event) { if (weakListener.getDelegatee() != null) { proceed(weakListener, event); } else { removeListener(event, weakListener);} } }
We check the collected referent in an event notification method. Can You see the problem with this?
Semantic based crosscutting behavior
Problem:
Operations with the same semantic characteristics should typically implement common behaviors.
A wait cursor should be put before any slow method executes.
Authentication before access to all security-critical data.
Since such concerns possess a crosscutting nature, AOP and AspectJ offer mechanisms to modularize them.
Because a method's name might not indicate its characteristics, we need a different mechanism to capture such methods.
Semantic based crosscutting behavior
Solution:
Declare the aspect adding semantic-based crosscutting behavior as an abstract aspect.
In that aspect, declare an abstract pointcut for methods with characteristics under consideration.
Finally, write an advice performing the required implementation.
Semantic based crosscutting behavior
Example: SlowMethodAspect declares the abstract slowMethods() pointcut
and advises it to first put a wait cursor, proceed with the original operation, and finally restore the original cursor.
Semantic based crosscutting behavior
Implementation:public abstract aspect SlowMethodAspect {
abstract pointcut slowMethods(Component uiComp);
void around(Component uiComp) : slowMethods(uiComp) { Cursor originalCursor = uiComp.getCursor(); Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR); uiComp.setCursor(waitCursor);
try { proceed(uiComp); } finally { uiComp.setCursor(originalCursor); } }}
GUIComp1 contains following aspect: public static aspect SlowMethodsParticipant extends SlowMethodAspect {
pointcut slowMethods(Component uiComp) : execution(void GUIComp1.performOperation1()) && this(uiComp);}
The aspected classes include code to participate
in the collaboration
End of part 1-- Problem Solving Examples
Questions?
Part 2: Eclipse Integration
To encourage the growth of the AspectJ technology
and community, PARC transferred AspectJ
to an openly-developed eclipse.org project
in December of 2002.
Part 2: AspectJ/Eclipse Integration
AJDT. Caching example. Build configurations. Aspect visualization. Debugging. Generating documentation.
AJDT
AJDT (AspectJ Development Tools) AspectJ plugin for Eclipse. at http://www.eclipse.org/ajdt
Consistent with JDT (Java Development Tools) AspectJ plugin for Java.
Installing the latest AJDT for Eclipse 3.0: You install AJDT From Eclipse. Help>Software Updates>Find and Install... And so on. http://www-106.ibm.com/developerworks/library/j-ajdt/
First steps: File>New>AspectJ Project File>New>Aspect Just the same as if the project were a Java project
Example – a long calculation(we would like to cache the result)
How many seconds does it take for running this Java application?
package caching;
public class Application{ public static void main(String[] args) { System.out.println("The square of 45 is " + calculateSquare(45)); System.out.println("The square of 64 is " + calculateSquare(64)); System.out.println("The square of 45 is " + calculateSquare(45)); System.out.println("The square of 64 is " + calculateSquare (64)); }
private static int calculateSquare(int number) { try {Thread.sleep(7000);} catch (InterruptedException ie) {} return number * number; }}
Example – Cache aspect Now, We press File>New>Aspect and type the following aspect:
import java.util.Hashtable;public aspect Cache { private Hashtable valueCache = new Hashtable();
pointcut calculate(int i) : args(i) && (execution(int Application.calculateSquare(int)));
int around(int i) : calculate(i) { if (valueCache.containsKey(new Integer(i))) { return ((Integer) valueCache.get(new Integer(i))).intValue(); } int square = proceed(i); valueCache.put(new Integer(i), new Integer(square)); return square; }} Now, How many seconds does it take?
Cache Example – Aspect viewOutline view of the Cache aspect:
around advice node > "advises" node > method node. The outline shows the relationship this construct (the around advice)
has with another construct in the system - the method being advised.
The target of the relationship is a hyperlink. click on the Application.calculateSquare(int) node, and the editor will switch
to it.
Cache Example – Aspected classOutline view of Application class
calculateSquare() method node > "advised by" node > source of advice
in this case, the around advice in the Cache aspect The source of the relationship is a hyperlink.
Cache Example – Advised by menu
Marker shows advice.
Right-click on it.
Popup menu appears.
See Advised by menu.
See appropriate advice.
Select advice to open it.
Build configurationsHow do we selectively add and remove aspects from an application?Build configurations define the source files that are passed to the compiler.
To exclude an aspect, right-click on it and select Exclude.
The icon for Debug.java is hollowed out, indicating its exclusion.
The icon for the spacewar package is partially emptied out, to indicate that some, but not all, of the package has been excluded.
To include an aspect, right-click on it and select Include.
The icons will now fill in, and the project will be built to include this aspect.
Run the program, and you'll see a window, with debugging information.
Build configurations (continued)
Build configurations are stored in .ajproperties files. In the Package Explorer below, one configuration has a triangle in
it, which indicates that it is the active configuration. Any changes made to the configuration using the Include and
Exclude contextual menu options are written to the currently active configuration file.
Active configuration
Aspect Visualization Perspective
Window>Open Perspective... Aspect Visualization Gives an overall feeling for how your application behaves.
How many classes do your aspects affect?
View the crosscutting concerns in your application. The Visualiser is located in the center of the perspective. It represents the classes and aspects
within a project as bars and the places
where aspects affect your code as stripes
on those bars. The lengths of the bars are relative to file size.
Aspect Visualization – The big picture
Visualizing the effects of the debug aspect
The stripe colors match those on the buttons in the Visualiser menu.
Next to each of the colored buttons is a checkbox and a label.
The label gives the name of the aspect that the color represents. The checkbox indicates if this aspect is included in the current
visualization.
You can filter out any aspects you're not interested in.
Just want to see whatyour Debug aspect is affecting?Deselect all but the Debug aspect!
Debugging You can set breakpoints in before and after advice.
Debugging When reaching the breakpoint, the advice is correctly
displayed on the stack.
Generating documentation The javadoc tool Generates API documentation for Java
projects, based on specially formatted comments in the source code.
The ajdoc tool is AspectJ extension to javadoc. Adds details of the
crosscutting nature of your aspects.
The javadoc output
has been enhanced
to show where the
advice takes effect.
End of part 2-- Eclipse integration
Questions?
References
1. Laddad, R. I Want my AOP, part 3. http://www.javaworld.com/javaworld/jw-04-2002/jw-0412-aspect3.html
2. Coyler, A. AspectJ. In Aspect-Oriented Software Development, R. Filman et al., Chapter 6.
3. AspectJ documentation. http://eclipse.org/aspectj
4. Chapman, M., and Hawkins, H. Develop aspect-oriented Java applications with Eclipse and AJDT.http://www-106.ibm.com/developerworks/library/j-ajdt/