Interceptors in Groovy

The codehaus website has been down all day, which is more than a little painful when one has an exam on Groovy tomorrow and there is not even a recommended text book for the course (and, as I have discovered, the lecture notes seem to have more than a few errors and omissions).

So here is my attempt to explain to myself (and anybody who stumbles across this) how method interception works in Groovy.

First of all we have to implement the Interceptor interface. This has three methods:

  • beforeInvoke()
  • doInvoke()
  • afterInvoke()

The signatures are:

Object beforeInvoke(Object obj, String methodName, Object[] args)

boolean doInvoke(void)

(this must return true if the method is to be invoked)

Object afterInvoke(Object obj, String methodName, Object[] args, Object result)

The created interface then has to reference to the class being intercepted via use of the ProxyMetaClass eg

def myProxy = ProxyMetaClass.getInstance(myClass)
myProxy.interceptor = new MyInterceptor()

and then via the use keyword

myProxy.use {
def myObj = new myClass()
myObj.someInvocation()
}

Here’s a toy but working example:

Integer.metaClass {
getFibonacci {
fib = delegate
def fibno = 0
while (fib) {
fibno += fib
fib–
}
return fibno
}
getSquare {
return delegate * delegate
}
getFivepercent {
return delegate * 0.05
}

}

class FibInterceptor implements Interceptor {

def beforeInvoke(Object obj, String methodName, Object[] args) {
print “beforeInvoke: Value is $obj, method is $methodName, arguments are $args”
println()
}

boolean doInvoke() { println “In action…”
return true}

def afterInvoke(Object obj, String methodName, Object[] args, Object res) {
println “After: value is $obj and the result was $res”
return res
}
}

def myProxy = ProxyMetaClass.getInstance(Integer)
myProxy.interceptor = new FibInterceptor()

Integer x = 2
myProxy.use {
println “X is $x and its Fibonacci number is ${x.fibonacci}, its square is ${x.square} and five percent of x is ${x.fivepercent}”
}

Giving output:

beforeInvoke: Value is 2, method is asBoolean, arguments are []
In action…
After: value is 2 and the result was true
beforeInvoke: Value is 0, method is plus, arguments are [2]
In action…
After: value is 0 and the result was 2
beforeInvoke: Value is 2, method is previous, arguments are []
In action…
After: value is 2 and the result was 1
beforeInvoke: Value is 1, method is asBoolean, arguments are []
In action…
After: value is 1 and the result was true
beforeInvoke: Value is 2, method is plus, arguments are [1]
In action…
After: value is 2 and the result was 3
beforeInvoke: Value is 1, method is previous, arguments are []
In action…
After: value is 1 and the result was 0
beforeInvoke: Value is 0, method is asBoolean, arguments are []
In action…
After: value is 0 and the result was false
beforeInvoke: Value is 2, method is multiply, arguments are [2]
In action…
After: value is 2 and the result was 4
beforeInvoke: Value is 2, method is multiply, arguments are [0.05]
In action…
After: value is 2 and the result was 0.10
X is 2 and its Fibonacci number is 3, its square is 4 and five percent of x is 0.10