Implementing an AOP Framework, Part 2

An entry about arcitechture | design patterns Publication date 16. February 2008 11:58

It’s time for the third part of my ongoing series on Aspect Oriented Programming (here's part one and part two), and in this part I’ll be implementing the proposed AOP framework that I described in the previous post. I’ll cover the major parts of the framework and show how some of the core functionality was implemented, and if you want the full story you can download the full source code at the end.

Generating the Proxy

Remembering the architecture described last time; at the core of the API sits the Weaver, which is responsible for generating the transparent proxy that hijacks the invocations for a given instance so that we can apply advice to its members. The Weaver, then, needs to be able to generate and compile code at run time. There are essentially two ways to do this in .NET – the System.CodeDom and the System.Reflection.Emit APIs. The CodeDom, short for Code Document Object Model, allows us to build a representation of the structure of a source code document and either write it out to disk or compile it. Reflection.Emit, on the other hand, allows us to directly emit IL code and compile it (we can also save the compiled assembly to disk). Whereas IL code can be more difficult to work with, especially for larger more complex pieces of code, it has the benefit of being a more efficient and light-weight API than the CodeDom.  Additionally it does not have a link demand such as the CodeDom, which will only work when the caller has full-trust permission. My Weaver implementation will be using Reflection.Emit.

Essentially, the code we want to emit is fairly straight forward. Recalling the architecture described last week, we want each of the members of the interface to have their invocation controlled by an instance of the Advisor class. Thus, assuming that we have the following interface:

public interface IFoo
{
    void Bar(object arg);
}

We want to the Weaver to generate the following proxy type for it:

public class FooProxy : IFoo
{
    private readonly IFoo _real;
    private readonly Advisor[] _advisors;
 
    public FooProxy(IFoo real, Advisor[] advisors)
    {
        _real = real;
        _advisors = advisors;
    }
 
    public void Bar(object arg)
    {
        int id = 0;
        Advisor advisor = _advisors[id];
        object[] arguments = new object[1];
        arguments[0] = arg;
        advisor.Invoke(_real, arguments);
    }
}

A call to the Bar() method will now be forwarded to the associated Advisor, which can then execute any advice and so forth. We’ll look at the actual implementation of the Advisor later – for now we only define it like this:

public sealed class Advisor
{
    public void Invoke(object obj, params object[] arguments)
    {
        throw new NotImplementedException();
    }
}

Before moving on, lets write some unit tests that we can use to verify our code generator before implementing it (TDD style!). Essentially there are several types of members our code generator needs to successfully deal with:

  • Method with no return value and no arguments
  • Method with no return value and one or more arguments
  • Methods with a return value but no arguments
  • Methods with a return value and one or more arguments
  • Properties (get and set)
  • Immutable properties (no set)
  • Settable properties (no get)

 

The downloadable source code contains tests for all these - here, I'll only reproduce the simplest scenario; a method with no return value and no arguments.

public interface INoReturnNoArgs
{
    void Foo();
}
[TestMethod]
public void WeaveMethodTest()
{
    MockObject<INoReturnNoArgs> mock = MockManager.MockObject<INoReturnNoArgs>();
 
    Mock advisorMock = MockManager.MockAll<Advisor>();
 
    INoReturnNoArgs proxy = new Weaver().Weave<INoReturnNoArgs>(mock.Object);
    Assert.IsNotNull(proxy, "Generated proxy should not be null");
 
    advisorMock.ExpectCall("InvokeAndReturn", typeof(object))
        .Args(mock.Object,
              new ParameterCheckerEx(delegate(ParameterCheckerEventArgs args) 
                {
                    // should pass an empty object array as the second parmeter, as there are no arguments for Foo()
                    object[] expected = args.ArgumentValue as object[];                            
                    return null != expected && expected.Length == 0;
                }));
 
    proxy.Foo();
}

I’m using TypeMock Isolator to help me isolate the Weaver class, so that I can test it and nothing else - I don’t want the success or failure of my test to be in any way affected by the correctness of the Advisor class or the implementation of the interface I’m proxying. More so – I haven’t even implemented these yet! When I do, they will have their own tests to verify their correctness; my Weaver tests should only care about verifying the Weaver itself. The test then, simply mocks up an instance of the INoReturnNoArgs interface which contains a single method that has no return value and no arguments. Then, I call the Weaver and tell it to generate a proxy for the given object. Finally, I use TypeMocks ability to verify that the Invoke method on my mocked Advisor class actually gets invoked when I then use the generated proxy to call the Foo method. If this test passes, I can be certain of two things – the proxy type generated by the Weaver compiles without errors, and a method call on the proxy results in the call being forwarded to the Advisor. That’s a fairly good indication that the Weaver is functioning correctly.

The next step now, then, is to make this test pass. To do that, we need to implement the Weaver.Weave<T> method:

public T Weave<T>(T instance, params IPointcut[] pointcuts)
{
    Type iType = typeof (T);
    Type proxyType;
 
    int hash = iType.GetHashCode();
 
    if (!_proxyTypeCache.TryGetValue(hash, out proxyType)) // get proxy type from cache?
    {
        lock (_proxyTypeCache) // double-checked lock for optimized synchronization
        {
            if (!_proxyTypeCache.TryGetValue(hash, out proxyType))
            {
                proxyType = BuildProxyType(iType); // first time; build proxy type and cache it
                _proxyTypeCache.Add(hash, proxyType);
            }
        }
    }
 
    // create advisors for instance
    List<Advisor> advisors = new List<Advisor>();
 
    foreach (MemberInfo member in typeof(T).GetMembers())
    {
        if(member is MethodInfo && (((MethodInfo)member).Attributes & MethodAttributes.SpecialName) > 0)
        {
            continue; // skip special-name methods (property get_ & set_ methods, for instance)
        }
 
        // set up advisor for each proxied member
        Advisor advisor = new Advisor(member);
 
        foreach(IPointcut pc in pointcuts)
        {
            foreach (IAdvice advice in pc.Advise(member))
            {
                advisor.AddAdvice(advice);
            }
        }
 
        advisors.Add(advisor);
    }
 
    // instantiate proxy
    T proxy = (T)Activator.CreateInstance(proxyType, instance, advisors.ToArray());
 
    return proxy;
}

That looks deceptively simple – where’s the code generation I was talking about? I’ve refactored it out into its own method, BuildProxyType. I’m not going to reproduce the code for it here, as it’s fairly long. Its pretty cool stuff however - if you want to have a look at it you can download all the source code at the end of this post and open up the Weaver.cs file. For the purposes of this walkthrough though, all we need to know is that it generates and compiles a type implementing the given interface, as earlier described. The Weaver then creates a new instance of this type, using constructor injection to pass it the real implementation and the associated advisors.

Advising a Member Invocation

The next step now, is to implement the Advisor. Essentially there are three scenarios here – there could be no advice, one advice or many advice that should be chained together. Each of these three scenarios needs to be supported for different types of members – we’ve settled on methods and properties so far, but we should make sure to have extensibility at this point so that we may later add support for events, for example. Additionally we need to allow the advice to control the target invocation, because each advice should be allowed to execute before and after logic and optionally stop the invocation from proceeding (a cache advice may wish to return a value from its cache and skip actually invoking the target member at all, for example).  

Assuming the Advisor holds a stack of advice, we can think of the flow like this:  The Advisor executes the first advice, passing it the current target. This target is either the next advice in the chain, or if there is no more advice in the chain it is the actual target method or property. The advice, then, can execute its before logic before notifying the target it received to proceed. This then goes on recursively until the real target has been executed, and the unwinding begins, allowing each advice to execute any after logic. In the previous part of this series, we saw a figure representing this:

image

To implement this flow, we need the notion of a target. The target should do one of two things - proceed to the next advice when there is advice left on the stack, otherwise invoke the real target. The real target can be different things, so we’ll use inheritance to allow for this. Thus, we can define an abstract base type AdviceTarget which holds the flow control logic, and defers the invocation logic to the specific implementations.  Again, lets write our test first:

public class MockAdvice : IAdvice
{
    public object Execute(AdviceTarget target)
    {
        return target.Proceed();
    }
}
[TestMethod]
public void SingleAdviceProceedTest()
{
    MockObject<AdviceTarget> adviceTargetMock = MockManager.MockObject<AdviceTarget>();
    AdviceTarget target = adviceTargetMock.Object;
 
    FieldInfo adviceField = typeof(AdviceTarget).GetField("_advice", BindingFlags.NonPublic | BindingFlags.Instance);
 
    Assert.IsNotNull(adviceField, "Invalid test data");
 
    // inject an advice into the target
    MockObject adviceMock = MockManager.MockObject<MockAdvice>();
    adviceField.SetValue(target, adviceMock.Object);
 
    // expected run: Proceed should first Execute the advice; advice will Proceed on target again, which will finally Invoke
    adviceTargetMock.ExpectUnmockedCall("Proceed", 2);
    adviceMock.ExpectUnmockedCall("Execute");
    adviceTargetMock.ExpectCall("Invoke");
    target.Proceed();
}

Here, we set up a mocked instance of the AdviceTarget (notice that this is actually an abstract class - TypeMock generates a mock implementation for us on the spot. Neat!). Now, the target needs to know about the advice it contains – we’ll probably implement this using constructor injection. However, since we’re mocking an abstract class we can’t instantiate it using a constructor, so we’ll use a bit of reflection instead – this means we’re relying on our AdviceTarget implementation to store the advice in a field named _advice, so we assert that this field really exists to make sure our tests fails in a meaningful manner if that should not be the case (remember we've yet to implement this class, so we're making an educated guess here). The interesting part is when we have a target set up properly and can test it – we know that the flow we’re looking for is for a Proceed call on the target to find a single advice on the stack, which should thus be executed. This advice is of type MockAdvice, and from its implementation you can see that it does nothing but proceed on the target, beginning our recursion. This second call to Proceed will find no more advice on the stack, so it should now invoke the target. In the full source code, there are variations of this test which verifies the scenarios where there are no advice and more than one advice, but this is essentially all there is to it. If this test passes, we can be fairly certain that our target handling is working properly. To make it pass, we need to implement the AdviceTarget class:

public abstract class AdviceTarget
{
    private IAdvice _advice;
 
    public AdviceTarget(IAdvice advice, object target, MemberInfo memberInfo, params object[] arguments)
    {
        this.Target = target;
        this.TargetInfo = memberInfo;
        this.Arguments = arguments;
        _advice = advice;
    }
 
    /// <summary>
    /// Gets the target
    /// </summary>
    public object Target { get; protected set; }
 
    /// <summary>
    /// Gets the MemberInfo describing the target
    /// </summary>
    public MemberInfo TargetInfo { get; protected set; }
 
    /// <summary>
    /// Gets the arguments of the target 
    /// </summary>
    public object[] Arguments { get; protected set; }
 
    /// <summary>
    /// Proceeds with the invocation towards the target
    /// </summary>
    /// <returns></returns>
    public object Proceed()
    {
        // execute all advice in chain recursively, then execute the target
 
        if (null != _advice)
        {
            IAdvice next = _advice;
 
            if (_advice is ChainedAdvice)
            {
                _advice = ((ChainedAdvice)_advice).Next;
            }
            else
            {
                _advice = null;
            }
 
            return next.Execute(this);
        }
        else
        {
            // end of advice chain; invoke target
            return Invoke();
        }
    }
 
    /// <summary>
    /// When implemented in a deriving class, should invoke the target
    /// </summary>
    /// <returns></returns>
    protected abstract object Invoke();
}

Of interest here is the Proceed implementation. Advice can be either single, or chained. A chained advice is essentially an advice that knows who the next advice in the chain is – a one-directional linked list, if you will. The Proceed method then walks this chain and calls each advice recursively until it reaches the end, at which point it calls the Invoke method. This method is abstract, as the invoke logic will differ for different targets. For example, the MethodTarget implementation looks like this:

public class MethodTarget : AdviceTarget
{
    public MethodTarget(object target, IAdvice advice, MethodInfo memberInfo, params object[] arguments)
        : base(advice, target, memberInfo, arguments)
    {}
 
    protected override object Invoke()
    {
        MethodInfo method = (MethodInfo)this.TargetInfo; 
        return method.Invoke(this.Target, this.Arguments);
    }
}

In Use

So far, we've covered most of the interesting aspects of the implementation. Download the source code at the end of this post if you want to look at the complete implementation - now, I'd like to briefly explain how the API can be used. Below is an example usage (also included in the download):

 

// add logging as an advice
ConsoleLoggerAdvice advice1 = new ConsoleLoggerAdvice();
 
// caching advice; performing the same calculation several times should be superfast!
ResultCacheAdvice advice2 = new ResultCacheAdvice();
 
// set up a pointcut for the method CalculateSomething on IFoo, applying the advice to this method
MemberPointcut pc = new MemberPointcut(typeof(IFoo).GetMethod("CalculateSomething"), advice1, advice2);
 
ProxyFactory.AddPointcut("1", pc);
 
// test it
for (int i = 0; i < 2; i++)
{
    Console.WriteLine("Begin.");
 
    IFoo foo = ProxyFactory.CreateProxy<IFoo>(new Foo());
 
    int val = foo.CalculateSomething(5);
    Console.WriteLine("Result: " + val);
 
    Console.WriteLine("Done." + Environment.NewLine);
}
 
Console.ReadLine();

Here, we set up a couple of advice and configure a pointcut for the CalculateSomething method on the IFoo interface. Then, we create a proxy for this interface, passing it an instance of our own implementation. The proxy will then apply the logging and caching advice whenever we call the method - you can see this from the output the program produces:

Begin.
** Log: Calling member IFoo.CalculateSomething
** Log: argument 1: 5
** Log: Member returned in 1501 ms. Return value: 500
Result: 500
Done.
Begin.
** Log: Calling member IFoo.CalculateSomething
** Log: argument 1: 5
** Log: Member returned in 0 ms. Return value: 500
Result: 500
Done.

Notice that the logging advice writes out the time spent executing the method, and that for the second run the cache advice has kicked in and just returned the value from the cache, skipping the target method resulting in a super fast invocation. Obviously this is a fairly naive example, but at least it shows the possibilities of using an AOP framework. If you look at the source code, you'll also notice how cleanly separated everything is; the CalculateSomething method is not cluttered with logging and caching logic, and this logic can easily be reused for other methods, improving not only code reuse but maintainability and lowering the change resilience of our code.

Source Code here!

In this post I’ve tried to highlight the most interesting parts of the implementation of my AOP framework. There are details I’ve not covered here though, for example the implementation of different pointcuts. If you’re interested in digging further, then you can download the source code here. It includes a set of unit tests (which require TypeMock to run) plus a small demo application which shows the basic usage of the framework.

Moving on...

Initially, my plan was to end my tour of AOP with this post, but I’ve found the topic so interesting that I’ve decided to keep going a bit further. So far I’ve covered the core of what AOP is all about, but there are things that are closely related which have not been touched upon. For instance, in order to really take advantage of an AOP framework in our code, we'd need an Inversion of Control container. This can also make configuring advice and pointcuts much more elegant - perhaps we'd like to allow for loading this from an XML configuration file for example. And then there's the concept of mixins and introductions, which can be used to kind of simulate multiple inheritance. Additionally, I've neglected to talk about the implications of using an AOP framework, especially concerning performance. So, stay tuned for more on some of these topics soon :)

Be the first to rate this post

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Implementing an AOP Framework, Part 1

An entry about arcitechture | design patterns Publication date 10. February 2008 12:03

Last week, I wrote an introduction to the concept of Aspect Oriented Programming. As promised, here is the first of two follow-up posts that showcases the code necessary to implement an AOP framework in C#. In this first part I'll go over the architecture of the framework, and then in the second part, which I'll post in a few days, we'll start on the actual implementation. I've chosen a fairly simple form of AOP to implement, which is compositional, proxy-based AOP. I'll talk more about the implications of this choice towards the end of the post.

Pointcuts and Advice

The cornerstone of our AOP framework will be how the advice is applied to a target. We'll limit the capabilities of the framework to targeting advice for methods and properties, which will cover most use cases. From last weeks article, we remember that where an advice is applied is defined by a joinpoint, and a set of joinpoints make up a pointcut. We're not going to model joinpoints directly in our framework, since a pointcut by definition implicitly defines one or more of them by virtue of itself.

A pointcut, then, needs the ability to decide whether it applies to any given member. We can split this question into two parts - does the pointcut apply to type X, and if it does, does it apply to the specific member Y on that type? If the answer is yes, the pointcut can then be asked to supply us with the advice to apply to the member in question.

Chaining Advice

Any given member may be advised by zero, one or more advice. Thus, we need a way of chaining advice together, so they can be executed in order. Assuming there are n advice for a given member, we expect the advice to be invoked recursively towards the target (member), as shown in the figure below:

Advice chain

When each advice is executed, it can perform its before logic and then choose to proceed towards the target, walking down the advice stack. When we reach the end we invoke the target member, and the begin walking back up again as each advice executes any after logic and returns. Should an advice choose not to proceed on the way down, but instead return immediately, no further advice down the stack will be executed, and the target will neither be invoked. This is an important feature that can be used for caching and validation advice, for example.

Weaving the Proxy

Having defined a set of pointcuts and how the advice is execute on a target, we need a way to actually hook this up to the advised member. Assuming that we have some interface IFoo and an implementation of it Foo, we do this by generating a transparent proxy which implements IFoo and wraps an instance of Foo. When a client invokes a member on the proxy, the proxy then - instead of directly invoking the real member implementation - executes the advice stack as described above. Generating this proxy is the responsibility of the weaver. We'll be using the Reflection.Emit API to build the proxy at runtime; more on that next week.

The Architecture

Below are the central parts of the architecture so far discussed. Foremost is the ProxyFactory, which takes an instance of a given type implementing some given interface, looks up any pointcuts from some configuration that should be applied to it, and forwards them to the Weaver. The Weaver then generates a proxy for the interface, weaving in the advice defined by the pointcuts as a set of Advisors for each advised member. When the client later invokes a member on the proxy it is in reality the Advisor for the member which executes. Instead of directly invoking the implementation of the member on the real instance, the Advisor sets up an AdviceTarget and starts walking the stack of advice, as described earlier.

Class diagram  

A Flawed Implementation?

As mentioned in the beginning, the form of AOP implemented here based around the notion of generating a runtime proxy. This requires that any type we want to apply advice to must be defined by an interface, since the framework will use it to build the proxy and inject the advice using a variant of the decorator pattern. There are some drawbacks to such an architecture - most notably that only calls through the interface will be advisable. This means that any calls directly between members internally in a class will not execute any advice applied to them. Say, for instance, that you have an interface with two members - a GetAllItems method and a Count property. If we implement this interface so that the Count property calls GetAllItems internally to figure out the number of items, and then apply an advice to the GetAllItems method that caches the items, then only external calls to the proxy will hit the cache while the implementation of the Count property itself will always go directly to the GetAllItems method. This behavior is something that users of the framework need to be aware of, as it can have significant side effects if not taken into consideration when applying advice. Incidentally, this is also how the several AOP frameworks (like Spring) is implemented.

There are other ways to inject the aspects that would circumvent this issue - for example, we could instead of using an interface to create our proxy demand that any advisable member needs to be virtual and then use inheritance to create our proxy. Another much more powerful solution - but also much more complicated to implement - would be to use the (unmanaged) Profiling API to directly inject advice into a type right before the CLR JITs it. The mocking framework TypeMock uses this approach to generate its mocks. These are all runtime solutions - we could also implement a compile-time framework, but this would take away the flexibility of the framework as any change in advice configuration would require a recompile.

That's it for this part - next time we'll look at the actual code needed to implement the framework as proposed in this post, complete with downloadable source code and examples. Make sure to grab my RSS feed (if you haven't already), and don't miss it!

Be the first to rate this post

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Aspect Oriented Programming - A Primer

An entry about arcitechture | design patterns Publication date 5. February 2008 22:12

When developing an application, we can divide it into a set of concerns. The business logic that is the heart of the application is often referred to as the core concern, and then there are all the other things that the application needs to do to accomplish it - handling persistence, logging, security, transactions and so forth. Many of these are what we call crosscutting concerns, because they are not cleanly separated from the rest of the system but affect other concerns. Logging, for instance, crosscuts just about all the parts of an application, and as such is near impossible to separate completely from the rest of the code. However hard you try, most modules in your application will need to know about, and to some extent be bound to, the implementations (or at the very least the contracts) of its crosscutting concerns. Not only does this limit your ability to change and evolve the implementation of these, but just as importantly it also means that the code dealing with the what of the core concern is obscured with code dealing with the how of the system-level concerns.

The ACB of AOP

In AOP, the concerns of an application are called aspects. How we separate the concerns of an application into aspects can be visualized using the prism analogy, shown below. I've reproduced the diagram from a JavaWorld article titled I Want my AOP written by Ramnivas Laddad back in 2002, but in reality it can be said to stem from the theory of modal aspects by Dutch philosopher Herman Dooyeweerd from way back in the 1930's.

The prism analogy

The goal of aspect oriented programming, then, is to identify and isolate the concerns of an application, so that they can be implemented in isolation from each other.

From a developers point of view, there are essentially three things that an AOP framework will let you do to accomplish this; dynamically inject code before a given method, after a given method, or both. We call the code which we inject advice, and the three basic flavors of it are aptly named before advice, after advice and around advice. The location (for example a method) a given advice is applied at is called a joinpoint (which incidentally isn't necessarily a method) or a pointcut. Depending on the implementation of the AOP framework, this weaving either happen dynamically at runtime or statically at compile time. Most implementations accomplish it by setting up a transparent proxy and applying the decorator pattern, using interfaces/inheritance, whereas yet others use things like the .NET Profiler to dynamically inject the aspects straight into the target code. Each method have their own benefits and limitations, but that is a discussion beyond the scope of this primer.

The diagram below shows an example of how a compositional AOP plays out.

   Compositional AOP

In the diagram, we have an IAccount interface on which the client code calls the Withdraw method. The AOP framework builds a proxy for the real Account implementation, weaving the original code and the applicable advice at the configured joinpoints. When the client code then calls the Withdraw method, it is actually calling it on the proxy which then first applies and evaluates the respective advice before allowing the Withdraw method on the real Account class to be invoked. The Withdraw method can thus be implemented cleanly, only containing the required business logic to withdraw funds from an account, and the cross-cutting concerns are transparently handled in separate, reusable modules.

More, More!

I am planning a series of posts following up on this topic, which will showcase the implementation of a light-weight AOP framework with the primary goal of gaining a thorough understanding of the principles explained in this post. Look out for the first part of that soon, but if you just want to get started developing with AOP then there are several tried and tested frameworks readily available for you to choose from. Most notably the .NET version of the Spring Framework has AOP functionality, and the Enterprise Library includes a Policy Injection application block (yeah, they had to make up their own name for it - its Microsoft, after all :p). There's also Loom.NET and Aspect# (part of the Castle Project), among others.

Currently rated 1.0 by 1 people

  • Currently 1/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Powered by BlogEngine.NET 1.4.5.0

Welcome!

My name is Fredrik Kalseth, and this is my blog - thanks for visiting! I am fortunate enough to work with what I love for a living, and this blog is essentially the biproduct of that.

I work as a senior consultant for Capgemini, and am also an active participant in the Norwegian .NET community, as an avid attendee but also as a speaker (most recently at NNUG and MSDN Live).

As a developer, I have a wide circle of interest. My primary passion is for agile, test-driven development, with focus on best practices and clean code. That said, I also love to work on the frontend, especially with web development.

On Twitter? My handle is fkalseth. On LinkedIn? I`m there too.

NDC 2010

The conference to attend this summer happens June 16th-18th in Oslo, Norway. Are you going? Be sure to catch my talk on AOP while you're there!

 

Disclaimer

This is a personal blog; any opinions expressed here are my own and do not necessarily reflect those of my employer. All content herein is my own original creation, and as such is protected by copyright law. Unless otherwise stated, all source code posted on this blog is freely usable under the Microsoft Permissive License.

What Readers Talk About

Comment RSS