1

The May 2009 Release of Delphi Prism introduced the Cirrus layer that provides Delphi Prism developers access to a library for Aspect Oriented Programming natively for the first time. The AOP Wikipedia article has a much more detailed explaination than I could provide but for those who don’t want to read the full article AOP represents a new programming paradigm which claims to allow a cleaner separation of concerns. The general idea is that you can write your code as an Aspect which can then be injected into your target class, allowing the Aspect code to replace, wrap or alter the target code. There are a few 3rd party frameworks for Delphi Win32 such as MeAOP but I’m not sure if their support is anything like as extensive as Cirrus.

Why would you need  to use AOP?

Many people don’t really understand why they would need to use AOP in any way. It’s true that nearly all programming paradigms support some form of separation and modularity but to see where this can quickly fall down, you simply have to look at the classic logging example which has been the staple diet of coverage of the Cirrus framework. Logging is a good example because it is likely that if your application implements logging features, then you probably have logging code mixed up in your beautifully designed business objects at various places tainting the implementation of your once clean object.

A Simple Example

For the purposes of those people who weren’t convinced on it’s usefulness from the example given. Imagine we have a very important Business object of a Type called TCustomer. We’ve been told that we need to log everything done to our Customer objects for audit purposes (and you wouldn’t want to get on the wrong side of the Internal Audit team..).  We could easily add our logging calls into every function of TCustomer but this going to require a lot of CTRL+C, CTRL+V and is a lot of identical code-repetition and shouldn’t we be striving to avoid code repetition? This might also get your local overly-sensitive Anti Copy-Paste mafia up in arms. There is another problem with simply inserting our code into each method: What happens if later on, another programmer adds another method for updating, deleting or even just saving a copy of our Customer. Internal Audit are not going to be happy. Of course, you could blame it on your inexperienced colleague but that’s not going to make much difference, it’s a team failure after all.

Had we used AOP we could have used something as simple as the Cirrus Introduction LogToMethod code, tagged our TCustomer class with a single attribute and been saved from this potential oversight:

// For the full code for this example, please see the original introductory tutorial at:
// http://prismwiki.codegear.com/en/Cirrus_Introduction
namespace MyLogToMethodAspect;

interface
uses
  RemObjects.Oxygene.Cirrus;

type
  [AttributeUsage(AttributeTargets.Class or AttributeTargets.Struct)]
  LogToMethodAttribute = public class(System.Attribute, IMethodImplementationDecorator)
  public
    [Aspect:AutoInjectIntoTarget]
    class method LogMessage(aEnter: Boolean; aName: String; Args: Array of object);

    method HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
  end;

implementation

class method LogToMethodAttribute.LogMessage(aEnter: Boolean; aName: String;
  Args: Array of object);
begin
  if aEnter then
    Console.WriteLine('Entering ' + aName)
  else
    Console.WriteLine('Exiting ' + aName);
end;

method LogToMethodAttribute.HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
begin
  if String.Equals(aMethod.Name, 'LogMessage',
    StringComparison.OrdinalIgnoreCase) then exit;
  if String.Equals(aMethod.Name, '.ctor',
    StringComparison.OrdinalIgnoreCase) then exit;

  // Cool use of anonymous methods here....
  aMethod.SetBody(Services,
    method begin
      LogMessage(true, Aspects.MethodName, Aspects.GetParameters);
      try
        Aspects.OriginalBody;
      finally
        LogMessage(false, Aspects.MethodName, Aspects.GetParameters);
      end;
    end);
end;

end.

Of course, you don’t need Aspect Oriented Programming in the same way as you don’t need Object Oriented Programming, we could just revert to procedural programming but as programmers we strive to separate various concerns cleanly and this is another step towards that.

What’s the catch?

The problem with separating our code in such a way is that potentially, it could be hard for a programmer unfamiliar with AOP to understand the code as the only clue of any logging activity for our target class might be a single attribute in our interface section. Our Aspect might also rely upon the underlying method in which case we could run into problems if we restructure or  move our methods around causing unintended consequences. There is also a concern that a serious bug in our Aspect code could lead to a more widespread failure in the program (as it has the potential to be applied to large swathes of code easily).

However, with careful testing and implementation these potential drawbacks can be mitigated.

Apart from Logging, what else can I use it for?

Typical uses include logging, authentication, caching and dependency injection. John Moshakis has been blogging some elegant real-life uses of the Cirrus framework such as Implementing Dependency Injection, allowing him to switch seamlessly between test and production services in an app.

My Own Journey to AOP

After the Cirrus Framework was released I wanted to try to build my own Aspect which wasn’t the logging sample provided. I had a service that retrieved a string of XML from a REST Web Service at frequent intervals which I felt could be improved with caching functionality. The Code for the Web Service and for the Cache were just test classes for the purpose of this experiment but I would imagine that you could extend this to use the The Caching Application Block from the Microsoft Enterprise Library if you wished. Normally, I could add caching to each method by modifying it like so:

method TFlickr.GetUserInformation(username: String): String;
begin
  // Do we have this information in our cache?
  if (MyCache.HasData('UserInfo_' + username)) then
  begin
    // We do, use the cache'd data.
    Result := MyCache.GetData('UserInfo_' + username);
  end
  else
  begin
    // No cache'd version, retrieve a new set of data.
    { Open our TCP Socket, call the webservice with the
      correct parameters and return the result.     }
    // Add our newly retrieved data to the cache.
    MyCache.SetData('UserInfo_' + username, Result);
  end;
end;

This method of implementation would lead to me having to put this code into each method and would lead to code repetition and possibly bugs being introduced (for example, if I forgot to update the cache key string on one method). I wondered if I could have a CacheString attribute that would allow me to automaticaly add my generic “do we have cache’d data? if so use it else carry on”  code to each method.

First of all, I tried essentially moving this code into a Method Aspect and adding a generic key function which takes the method name and method arguements and turns it into a consistent string using the Aspects.MethodName and Aspects.GetParameters methods:

WARNING! BROKEN CODE:

class method MyCache.getKeyFromMethod(aName: String; Args: Array of object): String;
begin
  var argConcat: String;

  argConcat := aName;
  for argTemp: String in Args do
  begin
    argConcat := argConcat + argTemp;
  end;

  Result := argConcat;
end;

method MyCacheAttribute.HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
begin
  aMethod.SetBody(Services,
    method begin
	  var cacheKey := MyCache.getKeyFromMethod(Aspects.MethodName, Aspects.GetParameters);
	  // Do we have this information in our cache?
	  if (MyCache.HasData(cacheKey)) then
	  begin
		// We do, use the cache'd data.
		Result := MyCache.GetData(cacheKey);
	  end
	  else
	  begin
        Aspects.OriginalBody;

        // Send the result of the Original Method to the Cache for next time.
        MyCache.setData(cacheKey, Result);
      end;
    end);
end;

Unfortunately it is not quite as simple as just placing your code into the AMethod.SetBody Anonymous method. Luckily it turned out that the Cirrus documentation on the Wiki had recently been updated and I discovered the Cirrus Statements and Cirrus Values namespaces after a bit of helpful guidance from John Moshakis on StackOverflow and Twitter (thank-you for that John!). I discovered that what you are required to do in most cases is to construct your code in an almost compiler like fashion using the Statement and Values namespaces.

Eventually after playing around with the Cirrus API, I eventually ended up with the following code:

method MemCacheAttribute.HandleImplementation(Services: IServices; aMethod: IMethodDefinition);
begin
  // Get a Reference to our Result Value
  var newResult := new ResultValue();

  // Get our MemCache Object
  var cacheType := Services.FindType('AOPCacheLibrary.MyCache');
  // Setup our static methods.
  var getDataProc := new ProcValue(new TypeValue(cacheType), 'getData', [new NamedLocalValue('cacheKey')]);

  // Assign the result of our cache request to a local variable.
  var getDataAssignment := new AssignmentStatement(new NamedLocalValue('strData'), getDataProc);

  // CACHE DATA USED: To assign our Cache'd data to the Method Result
  var useCacheData := new AssignmentStatement(newResult, new NamedLocalValue('strData'));
  // CACHE DATA NOT USED: To assign the result of our OriginalBody to the Method Result.
  var useOriginalMethodResult := new AssignmentStatement(new NamedLocalValue('resultOriginal'), newResult);

  aMethod.SetBody(Services,
    method begin
      var strData: String;
      // Get our Cache Key Name
      var cacheKey := MyCache.getKeyFromMethod(Aspects.MethodName, Aspects.GetParameters);

      try
        // See if we have anything in the cache for this key.
        unquote(getDataAssignment);
        // Replace this
        unquote(useCacheData);
      except
        on E: Exception do
        begin
          var resultOriginal: String;
          // No cache data found, execute the original method body.
          Aspects.OriginalBody;

          // Assign the result of the Original Method to a new variable.
          unquote(useOriginalMethodResult);
          // Send the result of the Original Method to the Cache for next time.
          MyCache.setData(cacheKey, resultOriginal);
        end;
      end;
    end);
end;

According to the Red Gate .NET Reflector and Disassembler, this produces the following code:

    function TFlickr.GetUserInformation(username: String): String;
    var
        Result: string;
    begin
        cacheKey := MyCache.getKeyFromMethod('GetUserInformation', New(array[1] of TObject, ( ( userid ) )));
        try
            Result := MyCache.getData(cacheKey)
        except
            on E: Exception do
            begin
                // This is where we do our _ORIGINAL_ function
                resultOriginal := Result;
                MyCache.setData(cacheKey, resultOriginal)
            end
        end;
        begin
            Result := Result;
            exit
        end
    end;

This means that I can now add caching to any of my string returning web service methods in my class by simply tagging the methods that I want cached with a code attribute:

  TFlickr = public class
  private
	{code}
  public
    {more code etc, etc}
    [Aspect:MyStringCache]
    method TFlickr.GetUserInformation(username: String): String;
  end;

This works perfectly but there are a few problems with this code:

  • As you can see: constructing statements using the Cirrus API (in the statements before the aMethod.setBody) doesn’t exactly make for readable code. You may wish to consider outlining each statement with psuedocode so that it is clearer at a glance what the API calls produce.
  • I should be using the Cirrus Procedure Value calls for all three calls to the Static “MyCache” class but am not because I couldn’t quite get this working. Please be aware that John informs me that the method I use in the first and third instance is not the correct way to go about this.
  • This exact method only currently works for System.String returning methods. There must be a way of abstracting this function to apply it to almost a method returning almost any type (if anyone has any suggestions about how this might be accomplished then, by all means post a link in the comments).

Please note that the code above is NOT intended to be a reference implementation for AOP or Application Caching but is merely intended to show you my own learning path with Cirrus and AOP. The Cirrus API is quite difficult to work with at first and can give you frustrating compiler errors but it’s worth keeping an eye on the documentation pages and as more examples appear online it will become easier to work with. You should have the Red Gate .NET Reflector on hand with its disassemble function to confirm exactly what the resulting code in your Output assemblies looks like.

If you have any constructive suggestions for my code above then please let me know. Additionally, if you think it would be useful for me to post the downloadable Delphi Prism project then please also let me know in the comments below.

Starting your own journey to Cirrus/AOP

Tags: , , ,

One Comment

Leave a Comment