views:

420

answers:

2

Is it possible to intercept all the methods called in a application? I'd like to do something with them, and then let them execute. I tried to override this behaviour in Object.metaClass.invokeMethod, but it doesn't seem to work.

Is this doable?

+1  A: 

Have you looked at Groovy AOP? There's very little documentation, but it allows you to define pointcuts and advice in a conceptually similar way as for AspectJ. Have a look at the unit tests for some more examples

The example below will match all calls to all woven types and apply the advice before proceeding:

// aspect MyAspect
class MyAspect {
  static aspect = {
    //match all calls to all calls to all types in all packages
    def pc = pcall("*.*.*")

    //apply around advice to the matched calls
    around(pc) { ctx ->
      println ctx.args[0]
      println ctx.args.length
      return proceed(ctx.args)
    }
  }
}
// class T
class T {
  def test() {
    println "hello"
  }
}
// Script starts here
weave MyAspect.class
new T().test()
unweave MyAspect.class
Rich Seller
+1  A: 

First of all, overriding Object.metaClass.invokeMethod doesn't work because when Groovy tries to resolve a method call for a type X, it checks the metaClass of X, but not the metaClass of its parent class(es). For example, the following code will print "method intValue intercepted"

Integer.metaClass.invokeMethod = {def name, def args ->
  System.out.println("method $name intercepted")
}

6.intValue()

// Reset the metaClass  
Integer.metaClass = null

But this code will not:

Object.metaClass.invokeMethod = {def name, def args ->
  System.out.println("method $name intercepted")
}

6.intValue()

// Reset the metaClass  
Object.metaClass = null

Your question was "Is it possible to intercept all the methods called in a application?", but could you be a bit more precise about whether you want to:

  • Intercept calls to Groovy methods, Java methods, or both
  • Intercept calls to only your Groovy/Java methods or also intercept calls to Groovy/Java library classes

For example, if you only want to intercept calls to your Groovy classes, you could change your classes to implement GroovyInterceptable. This ensures that invokeMethod() is invoked for every method called on those classes. If the nature of the interception (i.e. the stuff you want to do before/after invoking the called method) is the same for all classes, you could define invokeMethod() in a separate class and use @Mixin to apply it to all your classes.

Alternatively, if you also want to intercept calls to Java classes, you should check out the DelegatingMetaClass.

Don
I'd like to be able to intercept both Groovy and Java methods calls.
Geo
Then you should use DelegatingMetaClass or a full-blown AOP framework such as groovy-aop or AspectJ
Don
You can't intercept *all* method calls with a metaClass, because Java methods called from Java wont go through Groovy dispatch. So if you have a Java class calling another Java class, you can't intercept that call without using some sort of AOP or something like that.
noah