BT
x Your opinion matters! Please fill in the InfoQ Survey about your reading habits!

Painless AOP with Groovy

Posted by John McClean on Oct 02, 2006 |

Cedric Beust described Aspect Oriented Programming (AOP) as "a great idea that will remain the privilege of a few expert developers". For some, even with Spring and JBoss, the barriers to entry remain too high. Fortunately, this is an area where the dynamic languages may be able to help out. They can provide either a gentle nursery slope for experimentation and learning, prior to the red runs of AspectJ, or a highly productive work environment in their own right. Java developers need not even stray far from home. Groovy, a JVM dynamic language with a Java-like syntax, sports impressively powerful features that make mimicking AOP a breeze. Although we will focus on Groovy in this article, the zeitgeist demands a comparison with the much loved and feared Ruby. Rubyists can also get 80% of the functionality with 20% of the effort.

AOP allows us to modularise code that may otherwise remain entangled across a number of methods and classes. For example, we may find ourselves doing the same security check around a number of different units of work, instantiating some variables before other units and logging some information after yet others have completed or thrown an exception. AOP allows us to write all this functionality once and have it applied to our methods at the appropriate point (before, after, around or when an exception is thrown), where once we may have violated the DRY principle and written it multiple times. Typically, in Java, we would use one of two mechanisms. Either we would use a framework to modify the byte-code of our own classes to directly insert this functionality, or we would dynamically create a proxy for our own classes (either using JDK Dynamic Proxies or CGlib). In Groovy, as we shall see, we need neither technique.

MOPs : not just for cleaning!

Groovy's Metaobject-Protocol is a mechanism by which application developers can modify the implementation and behaviour of their language. Groovy's MOP is particularly powerful. The result of judicious design, all dynamic method calls get routed through invokeMethod and property access through getProperty and setProperty. Thus, we have a single point of contact for modifying the core behaviour of the Objects we create. By overriding invokeMethod, getProperty and setProperty we can intercept every single call to our Objects without the need for a proxy or byte-code manipulation.

Free the functions!

"Would you be willing to trade all the days from this day to that for one chance, just one chance, to come back here and tell our enemies that they may take our lives, but they'll never take our freedom." - Mel Gibson rouses the Scots in Braveheart

Language aficionado Steve Yegge, humourously labelled Java the "Kingdom of the Nouns", a land where functions are enslaved and may only travel attached to a noun. Groovy frees functions from the tyranny of Object bound slavery. A Groovy Closure is an anonymous function that can access its enclosing scope, can be called repeatedly at will, and be passed around as it were data. Syntactically closures are defined in what look like Java code blocks. The example below prints out "hello world from Groovy!".

String printMe =  "hello world"
def c = { parameter | println(printMe + parameter} }
c(" from Groovy!")

The ability to create such lightweight functions combined with the power of the Groovy MOP will enable us to implement some AOP-style magic with very little code.

Before we start

If you want to follow the examples below, you'll need to follow these steps:

  1. Download Groovy :
    Groovy can be downloaded from the Groovy homesite
  2. Follow the installation instructions on the Groovy installation page
  3. Optionally, download and install the Groovy Eclipse Plugin. The update site for the plugin is http://dist.codehaus.org/groovy/distributions/update/. There is a Wiki site with some additional information here. The Groovy Eclipse plugin is still pre-release, but, with a bit of tinkering, I find it works well. You may need to take into account the following :-
    • Setup your step filters : In preferences/java/debug/stepfiltering, turn on step filtering for the following classes:-

      Groovy does a lot of work under the covers and this prevents the Eclipse plugin taking you through the Groovy infrastructure as you execute your code.

    • You step into Closures rather than over them
    • You can't currently use debug as/groovy directly, instead you must create a new Debug Application configuration. The steps are defined in the wiki.
    • In order to get the plugin to find your source you may find that you have to give Eclipse a few additional clues. A work around is to make a copy of your classes directory and add it as the last class folder. Then in project/properties/libraries you can attach your source folder to your dummy class folder.

 

Iteration 1: Let's get started

AOP allows us to execute an arbitrary function when a specified method is called. When we do this, we are said to be applying "advice". Before and after advice types are particularly easy to implement in Groovy.

Define the infrastructure

First, let's write a Groovy class that can hold the details of a method call that has just been intercepted. We will pass this Object to all our advice implementations as a parameter. This will allow them to "understand" their context, to access and even modify properties on the target Object and the incoming parameters.

N.B. The keyword "def", used here as an instance variable modifier, indicates that the variables are properties. Groovy will add standard JavaBean Getters and Setters for us. Also note that this class extends Expando, as this will be important later.

We can define a class that overrides invokeMethod and inserts optional calls to our before and after advice types. To leverage AOP-like functionality, we need only inherit from this Class. In order to force Groovy to call invokeMethod for each method call to our class we need to implement the marker interface GroovyInterceptable.

In this class the keyword def is being used as a local variable modifier. This indicates that the variable is dynamically typed. We also take advantage of Groovy's convenient parameter naming support to instantiate our AopDetails Object.

This principle can be extended to property access, but, for clarity, I shall leave the implementation of this to your imagination.

We've defined all the necessary infrastructure to get started, so let's demonstrate some AOP in action with an example.

An example in action

We need a main method to apply advice to our Example class, and print out some helpful status messages.

If you are following along with the Groovy Eclipse plugin, you can a breakpoint at the start of the main method and step through the code. We can execute Runner.groovy by right clicking on the file and selecting run-as -> groovy from the popup menu, to debug follow the steps above & in the wiki.The output from executing Runner.groovy is as follows

Calling print message with no AOP :-


Calling print message with AOP before advice to set message :-
hello world


Calling print message with AOP after advice to output status :-
hello world
Status:message displayed


Calling print message via invokeMethod on a Statically defined Object :-
hello world via invokeMethod

Initially, we call printMessage with a Map that contains an empty String under the key "message". However, prior to the next call to printMessage we declare some before advice to be applied to printMessage that changes the message to "hello world". Next, we apply some after advice to output a status message. We can set a breakpoint in invokeMethod to see clearly see what is happening.

"invokeMethod" checks for a Closure to execute with the current method name in the before and after maps. If it finds one - it executes it at the appropriate point.

This trivial example demonstrates principles that can be readily applied to more complex, real world situations.

Iteration 2: Around we go

So far, so straight forward. For the next iteration we'll add in some around advice. Around advice is a more powerful advice type, in that it can modify the flow of the application - it has the power to decide whether or not our target method even get's executed. As such, it is an excellent place to implement security features.

Define the infrastructure

We need to modify our infrastructure to cope with the new Advice type. We don't need to change AopDetails. When designing AopDetails we used Expando as the base class. Expando is a special Groovy Object that leverages similar MOP trickery to provide an expandable Object. We need to add a proceed() method to AopDetails so that our Around advice can call the target method, with Expando, this is no problem. Let's take a look at our new AopObject:

We've added a map to hold our around advice Closures. The continueWith Closure calls the target method, and we temporarily add it to our AopDetails Object as the proceed method. If a suitable around advice exists we call it, otherwise we continue directly to our target method.

Another example in action

Let's make our example a little more real world this time. We will attempt to simulate a web controller.

Our web controller implements a "save" method, and we've mocked up a model Object using powerful Groovy features (Expando and Closures). We've added two functions to our model - params, which loads parameters from the request and save which "saves" the model. As the save action may be performing a sensitive update, it might be a good idea to introduce some security features around it.

The Runner class mocks up Request and Session Objects using Maps. We initially call save on the controller without any security features, but for the subsequent call we insert some around advice that checks the user id stored in the session. Finally, we demonstrate that if the user id is correct the original method can indeed proceed. The output is as follows: -

Attempting initial save : no AOP security:-
Saved model 10 successfully


Attempting save with AOP security & incorrect id:-
Sorry you do not have permission to save this model (10)


Attempting save with AOP security & correct id:-
Saved model 10 successfully

The screenshot shows the debugger at a breakpoint inside the around security Closure in Runner.groovy.

Iteration 3: Reusable methods

With Closures we can define methods independently from Classes. With the MOP, as we have seen with Expando, we can insert those free methods into multiple classes. Heck, we can even inject different methods into different Objects of the same class. This level of modularity is much more fine-grained than standard Java allows. Most of us Java developers are used to injecting services via Object Interface implementations, and we typically use a lot of XML to achieve this!

Define the infrastructure

We can expand our AOP-like infrastructure classes to allow the insertion and deletion of methods from our Objects. We need two new Classes at either end of the inheritance hierarchy to do this. AopIntroductions, which allows us to introduce new methods to our Objects, becomes our base class (AopObject should now extend AopIntroductions). AopRemovals, which prevents calls to specified methods from being executed, is the new top-level class.

Let the games begin!

Now we can have some real fun! We are able to leverage instance methods as reusable components. We can define generic methods in one place and mix them into our Objects across multiple Class types while limiting access to sensitive methods on a per Object basis.

Let's refactor our controller from the previous example by removing the save method. We'll create a CrudOperations class that will be a holder class for crud methods. We'll then inject the methods from our CrudOperations class into our web controller.

Notice the call to the constructor of CrudOperationsMixin, this is where the magic happens!

In the class above we have defined a Map of method references. We're injecting each of those references by their key into the host Object (our web controller).

The beauty is we can still apply around, before and after advice types to our newly added methods! Runner.groovy from the previous example can run as is. However, we'll add another twist by disabling the save method at runtime in a new Class.

Groovy supports operator overloading and the List class uses the left shift operator ("<<") to append new Objects to the List.

The output from running Runner.groovy is as follows:

Attempting initial save : no AOP security:-
save operation
Saved model 10 successfully


Attempting save with AOP security & incorrect id:-
Sorry you do not have permission to save this model (10)


Attempting save with AOP security & correct id:-
save operation
Saved model 10 successfully


Attempting save with save method removed (perhaps for security reasons):-
Caught NoSuchMethodException

In the debugger screenshot below I've highlighted the StackTrace that shows the path from our web controller, where the call to save was made, to our mixins class where the save method actually resides.

From Groovy to Ruby : Alternatives for train drivers

If Groovy isn't to your taste, we could re-implement the above examples in the dynamic language of your choice. For example in Ruby we could override the method_added hook rather than invokeMethod. "method_added" is called when a new method is added to an class. As the methods are added to our Object we can proxy them swapping them out for an implementation that inserts before, after, around advice via alias_method. Even Javascript, once the bane of every web developer's life, has powerful idioms allowing AOP to be implemented easily. There's even a framework for it - AspectJs!

Summary

Dynamic languages like Groovy make implementing custom-AOP infrastructure painless. There is no requirement for byte-code modification, and the infrastructure code base can be kept small so as to be readily understandable by new developers. Groovy's MetaObject-Protocol allows us to mould our Object's behavoiur to suit the problem at hand and first class functions and dynamic typing makes dynamically inserting functionality relatively easy. The examples described here are the first step on the journey towards full AOP functionality. Still left to implement are exception advice, support for the application of multiple advices per method (including advice chaining for arounds) and centralised support for applying Aspects to your Objects. But I hope that I've demonstrated that we can get very far, with relatively little effort, thanks to the power of the Groovy language.

Resources

About the author

John McClean is a software developer living in Dublin, Ireland, where he works for AOL Technologies, Ireland. In his spare time, he develops the Slingshot Application Framework a highly productive environment for developing web-based applications. John maintains a blog where he jots down his thoughts on technical topics of interest.

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Nice Article by John Wilson

That was very clear. I think this is the easisest way of doing AOP in Groovy but it is not the most powerful. With custom MetaClasses you can do AOP in a completely non intrusive manner and you can apply AOP to POJOs as well as POGOs.

Perhaps you might conside a part 2?

John Wilson

Documentation by Horia Muntean

Hopefully this is a sign that the 'new MOP' of Groovy is starting to get some documentation.

Re: Documentation by John Wilson

From a user's point of view the "new MOP" is very close to the old one. (really just the way missing methods and properties are reported). The main changes that the "new MOP" intoduces are designed to be used by the compiler. In fact we have recently discovered that we can pass type information (which is one of the main motivations for the "new MOP") without having to change the API at all.

Re: Nice Article by Jeff Payne

True. Although these examples are very easy to understand, modifying the classes at runtime using object.metaClass instead of at design time doesn't couple your classes to AopObject, etc. POGO/POJOs need not know that their methods are being intercepted. Should be fairly easy to implement an AOP framework to allow for defining advice code (maybe in the form of closures). I'm new to Groovy and glad to see that it has these abilities.

Re: Nice Article by John McClean

Thanks John. Re: custom meta-classes, sounds interesting, I would certainly consider writing a part 2. I wonder if infoQ would consider publishing it?

Re: Nice Article by Martin Gilday

I would certainly be interested in more articles being published here on this topic, and other uses of Groovy.

Re: Nice Article by Matthias K.

First of all, thanks a lot for this article, I was looking into Spring AOP in Grails and found that it's not capable enough (try intercepting closures with Spring...) so this may be the better way instead.

However, has anyone actually tried to run this code? For me it continually recurses into invokeMethod(), resulting in a stack overflow... duh!

Apart from that, there are some things odd or unclear about the code. Why does AopObject inherit from GroovyObjectSupport? It's a Groovy class, thus it already inherits invokeMethod() naturally. GroovyObjectSupport, according to the documentation, is only meaningful when programming in Java.

The proceed field furthermore is assigned a superfluous closure. You can simply assign continueWith to proceed:

callDetails.proceed = continueWith

That's the idea behind closures... they're addressable pieces of code and can be passed around.

Any help appreciated on that recursion problem though... don't know why a call to super.invokeMethod invokes the same overridden method again.

Re: Nice Article by John McClean

Hi Mattias,

The Groovy internals changed significantly in the two years between the original article and your comment, I imagine that is the cause of the infinite recursion.

It's much simpler to do the same thing with Metaclasses these days.

Re: Nice Article by Chris Mead

Again, thanks for the article. It may be old, but it is at the top of related Google searches.

I fixed a couple issues. I am not sure if I am 100% correct as I am only using before and after for now.

1) Add "def proceed" to AopDetails
2) Make sure that your AopObject matches what is onscreen here. I had to remove a method called callInvokeSuper() and simply use "super.invokeMethod(name,args)" instead (which is what is in the screenshot.)

Good luck.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

9 Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2014 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT