Aspect J Java based AOP System Aspect J
Aspect. J - Java based AOP System
Aspect. J - basic features • Compatible extension to Java: – Upward Compatibility - all legal Java programs are legal Aspect. J programs – Platform Compatibility - all legal Aspect. J programs run on all standard Java VM – Tool Compatibility - existing tools can be extended to support Aspect. J in a natural way: IDEs, documentation and design tools – Programmer Compatibility - Programming with Aspect. J must feel like a natural extension to programming with Java
Aspect. J - basic features (contd. ) • Aspect. J program structure: – Classes - like in Java – Aspects - for concerns that crosscut class structure • Aspect. J crosscutting implementation – Dynamic - define additional implementation to run at certain point of program execution – Static - define new operation on existing types (introduction)
Aspect. J - language overview • Basic concepts: – Joinpoint - certain points in a program’s execution (for example - a call to a class’ method – Pointcut - Program constructs to designate joinpoints and to collect specific context at these points – Advice - Code that runs upon meeting certain conditions – Aspect - class like crosscutting unit, where advice and pointcuts form weaving rules
Joinpoints • certain points in a program’s execution – have context associated with them (for example a method call joinpoint can have the target object as a part of the context – in Aspect. J, available joinpoints are limited to the following: • • • Method call and execution Constructor call and execution Read/Write access to field Exception handler execution Object and class Initialization execution
Pointcut designators • Program constructs to designate Joinpoints • set of Joinpoints and (optionally) some values from their execution context • Aspect. J provides primitive pointcut designators which can be used to compose named/anonymous user defined pointcuts
Pointcut designators (contd. ) • primitive pointcut designators: – calls(signature) / receptions(signature) / executions(signature) - matches call/reception/execution join points at which the method/constructor called matches signature; method signature: Result. Type. Name Recvr. Type. Name. meth_id (params) constructor signature: New. Object. Type. Name. new (params) – gets(signature) / gets(signature) [val] / sets(signature) [old. Val] [new. Val] - matches get / set join points where the field accessed matches signature; field signature: Field. Type. Name Object. Type. Name. field_id
Pointcut designators (contd. ) • primitive pointcut designators (contd): – handles(Throwable. Type) - matches exception handler execution join point of the specified type – instanceof(Currently. Executing. Object. Type) “this” is of specified type – within(Class. Name) / withincode(signature) the executing code is contained within Class. Name / within the specified method – cflow(pointcut_designator) - for example <cflow (call (* My. Class. my. Method (. . ))> - all joint points in control flow of call to the method
Pointcut designators (contd. ) • primitive pointcut designators (contd): context collecting pointcuts: – this(object) - all the join points where “this” is the object specified – target(object) - the method is called on specified object – args(arg 1, . . ) - the join points where the arguments are as specified – named / unnamed pointcuts (like named / unnamed classes) – use ||, &&, !, . . , *, + to compose pointcuts
Advices • Associate code with a specific pointcut • Advice types available: before, after (after returning / after throwing), around • the context is passed to advice by the pointcut • example: pointcut move (Line l): receptions (void l. Move. XY (int, int)); after (Line l): move (l) {/*print msg*/}
Advices (contd. ) • Order of advice execution: – any around advise (most specific first); the invocation of proceed () invokes next most specific around advise – any before advice, most specific first – the computation itself – all after advice, less specific first – return value from step (3) is returned to the innermost call to proceed () and that piece of around advice continues running – when the innermost around advice finishes, the surrounding around advice continues running – once the outermost piece of around advice returns, control continues back from the join point
Advices (contd. ) • Aspect precedence (for two pieces of advice a 1, a 2 defined in aspects A 1, A 2 respectively) – if A 1 = A 2, the advice that appears first in the aspect’s declaration body is more specific – A 1 extends A 2 --> a 1 is more specific – declaration of A 1 includes a dominates modifier that mentions A 2 --> a 1 is more specific than a 2
Aspects • Modular units of crosscutting implementation • declaration - similar to class declaration • include pointcut declarations, advice declarations and declarations permitted in class declaration • aspect inheritance, abstract aspects are supported
Example 1 - thread pooling • Thread pooling - more effective and resource saving approach to thread managenment • AOP implementation in a modular fashion source code need not be modified • original code - simple multithreaded Server
The server source code //Uppercase. Server. java import java. io. *; import java. net. *; public class Uppercase. Server { public static void main(String[] args) throws Exception { if (args. length != 1) { System. out. println("Usage: java Uppercase. Server "); System. exit(1); } int port. Num = Integer. parse. Int(args[0]); Server. Socket server. Socket = new Server. Socket(port. Num); while(true) { Socket request. Socket = server. Socket. accept(); Thread server. Thread = new Thread(new Uppercase. Worker(request. Socket)); server. Thread. start; () { { {
class Uppercase. Worker implements Runnable { private Socket _request. Socket; public Uppercase. Worker(Socket request. Socket) throws IOException { System. out. println("Creating new worker"); _request. Socket = request. Socket; } public void run() { Buffered. Reader request. Reader = null; Writer response. Writer = null; try { request. Reader = new Buffered. Reader(new Input. Stream. Reader(_request. Socket. get. Input. Stream())); response. Writer = new Output. Stream. Writer(_request. Socket. get. Output. Stream());
while(true) { String request. String = request. Reader. read. Line(); if (request. String == null) {break; } System. out. println("Got request: " + request. String); response. Writer. write(request. String. to. Upper. Case() + "n"); response. Writer. flush(); } } catch(IOException ex {} ( finally { try { if (response. Writer != null) {response. Writer. close(); } if (request. Reader != null) {request. Reader. close(); } _request. Socket. close(); } catch (IOException ex 2) {} } System. out. println("Ending the session; (" { {
//Thread. Pool. java - simple class that acts like stack for available threads import java. util; *. public class Thread. Pool { List _waiting. Thread = new Vector(); public void put(Delegating. Thread thread) { System. out. println("Putting back: " + thread); _waiting. Thread. add(thread); } public Delegating. Thread get() { if (_waiting. Thread. size() != 0) { Delegating. Thread available. Thread =(Delegating. Thread)_waiting. Thread. remove(0); System. out. println("Providing for work: " + available. Thread); return available. Thread; { return null; } static class Delegating. Thread extends Thread { private Runnable _delegatee; public void set. Delegatee(Runnable delegatee) { _delegatee = delegatee; } public void run() {_delegatee. run{; ()
Adding thread pooling //Thread. Pooling. java public aspect Thread. Pooling { Thread. Pool pool = new Thread. Pool(); //=================================== // Thread creation //=================================== pointcut thread. Creation(Runnable runnable) : call(Thread. new(Runnable)) && args(runnable); Thread around(Runnable runnable) : thread. Creation(runnable) { Thread. Pool. Delegating. Thread available. Thread = pool. get(); If (available. Thread == null) { available. Thread = new Thread. Pool. Delegating. Thread(); } available. Thread. set. Delegatee(runnable); return available. Thread; {
Adding thread pooling (contd. ) ==================================// = //Session //================================== = pointcut session(Thread. Pool. Delegating. Thread thread) : execution(void Thread. Pool. Delegating. Thread. run()) && this(thread); void around(Thread. Pool. Delegating. Thread thread) : session(thread) { while(true) { proceed(thread); pool. put(thread); synchronized(thread) { try { thread. wait(); } catch(Interrupted. Exception ex{} ( { { {
Adding thread pooling (contd. ) ==============================// //Thread start //============================== pointcut thread. Start(Thread. Pool. Delegating. Thread thread) : call(void Thread. start()) && target(thread); void around(Thread thread) : thread. Start(thread) { if (thread. is. Alive()) { // wake it up synchronized(thread) { thread. notify. All(); } } else { proceed(thread); } } }
The implementation in details • thread. Creation() captures the creating a new thread object taking a Runnable object as the argument. • Advise thread. Creation() pointcut to first check the thread pool for available threads. If no thread is available, create a new one. Set the delegatee to the Runnable object passed in and return that object. No proceed() so the actual operation is not executed • session() captures the run() method's execution of any Thread. Pool. Delegating. Thread objects.
The implementation in details (contd. ) • By putting session() inside a while(true) loop, you advise session() to never finish the servicing. That ensures a thread, once created, never dies. Once a request is processed, you put the thread back into thread pool and put the thread into waiting state. • thread. Start() captures a call to the Thread. start() method. It uses is. Alive() to check if the thread previously started (thread obtained from a pool and now in a waiting state) Wake up the thread by notifying it. If the thread had not started yet, proceed with starting the thread.
Example 2 - Enforcement modularization • Implement policy enforcement to ensure no duplicate listener are added to the models, and listeners do not loiter around when the view they represent become usable. • Policy enforcement implementation in more useful and easy way - no documentation, code reviews and so on.
Problems with listeners • let you add a listener object more than once, which leads to duplicate work if an eventnotification method carries an expensive operation. • Easy to forget to remove listeners before destroying a view - cause the listener to consuming memory.
aspect structural view • need to implement 2 concerns: – uniqueness concern – no loitering-views concern
Base aspect: Event. Listener. Management • This aspect contains an add. Listener. Call() pointcut that captures calls to methods adding a listener. // Event. Listener. Management. java import java. util. *; public abstract aspect Event. Listener. Management { pointcut add. Listener. Call(Object model, Event. Listener listener) : call(void *. add*Listener(Event. Listener+)) && target(model) && args(listener) && model. And. Listener. Type. Match(); abstract pointcut model. And. Listener. Type. Match; () {
Implement the uniqueness concern • checks whether that listener was previously added. If that listener is already present, the operation does not proceed; otherwise, it adds the listener • advises the add. Listener. Call() pointcut to check for the listener's uniqueness by looking in a list obtained by invoking get. Current. Listeners(). It proceeds with adding the listener only if the list doesn't include a listener:
Implement the uniqueness concern - contd. • //Event. Listener. Uniqueness. java import java. util. *; import javax. swing. event. *; import javax. swing. table. *; public abstract aspect Event. Listener. Uniqueness extends Event. Listener. Management { void around(Object model, Event. Listener listener) : add. Listener. Call(model, listener} (
Implement the uniqueness concern - contd. • Event. Listener[] listeners = get. Current. Listeners(model); if (!Utils. is. In. Array(listeners, listener)) { System. out. println("Accepting " + listener); proceed(model, listener); } else { System. out. println("Already listening " + listener); } } public abstract Event. Listener[] get. Current. Listeners(Object model); }
Implement the uniqueness concern - contd. • the concrete aspect Table. Model. Listener. Uniqueness extends Event. Listener. Uniqueness to apply the aspect to Table. Model and related classes. It provides an implementation for the model. And. Listener. Type. Match() pointcut to restrict the model type to Abstract. Table. Model and the listener type to Table. Model. Listener.
Implement the uniqueness concern - contd. • //Table. Model. Listener. Uniqueness. java import java. util. Event. Listener; import javax. swing. event. Table. Model. Listener; import javax. swing. table. *; aspect Table. Model. Listener. Uniqueness extends Event. Listener. Uniqueness { pointcut model. And. Listener. Type. Match() : target(Abstract. Table. Model) && args(Table. Model. Listener); public Event. Listener[] get. Current. Listeners(Object model) { return ((Abstract. Table. Model)model). get. Listeners(Table. Model. Listener. class; ( { {
Implement a no loitering-views concern • ensuring that no view loiters after its destruction. • It advises add. Listener. Call() to proceed with the listener obtained by calling the get. Weak. Listener() method.
Implement a no loitering-views concern - contd. • //Event. Listener. Weakening. java import java. lang. ref. *; import java. util. *; import javax. swing. event. *; public abstract aspect Event. Listener. Weakening extends Event. Listener. Management dominates Event. Listener. Uniqueness { void around(Object model, Event. Listener listener) : add. Listener. Call(model, listener) { proceed(model, get. Weak. Listener(listener)); } public abstract Event. Listener get. Weak. Listener(Event. Listener listener; ( {
Implement a no loitering-views concern - contd. • The Table. Model. Listener. Weakening aspect handles table-related listeners. It uses a specialized Weak. Event. Listener that implements Table. Model. Listener by delegating to the referent object.
Implement a no loitering-views concern - contd. • //Table. Model. Listener. Weakening. java import java. util. *; import javax. swing. event. *; import javax. swing. table. *; public aspect Table. Model. Listener. Weakening extends Event. Listener. Weakening { pointcut model. And. Listener. Type. Match() : target(Abstract. Table. Model) && args(Table. Model. Listener); public Event. Listener get. Weak. Listener(Event. Listener listener) { System. out. println("Weakening " + listener); return new Weak. Table. Model. Listener((Table. Model. Listener)listener; ( { {
Implement a no loitering-views concern - contd. • public class Weak. Table. Model. Listener extends Weak. Event. Listener implements Table. Model. Listener { public Weak. Table. Model. Listener(Table. Model. Listener delegatee) { super(delegatee); } public void table. Changed(Table. Model. Event e) { Table. Model. Listener listener = (Table. Model. Listener)get. Delegatee(); listener. table. Changed(e); }
Implement a no loitering-views concern - contd. • static aspect Table. Remove. Garbage. Collected. Listeners extends Weak. Event. Listener. Remove. Garbage. Collected. Listeners { pointcut lexical. Scope. Match() : within(Weak. Table. Model. Listener); public void remove. Listener(Event. Object event, Event. Listener listener) { ((Table. Model)event. get. Source()). remove. Table. Model. Listener((Table. Model. Listener)listener); } } }
Example 3: Characteristicbased implementation modularization • Operations with the same characteristics should typically implement common behaviors. for example you may need to authenticate access to all security-critical data.
Characteristic-based implementation modularization • declare the aspect adding characteristicbased crosscutting behavior as an abstract aspect. • declare an abstract pointcut for methods with characteristics under consideration. • write an advice performing the required implementation.
Slow. Method. Aspect's implementation • abstract slow. Methods() pointcut and advises it to first put a wait cursor, proceed with the original operation, and finally restore the original cursor.
Slow. Method. Aspect's implementation - contd. • //Slow. Method. Aspect. java import java. util. *; import java. awt. event. *; public abstract aspect Slow. Method. Aspect { abstract pointcut slow. Methods(Component ui. Comp); void around(Component ui. Comp) : slow. Methods(ui. Comp) { Cursor original. Cursor = ui. Comp. get. Cursor(); Cursor wait. Cursor = Cursor. get. Predefined. Cursor(Cursor. WAIT_CURSOR); ui. Comp. set. Cursor(wait. Cursor; (
Slow. Method. Aspect's implementation - contd. • try { proceed(ui. Comp); } finally { ui. Comp. set. Cursor(original. Cursor); } } }
Slow. Method. Aspect's implementation - contd. • Two test components, GUIComp 1 and GUIComp 2, nest a concrete implementation of the aspect. public static aspect Slow. Methods. Participant extends Slow. Method. Aspect { pointcut slow. Methods(Component ui. Comp) : execution(void GUIComp 1. perform. Operation 1()) && this(ui. Comp); }
Slow. Method. Aspect's implementation - contd. public static aspect Slow. Methods. Participant extends Slow. Method. Aspect { pointcut slow. Methods(Component ui. Comp) : (execution(void GUIComp 2. perform. Operation 1()) || execution(void GUIComp 2. perform. Operation 2())) && this(ui. Comp); }
- Slides: 45