I'm trying to intercept all calls to properties on a Groovy class. Since this did not work as expected, I created the following example:
class TestClass {
def getProperty(String key) {
println "getting property: " + key
}
def invokeMethod(String method, args) {
println "invoking method: " + method
}
def getFoo() {
return 1
}
}
tc.foo // 1
tc.getFoo() // 2
1) does the right thing, that is getProperty is called. However, 2) works (i.e. 1 is returned) but neither getProperty nor invokeMethod is called.
Is there a way to intercept the getfoo() call as well?
Stefan
I wrote an article a couple of months ago. You can read it here.
Try this code :
TestClass.metaClass.invokeMethod = {
def metaMethod = delegate.metaClass.getMetaMethod(method,args)
println "executing $method with args $args on $delegate"
return metaMethod.invoke(delegate,args)
}
I had to modify the code in a previous answer a bit to get what I think you want:
TestClass.metaClass.invokeMethod = {method, args ->
def metaMethod = TestClass.metaClass.getMetaMethod(method,args)
println "executing $method with args $args on $delegate"
metaMethod.invoke(delegate,args) // could result in NPE
}
Then executing
tc.foo
tc.getFoo()
Results in:
getting property: foo // println output
null // getProperty return is null
executing getFoo with args [] on TestClass#655538e5 // invokeMethod output
1 // metaMethod invocation
The problem is that there are two different kinds of paths how a request is handled is used here. For asking properties the getProperty method is called before we go into the meta class - if you overwrite getProperty you have to do the meta class call yourself actually. In case of invokeMethod it is normally asked after the meta class has been asked. Since the meta class will respond to your asking for getFoo(), invokeMethod will not be asked at all. If you let the class implement GroovyInterceptable then invokeMethod is asked first, the same way as getProperty. That also explains why the ways using the meta class instead do work.
Related
I tried some (runtime) metaprogramming with Groovy. I went ahead and implemented the getProperty method of GroovyInterceptable. But now I found out that this only works when getting a property on the object from the outside. When getting an property from inside a method in that class my getProperty method does not called (see the code example below).
Now, is this expected and has it always been like this? A collegue told me this used to be different in the past (from what he remembers). Is there another way where both property reads from inside and outside would call my getProperty method?
class SomeGroovyClass implements GroovyInterceptable {
def foo
SomeGroovyClass() {
foo = "ctor"
}
def getProperty(String name) {
if (name == "foo") {
return "blub"
} else {
return "foo"
}
}
def test2() {
System.out.println "foo from inside via method call: " + this.foo
}
}
def someGroovyClass = new SomeGroovyClass()
System.out.println "foo from outside: " + someGroovyClass.foo
someGroovyClass.test2()
Output is
foo from outside: blub
foo from inside via method call: ctor
One way to force the use of the getProperty method is to force the type used to access this. Changing your test2 method to:
def test2() {
println "foo from inside via method call: " + ((GroovyInterceptable) this).foo
}
results in:
~> groovy solution.groovy
foo from outside: blub
foo from inside via method call: blub
alternatives to forcing the type:
def test2() {
def me = this as GroovyInterceptable
println "foo from inside via method call: " + me.foo
}
and
def test2() {
GroovyInterceptable me = this
println "foo from inside via method call: " + me.foo
}
I can grok where the groovy compiler is coming from...there really is no way for it to know which handling of the foo property you are looking for unless you are explicit about it.
I believe the main purpose of the getProperty mechanism is to cover access of non-existent properties. This makes defaulting to the existing property when one is available a reasonable choice in my opinion and they still leave the door open as you can always force things using typed access like in the above.
A class named InterceptorTest implementing GroovyInterceptable,which could be an interceptor,has its invokeMethod overrided as follows:
class InterceptorTest implements GroovyInterceptable{
def invokeMethod(String name,args){
println "intercepting call! " //println a short message
}
}
I thought class InterceptorTest to be an interceptor would delegate all method calls to invokeMethod,so the following should work:
def interceptor=new InterceptorTest()
println interceptor.work()
Unfortunately, I got a StackOverflowError,seemingly at the body of invokeMethod,but I had no clue why it happened.
As a counterpart, I altered the function body,not println but just return message:
class InterceptorTest implements GroovyInterceptable{
def invokeMethod(String name,args){
"intercepting call! " //return a short message
}
}
then it worked fine:
def interceptor=new InterceptorTest()
println interceptor.work() //get "intercepting call!"
But WHY?
According to the documentation, "println" is actually a method in the class, injected by Groovy. Therefore, you achieve infinite recursion by trying to call it within the invokeMethod function.
We cannot use default groovy methods like println because these methods are injected into all Groovy objects so they will be intercepted too.
I need to pass a list of methods to execute inside the class with "closure way", see the code bellow
class A{
def m1(){
println "Method1"
}
def m2(){
println "Method1"
}
def commands = { closure->
println "before"
closure.call()
println "after"
}
}
A a = new A()
a.commands{
println "before execute method m1"
m1() //A need to execute m1 method of the class A
println "after execute method m1"
}
When I comment the m1() the output is
before
before execute method m1
after execute method m1
after
otherwise throw the exception MissingMethodException: No signature of method for method m1()
So, it does not recognize the m1() method as method of class A
Depending on what you are really trying to accomplish, you may want something like this...
class A{
def m1(){
println "Method1"
}
def m2(){
println "Method1"
}
def commands(closure) {
def c = closure.clone()
c.delegate = this
println "before"
c()
println "after"
}
}
The delegate gets an opportunity to respond to method calls that are made inside of the closure. Setting the delegate to this will cause the calls to m1() to be dispatched to the instance of A. You may also be interested in setting the resolveStrategy property of the closure. Valid values for resolveStrategy are Closure.DELEGATE_FIRST, Closure.OWNER_FIRST, Closure.DELEGATE_ONLY, Closure.OWNER_ONLY. The owner is the thing that created the closure and cannot be changed. The delegate can be assigned any object. When the closure makes method calls the methods may be handled by the owner or the delegate and the resolveStrategy comes into play in deciding which to use. The names DELEGATE_ONLY, OWNER_ONLY, DELEGATE_FIRST and OWNER_FIRST I think are self explanatory. If you need any more info, let me know.
I hope that helps.
I want to create a wrapper that traps a particular exception and retries for all methods in a large (100+ methods) interface. I have the retry code working no worries, but I can't figure out how to hook up an implementation of the interface without cut'n'paste into all the methods.
I tried to use a missing method handler but that meant that I couldn't have it implement the interface. Abstract is obviously out as I won't be able to instantiate it.
I'm hoping for a better solution than creating the class as a template on the fly but I'm willing to do that.
Have you tried overriding invokeMethod for the interface?
YourInterface.metaClass.invokeMethod = {String name, args ->
def result
println "Calling method $name"
try{
result = metaClass.getMetaMethod(name, args).invoke(delegate, args)
}catch(YourException | AnyOtherException | Exception e){
println "Handling exception for method $name"
result = //Call retry code here
}
println "Called method $name"
result
}
Overriding invokeMethod works as as interceptor for all the method calls in the interface. Handle the exception for each method and return the success result.
I tried to use #dmahapatro's example but I kept getting IllegalArgumentException. I eventually realised that it only happened for mixin methods (the method shows the signature of the mixin). Instead of invoke() I needed to use doMethodInvoke() to get the appropriate type coersion.
errorProneInstance.metaClass.invokeMethod = { String name, args ->
def result
def method = delegate.metaClass.getMetaMethod(name, args)
while(true) {
try {
result = method.doMethodInvoke(delegate, args)
break
} catch (AnnoyingIntermittentButRetryableException e) {
print "ignoring exception"
}
}
result
}
At runtime I'm grabbing a list of method names on a class, and I want to invoke these methods. I understand how to get the first part done from here:
http://docs.codehaus.org/display/GROOVY/JN3535-Reflection
GroovyObject.methods.each{ println it.name }
What I can't seem to find information on is how to then invoke a method once I've grabbed its name.
What I want is to get here:
GroovyObject.methods.each{ GroovyObject.invokeMethod( it.name, argList) }
I can't seem to find the correct syntax. The above seems to assume I've overloaded the default invokeMethod for the GroovyObject class, which is NOT the direction I want to go.
Once you get a MetaMethod object from the metaclass, you can call invoke on it. For example:
class MyClass {
def myField = 'foo'
def myMethod(myArg) { println "$myField $myArg" }
}
test = new MyClass()
test.metaClass.methods.each { method ->
if (method.name == 'myMethod') {
method.invoke(test, 'bar')
}
}
Alternatively, you can use the name directly:
methodName = 'myMethod'
test."$methodName"('bar')
Groovy allows for dynamic method invocation as well as dynamic arguments using the spread operator:
def dynamicArgs = [1,2]
def groovy = new GroovyObject()
GroovyObject.methods.each{
groovy."$it.name"(staticArg, *dynamicArgs)
}
Reference here
Question answered here.