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.
Related
I'm very new to Groovy.
Very simple question about the code found in CliBuilder.
http://docs.groovy-lang.org/latest/html/gapi/index.html?overview-summary.html
def cli = new CliBuilder(name:'ls')
cli.a('display all files')
cli.l('use a long listing format')
cli.t('sort by modification time')
def options = cli.parse(args)
assert options // would be null (false) on failure
assert options.arguments() == ['*.groovy']
assert options.a && options.l && options.t
The CliBuilder class behaves as knowing whatever methods we want to call in advance. By what Groovy's feature it can be supported?
This is called Runtime metaprogramming.
If you want to create your own class with "dynamic methods", the easiest way is to implement the GroovyInterceptable interface and add the invokeMethod method to your class.
class Interception implements GroovyInterceptable {
def definedMethod() { }
def invokeMethod(String name, Object args) {
'invokedMethod'
}
}
Whenever a method is called on an instance if the class Interception, invokeMethod is called instead. Note that this is also true for methods actually defined in the class (e.g. definedMethod)
You can use the metaClass to call the actual method like this
class Interception implements GroovyInterceptable {
def definedMethod() { }
def invokeMethod(String name, Object args) {
if (name == "actualMethod") {
return metaClass.invokeMethod(this, name, args)
}
return "invokedMethod: $name($args)"
}
def actualMethod() {
return 'hello there'
}
}
Here a call to actualMethod still goes through invokeMethod, however invokeMethod contains logic to call the actual method.
There are some other ways (see link on top) to acomplish similar behavior, but I found this to be the easiest.
Note that runtime metaprogramming is incompatible with #CompileStatic unless you add a TypeCheckingExtension to mitigate this.
Run Example
Why do I need to use System.out.println instead of println when I use GroovyInterceptable?
For example if I am coding in a Groovy file I can just print to the console by typing:
println "Printing to Console"
But if I want to print here:
class Test implements GroovyInterceptable {
def sum(Integer x, Integer y) { x + y }
def invokeMethod(String name, args) {
System.out.println "Invoke method $name with args: $args"
}
}
def test = new Test()
test?.sum(2,3)
I have to use System.out.println in that method, or else I get a StackOverflowError. Why?
UPDATE:
Thanks to #Dan Getz for the answer below I know why it happens with the GroovyInterceptable class now. Does anyone know if there are other class implementations in Groovy where this issue could arise?
This is because your class Test implements the GroovyInterceptable interface, which according to the docs, is
used to notify that all methods should be intercepted through the invokeMethod mechanism of GroovyObject.
This isn't just methods that have been defined on your class. Try:
test?.total(2,3)
You'll see that it returns
Invoke method total with args: [2, 3]
The call to println inside invokeMethod is thus understood as a call to this.println, just like a call to sum would be. But this.println just calls invokeMethod again, because you implemented GroovyInterceptable, and so on.
This wouldn't happen if you didn't implement GroovyInterceptable. For example, running the following code
class Test {
def sum(Integer x, Integer y) {
println "Let's sum!"
x + y
}
}
def test = new Test()
test?.sum(2,3)
Will output
Let's sum!
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
}
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.