kodbasen

Where the compass is spinning around software

Simple Groovy AOP using MOP

Development

I was looking for a simple solution for adding cross cutting concerns to Groovy classes. The most obvious solution was to implement GroovyInterceptable but I wanted a less intrusive solution. After a bit of googling I’ve stumbled across DelegatingMetaClass.

Here is a simple interceptor that works like a around advice. It only logs all method calls for a class.


class Interceptor extends DelegatingMetaClass {
  	Interceptor(final Class cls) {
      	super(cls)
      	initialize()
  	}

  	public Object invokeMethod(Object obj, String method, Object[] args) {
      	String cls = obj.class.simpleName
      	println "before: $cls.$method, args:$args -->"
      	def val = null
      	try {
          	val = super.invokeMethod(obj, method, args)
      	} catch(Exception e) {
          	println "after: $cls.$method, has thrown:$e <--"
          	throw e
      	}
      	println "after: $cls.$method, return value:$val <--"
      	return val;
  	}

  	def static injectIn(Class cls) {
      	cls.metaClass = new Interceptor(cls)
  	}
}

We can now use the Interceptor to trace calls to ArrayList like this:

	Interceptor.InjectIn(ArrayList)

	def list = []
	list << "Joe"

If we run the above it will produce:

	before: ArrayList.leftShift, args:[Joe] -->
	after: ArrayList.leftShift, return value:[Joe] <--

It works width mixin’s too, for example:

	class Person {
		def name

		def sayHello() { println "Hi, my name is $name!"}
	}

	class Dancer {
		def dance() { println "I can dance" }
	}

	class Singer {
		def sing() { println "I can sing" }
	}

We can now create a person who dance and sing. The Interceptor must be injected after the mixin:

	Person.mixin Singer
	Person.mixin Dancer
	Interceptor.injectIn(Person)

	def p = new Person(name: "Jill")
	p.sayHello()
	p.sing()
	p.dance()

This will produce the following output:

	before: Person.sayHello, args:[] -->
	before: Person.println, args:[Hi, my name is Jill!] -->
	Hi, my name is Jill!
	after: Person.println, return value:null <--
	after: Person.sayHello, return value:null <--
	before: Person.sing, args:[] -->
	I can sing
	after: Person.sing, return value:null <--
	before: Person.dance, args:[] -->
	I can dance
	after: Person.dance, return value:null <--

As we can see in the example above the println-method is part of the Groovy object.

18 Aug 2014 #Development #Groovy