450 likes | 678 Views
Overview of AspectJ. A first language for Aspect-Oriented Programming Based on slides by Oren Mishali. Aspect Oriented Programming A reminder …. During the life cycle of a software system, we encounter crosscutting concerns that span multiple modules. e.g. tracing, caching ….
E N D
Overview of AspectJ A first language for Aspect-Oriented Programming Based on slides by Oren Mishali
Aspect Oriented ProgrammingA reminder… • During the life cycle of a software system, we encounter crosscutting concerns that span multiple modules. • e.g. tracing, caching … • Attempt to implement them in a traditional way (OOP) leads to scattering & tangling. • AOP suggests the separation of each concern into unit of modularization –aspect.
Pointcuts and Advice • Most of AspectJ gives keywords for defining joinpoints within pointcuts (Where and When) • Advice (What) is slightly extended Java • Pointcuts are also used to expose context from the joinpoint environment to the advice code
A simple banking system Client * Clerk << uses >> * Account - balance:float +deposit(float) +withdraw(float) • While developing , we want to trace each public method call. • Solution without AspectJ: • Look for public method calls and insert printing before the call.
AspectJ solution PublicTracing.java aspect PublicTracing { pointcut publicCalls() : call ( public *.* (..) ); before() : publicCalls() { System.out.println(“Calling public method”); } } • ajc *.java • java BankingSystem Calling public method Calling public method …………………
AspectJ • General-purpose, aspect oriented extension to Java. • Every valid Java program is also a valid AspectJ program. • With AspectJ: • You implement the core concerns using Java. • You use the extensions provided to implement the aspects. • You weave them together with ajc (no need for javac). • New versions are frequent (now 1.5.2)
Dynamic crosscutting • AspectJ enables us to define events in the program flow and then add an advice at those events. • This capability is called dynamic crosscutting • Keywords: join point, pointcut, advice. • Is it possible to add an advice before the execution of a while block ? • Or before the execution of a method?
Join points • Join point is an event in the execution of a program. • With well defined start point and end point. e.g. execution of a for block, method call, static initialization of a class, execution of a catch block … • Exposed join points are subset of all possible join points that AspectJ exposes to us. • In practice this means that we can only add advice at exposed join points. • So what are they?
Exposed join points in AspectJMethod call & Method execution acount.deposit(amount) :Client account:Account deposit(amount) deposit execution deposit call • The most useful join points. • For most purposes, the difference does not matter. • What is the difference?
Exposed join points in AspectJAll the rest… • Constructor call & execution • Field read & write access ( Except for local or const ) • Execution of a catch block • Static initialization of a class • Object initialization & pre-initialization • Execution of an advice ( Advise an advice ? ) Is it possible to add an advice before the execution of a while block ?
Pointcuts • Join point is a concept. • Pointcut is a syntactic construct that defines a set of join points. • Suppose we want to trace each execution of a catch block that handles exceptions of type AccountException: before() : handler(AccountException) { System.out.println(“Before execution of catch block”); } • Or: pointcutaccountExceptionHandler(): handler(AccountException); before() : accountExceptionHandler() { System.out.println(“Before execution of catch block”); } Anonymous pointcut Named pointcut
Pointcuts (cont.) • In same manner, a pointcut syntax exists for each kind of join point. • E.g. call( pattern ), execution( pattern ), set( pattern ) … • Use * , + , .. wildcards to define a common pattern. • Use operators to create complex pointcuts: • !p- each join point that is not defined by pointcut p. • p1||p2–each join point that is defined by either p1 or p2. • p1&&p2–each join point that is defined by bothp1 and p2. • E.g. set(private float Account.balance) && get(private float Account.balance) set(private float Account.balance) || get(private float Account.balance)
More primitive pointcuts… • Some join points have arguments: • Method and constructor join points. • Execution of a catch block ( handled exception ). • Field write access ( new value to be set ). • Suppose we want to trace each join point that has one argument of type String. • We can do that using args( pattern ) primitive pointcut: before() : args( String ) { System.out.println( “Before one String parameter”); }
More primitive pointcuts… • Suppose we want to trace each call to Account.deposit(float) made by a Clerk objectbut not by a Client object. • call( public void Account.deposit(float) ) ? • this( Type ) pointcut defines all join points where this-object is subtype of Type including Type itself. before() : call( public void Account.deposit(float) ) && this ( Clerk ) { System.out.println( “Clerk object calls Account.deposit()” ); } • target( Type ) is similar but based on the target object. call( * Account+.*(..) ) ==target( Account ) ?
Scoping pointcuts • adviceexecution() –all joinpoints in advice • within(TypePattern) –all joinpoints in code defined in the Type (class or aspect) • withincode(MethodPattern) –every joinpoint within methods with MethodPattern signature
Passing context from join point to advice • Advice often needs some information about the advised join point. (context) • E.g. advice on a method call join point wants to print the caller object (this), the called object (target), or the method arguments. • A pointcut may expose an argument/this-object/target-object from a join point. pointcut withdrawOperations(Client client, Account account, float amount) : call( public void Account.withdraw( float ) ) && this( client ) && target( account ) && args( amount ) ;
Advicebefore() advice • Logic - permit withdraw only if account.balance >= amount. before( Client client, Account account, float amount ) throws AccountException : withdrawOperations( client, account, amount ) { if ( account.balance() < amount ) { client.send(“Please contact you bank office”); account.block(true); throw new AccountException(); } } Note parameters of the before that link pointcut arguments with the advice
Advicearound() advice • Logic – if amount > account.balance, withdraw balance. void around( Client client, Account account, float amount ) throws AccountException: withdrawOperations( client, account, amount) { if ( amount < account.balance() ) proceed( client, account, amount); else proceed( client, account, account.balance() ); } • around() advice executes instead of the join point. • You must specify a return value.
Adviceafter() advice • Several kinds of after() advice: • after returning()– executes only after a successful return. • after throwing()– executes only after thrown exception. • after()– executes after both cases. • We can access the return value / exception : after() returning(float amount) : call( Account.balance(..) ) { Logger.log(amount); }
Aspect • Is the basic unit of modularization for crosscutting concern in AspectJ. • Can includes advice, pointcuts, introductions and compile-time declarations. • An aspect is like a class: • Can include data members and methods. • Can have access specifier. • Can be declared as abstract. • Can be embedded inside classes and interfaces. • An aspect is not a class: • Cannot be directly instantiated with new. • Instantiation is done by the system. Singleton by default. • Cannot inherit from concrete aspects. • Can have a privileged access specifier. • Gives it access to the private member of the class it is crosscutting.
Static crosscutting • Modifies the static structure of the program or its compile-time behavior. • Member introduction. • Adding class fields or methods. • Type-hierarchy modification. • Adding class interfaces or a super class. • Compile-time errors and warnings. • Based on certain usage patterns.
Static crosscuttingMember introduction • Suppose we want to allow deposit only if it is higher than last deposit. • We need lastDeposit field. Where? aspect DepositAspect { private float Account.lastDeposit = 0;// private to the aspect, not to Account. void around( Account account, float amount ) : execution ( void Account.deposit(float) ) && args( amount ) &&this(account) { if ( amount > account.lastDeposit ) { proceed( account, amount ); account.lastDeposit = amount; } } }
More on AspectJ:cflow() and cflowbelow() pointcuts Account.withdraw( amount ) • The program fails in the marked area. • should we trace using execution(Database.confirm(float)) ? • cflow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut including the pointcut join points. • cflowbelow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut excluding the pointcut join points. :Client :Account :Database withdraw(amount) confirm(amount)
More on AspectJ:clflow() & cflowbelow() pointcuts Account.withdraw( amount ) • The program fails in the marked area. • should we trace using execution(Database.confirm(float)) ? • cflow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut including the pointcut join points. • cflowbelow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut excluding the pointcut join points. :Client :Account :Database withdraw(amount) confirm(amount) • cflow( call(Account.withdraw(float)) && this(Client) )
More on AspectJ:clflow() & cflowbelow() pointcuts Account.withdraw( amount ) • The program fails in the marked area. • should we trace using execution(Database.confirm(float)) ? • cflow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut including the pointcut join points. • cflowbelow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut excluding the pointcut join points. :Client :Account :Database withdraw(amount) confirm(amount) • cflow( call(Account.withdraw(float)) && this(Client) ) && execution( Database.confirm(float) )
More on AspectJ: clflow() & cflowbelow() pointcuts Account.withdraw( amount ) • The program fails in the marked area. • should we trace using execution(Database.confirm(float)) ? • cflow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut including the pointcut join points. • cflowbelow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut excluding the pointcut join points. :Client :Account :Database withdraw(amount) confirm(amount) • cflow( call(Account.withdraw(float)) && this(Client) ) && execution( Database.confirm(float) ) • cflowbelow( call(Account.withdraw(float)) && this(Client) )
More on AspectJ clflow() & cflowbelow() pointcuts Account.withdraw( amount ) • The program fails in the marked area. • should we trace using execution(Database.confirm(float)) ? • cflow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut including the pointcut join points. • cflowbelow( pointcut ) Defines all join points that occur in the flow of the join points defined by pointcut excluding the pointcut join points. :Client :Account :Database withdraw(amount) confirm(amount) • cflow( call(Account.withdraw(float)) && this(Client) ) && execution( Database.confirm(float) ) • cflowbelow( call(Account.withdraw(float)) && this(Client) ) && execution(Database.confirm(float))
More on AspectJthisJoinPoint • Special reference variable that contains static and dynamic information of a join point. • May be used by an advice. • Usage involves run-time creation overhead. • Use thisJoinPointStaticPart when only static information is necessary. • When possible, prefer usage of args() this() and target() to collect dynamic information.
More on AspectJAdvice precedence • Often, several advices may advise the same join point in the same place. • What is the order of execution? • Precedence of such advices in a single aspect is according to their lexical appearance. • declare precedence construct should be used when order of execution is important among different aspects. • declare precedence : FirstAspect , SecondAspect; specifies that advices of FirstAspect take higher precedence.
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
Contract Enforcement - Example class Point { int _x, _y; void setX(int x) { _x = x; } // precondition: x >= MIN_X void 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); } privatevoid 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 theprogram, 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.
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