Introducing AS3Commons AOP

ActionScript, AOP, AS3Commons, Flex, Java Add comments

The AS3Commons AOP library offers a core API with functionality to implement Aspect Oriented Programming (AOP) concepts in your application or framework. The library uses standard API terminology so that developers who are already familiar with AOP should feel immediately comfortable.

What is AOP?

For those new to AOP, here is a short explanation of the general idea. (If you are already familiar with AOP concepts, you might want to skip this part and go directly to the examples).

The basic idea behind AOP is that an application can be broken down into functional parts that can be conditionally applied to existing code. A classic example is logging, where debug information about certain methods is written to the console in order to get runtime information about the execution of the code. One approach might be to add log statements at the beginning and the end of the methods that you would like to debug. This is quite a tedious task as you need to manually add code to several places. If you ever wanted to get rid of the log statements, you would have to remove the log statements again. With AOP, you can write the logging code in a separate class and then use that class as an Advice on the class that you want to debug. You can then easily reuse the logging code on other methods of other classes.

Terminology

  • Aspect: the general concept of a cross-cutting concern. Logging, Error Handling, Method Run-Time Monitoring are useful examples.
  • Advice: an extra piece of code that needs to be applied. E.g. writing a log statement to the console.
  • Pointcut: a rule about when an Advice should be applied. For instance: all public methods of a class, all methods starting with "load", all methods that take a String parameter, ...
  • Advisor: the combination of an Advice and a Pointcut. This term is not general AOP vocabulary. It was introduced in the Spring AOP framework and is also used in AS3Commons AOP.
  • Joinpoint: a single point in the execution of the code where Advice is applied because the rule of a Pointcut has been satisfied.

Types of Advice

Advice comes in different forms, depending on what and when you want to intercept. The general types are listed below and can be applied to a Constructor, a Method or an Accessor (Getter/Setter):

  • Before: executes before the invocation
  • After: executes after the invocation
  • After Throwing: executes after the invocation threw an error
  • After Returning: executes after normal invocation (when no error was thrown)
  • Around: combines all of the above

The AS3Commons AOP library contains interfaces for all of these kinds of advice. This makes it fairly easy to implement specific advice. Interceptors can also be created that give the developer more control over the invocation than the basic advice interfaces.

How does this work?

Applying Advice to existing code is done at run-time. For this, we create a typed-proxy (also at run-time) of a class or instance that is basically a decorator (or wrapper) around the original class or object that we created. A client then works with the proxy object instead of the original object, which is completely transparent to the caller. Calls to the proxy object are then redirected to the inner (original) object during which extra logic can be executed.

Note: Creating typed-proxies at run-time is made possible by the AS3Commons-Bytecode library.

Example

Consider the following MessageWriter class, which takes an optional message as a constructor argument and has a method to write the message to the console.

Actionscript:
  1. public class MessageWriter {
  2.  
  3.   private var _message:String;
  4.  
  5.   public function MessageWriter(message:String = "") {
  6.     _message = message;
  7.   }
  8.  
  9.   public function writeMessage():void {
  10.     trace("Message: '" + _message + "'");
  11.   }
  12.  
  13. }

We can now create an advice that writes an extra message to the console whenever the writeMessage() method is called. This type of advice is "method before" advice, for which the IMethodBeforeAdvice interface can be implemented.

Actionscript:
  1. public class MethodTracerAdvice implements IMethodBeforeAdvice {
  2.  
  3.   public function MethodTracer() {
  4.   }
  5.  
  6.   public function beforeMethod(method:Method, args:Array, target:*):void {
  7.     trace("* before '" + method.name + "'");
  8.   }
  9.  
  10. }

We can now create a simple application where a proxy is created for the MessageWriter class and the MethodTracerAdvice is applied.

Actionscript:
  1. var factory:AOPProxyFactory = new AOPProxyFactory();   
  2. factory.target = MessageWriter;
  3. factory.addAdvice(new MethodTracerAdvice());
  4.  
  5. var handler:Function = function(event:OperationEvent):void {
  6.   var messageWriter:MessageWriter = factory.getProxy(["Hello World!"]);
  7.   messageWriter.writeMessage();
  8. };
  9.  
  10. var operation:IOperation = factory.load();
  11. operation.addCompleteListener(handler);

The console output would be as follows:

Actionscript:
  1. * before 'writeMessage'
  2. Message: 'Hello World!'

Notice the following things:

  • The target of the AOPProxyFactory is a class. For now, only classes can be proxied but it will also be possible to proxy existing instances.
  • The MethodTracerAdvice class implements the IMethodBeforeAdvice interface. This allows the framework to know when the advice must be executed, namely before the method invocation.
  • There is an asynchronous step in loading the proxy factory, after which the proxy can be requested. If you want to create proxies for more classes, consider using the AOPBatchProxyFactory instead. Note that even with the AOPProxyFactory, you can get multiple instances of a proxy.
  • You can pass constructor arguments to the proxy factory that will be used when creating the proxy. Although not shown in this example, it is possible to intercept and change the constructor arguments using an IConstructorBeforeAdvice.

Example with Pointcut

The previous example applies the "method before" advice on every method call. If we would add another method to the MessageWriter class, say "writeAnotherMessage", then the advice would also be invoked on that method. To control when this advice is applied, we can specify a Pointcut that will instruct the framework about when to apply the advice.

Here is the modified MessageWriter class and example.

Actionscript:
  1. public class MessageWriter {
  2.  
  3.   private var _message:String;
  4.  
  5.   public function MessageWriter(message:String = "") {
  6.     _message = message;
  7.   }
  8.  
  9.   public function writeMessage():void {
  10.     trace("Message: '" + _message + "'");
  11.   }
  12.  
  13.   public function writeAnotherMessage():void {
  14.     trace("Message: 'Konnichiwa!'");
  15.   }
  16.  
  17. }

Actionscript:
  1. var factory:AOPProxyFactory = new AOPProxyFactory();   
  2. var pointcut:IPointcut = new MethodNameMatchPointcut("writeMessage");
  3. var advice:IAdvice = new MethodTracerAdvice();
  4. factory.addAdvisor(new PointcutAdvisor(pointcut, advice));
  5. factory.target = MessageWriter;
  6.  
  7. var handler:Function = function(event:OperationEvent):void {
  8.   var messageWriter:MessageWriter = factory.getProxy(["Hello World!"]);
  9.   messageWriter.writeMessage();
  10.   messageWriter.writeAnotherMessage();
  11. };
  12.  
  13. var operation:IOperation = factory.load();
  14. operation.addCompleteListener(handler);

The console output would now be as follows:

Actionscript:
  1. * before 'writeMessage'
  2. Message: 'Hello World!'
  3. Message: 'Konnichiwa!'

Notice the following:

  • A Pointcut is defined to specify that advice should only be applied if the method name is "writeMessage"
  • The Advice and Pointcut are defined separately and then combined with a PointcutAdvisor. This allows us to reuse Pointcuts and Advice in other scenarios.

Other Types of Advice

The examples shown above only show IMethodBeforeAdvice, but there are a lot more advice interfaces that you can use. As mentioned earlier you can also intercept after invocation. This is not limited to method invocations, but you can also do this for Constructors, Getter and Setters. (Note that there are still some glitches in the alpha code concerning these types of advice.)

Conclusion

The current code is not production ready, but is certainly worth a look if you are interested in using AOP in your application or framework. We will release a first alpha release in the coming days with some samples and documentation. You can already check out the code at the AS3Commons Google Code site.

It is still early in the development so the API might change a bit before the official 1.0 release. If you have any ideas or comments about certain implementations, we're always eager to hear from you.

Some of the things on the roadmap that should make it into the release:

  • AspectJ expression language for pointcuts
  • Composite Pointcuts
  • Proxying of existing object instances

Looking forward to hearing your thoughts and enjoy coding!


Add to Bloglines - Digg This! - del.icio.us - Stumble It! - Twit This! - Technorati links - Share on Facebook - Feedburner
 

11 Responses to “Introducing AS3Commons AOP”

  1. Wim Van Buynder Says:

    Very cool concept and nice article! I will try this for sure!
    We use a lot of these stupid trace(“blaba”) lines in a non-structured way, I think this library can help us out to make our function look more decent! Thanks!

  2. Roland Zwaga Says:

    If you’re just interested in tracing then perhaps AOP might be a too heavy solution.

    Check out AS3Commons-logging:

    http://www.as3commons.org/as3-commons-logging/index.html

    Very lightweight and all you do is just add a TraceTarget during development, and remove it in production. (You could use a compiler switch for this as well).

    That might be an easier solution to your particular situation then going all the way the AOP route.

    cheers,

    Roland

  3. Piergiorgio Niero Says:

    Very useful, thank you for sharing.

  4. Ktu Says:

    I noticed that you are using Vectors in the source code. Does this library (and also the bytecode library) require FP 10+? Or is that just a design decision?

  5. Christophe Says:

    Hi Ktu, yes the libraries require FP10+. Using Vectors is basically for type-safety and speed reasons (although the latter is hard to measure and opinions vary).

  6. Ktu Says:

    I understand Vectors, I think my question was ill formed.
    Is there some native class that this all depends on that utilizes a FP 10+ feature?
    or
    Is it possible to back port this to a FP9 version without the use of Vectors (or anything else FP 10+ specific)?

  7. Christophe Says:

    I think it would be possible to create a FP9 version without Vectors, but that would require the underlying libraries to be FP9 compatible as well, which would be quite a task. Other than that, I don’t think there is any specific feature that is in FP10 that we need, unless there is something in AS3Commons Bytecode that I don’t know of.

  8. AlexH Says:

    I played a bit with the library and I really like it. But there is one thing I just can’t get my head around. Is it possible to prevent and intercepted accessor, a setter from being called? With a regular method it works just fine to set proceed to false, but not with a setter method.

  9. Christophe Says:

    Hi Alex,

    the behavior of the interecption in AS3Commons Bytecode currently does not allow to stop a getter or a setter. We’re looking into a way to make this possible. I’ll definitely update the code if we have any improvements.

  10. admapasz Says:

    Question: Are Pure AS3 projects supported? I tried to hook it up to one, but it broke in the constructor of ProxyFactoryUtil, which seemed to expect a Flex app. for the loaderInfo.

  11. Christophe Says:

    Hi admapasz,

    pure AS3 is supported. The proxy factory accepts a loaderinfo object that you can pass in. In case of a Flex app, the proxy factory will automatically figure out the loader info. In case of a AS3 project, pass in “systemManager.stage.loaderInfo”. Perhaps we could also figure this out automatically, but I have not looked into that yet.

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in