can not override invokeMethod inherited from GroovyObject - groovy

Since every Groovy object implements GroovyObject interface, i would try to override invokeMethod(), here is my test :
class MyGrrovyClass {
static test(){
println 'i am in test'
}
Object invokeMethod(String name, Object args){
log.info('method intercepted')
def metaClass = InvokerHelper.getMetaClass(this)
def result = metaClass.invokeMethod(this, name, args)
return result
}
public static void main(String[] args) {
test()
}
}
but it seems doesn't work, i've never seen log message in my console
My second question is : GroovyInterceptable is the subinterface of GroovyObject, what the difference between that i override directly invokeMethod of GroovyObject and i implement invokeMethod of GroovyInterceptable interface?
thanks

According to the documentation (http://groovy.codehaus.org/Using+invokeMethod+and+getProperty) you must implement GroovyInterceptable to intercept existing methods I think this answers your first and second questions!
I made some slight changes to get your sample class working although was surprised to see that my println was intercepted but not System.out.println - this meant that I was getting a stack overflow because I originally had a simple println in the invokeMethod and that was getting recursively called.
class MyGrrovyClass implements GroovyInterceptable {
def test(){
println 'i am in test'
}
def invokeMethod(String name, args){
System.out.println('method intercepted: '+ name)
def result= metaClass.getMetaMethod(name, args).invoke(this, args)
}
}
def mgc= new MyGrrovyClass()
mgc.test()

Related

Groovy CliBuilder: any method defined?

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

Access a property inside invokeMethod() implemented in a trait

The following Groovy trait implements the GroovyInterceptable interface to allow execution of code before and after method calls.
trait Bar implements GroovyInterceptable {
def bar = "bar"
#Override
invokeMethod(String name, Object args) {
System.out.println(bar)
metaClass.getMetaMethod(name, args).invoke(this, args)
}
def doSomething() {
}
}
The following class implements the trait Bar.
class Foo implements Bar {
}
Have a look at the following code.
def foo = new Foo()
foo.doSomething()
The call to doSomething() is being intercepted by invokeMethod(). A java.lang.StackOverflowError occurs because accessing the property bar inside invokeMethod() implicitly makes a call to the getter of bar which in turn is intercepted by invokeMethod() just trying to access bar again.
How can I access a class property inside invokeMethod without calling this property's getter or setter?
In combination with the trait using this.#bar to access the property does not work.
The code metaClass.getMetaMethod(name, args).invoke(this, args) to invoke the intercepted method could be incorrect although it works when using the trait logic directly inside a class.
Edit for Solution:
The accepted answer contributed by user Opal works like a charm in a script environment. Since the trait is part of a larger project and defined in its own file I made it work like this:
package com.example.project
trait Bar implements GroovyInterceptable {
def bar = "bar"
#Override
invokeMethod(String name, Object args) {
System.out.println(this.com_example_project_Bar__bar)
metaClass.getMetaMethod(name, args).invoke(this, args)
}
def doSomething() {
}
}
It turns out that there's no need to use # for direct field access:
trait Bar implements GroovyInterceptable {
def bar = "bar"
#Override
invokeMethod(String name, Object args) {
System.out.println(Bar__bar)
metaClass.getMetaMethod(name, args).invoke(this, args)
}
def doSomething() {
}
}
class Foo implements Bar {
}
def foo = new Foo()
foo.doSomething()

How to use PowerMockito to verify super method is called

I am testing a legacy code that use inheritance method. I am trying to mock super-method
to verity if the super-method is being call or not.
#RunWith(PowerMockRunner.class)
public class HumanTest {
#Test
public void test() throws NoSuchMethodException, SecurityException {
// 1. arrange
Human sut = PowerMockito.spy(new Human());
PowerMockito.doNothing().when((SuperHuman) sut).run(); // SuperHuman is the parent class
// 2. action
sut.run();
// 3. assert / verify
}
}
public class Human extends SuperHuman {
#Override
public void run() {
System.out.println("human run");
super.run();
}
}
public class SuperHuman {
public void run() {
System.out.println("superhuman run");
}
}
I was expecting that "human run" will be printed. But the actual result was none printed.
PowerMockito.doNothing().when((SuperHuman) sut).run(); // SuperHuman is the parent class
This won't work in your case since PowerMockito will mock method of Human even if you made cast.
I checked your code example and could say that it is possible to suppress invocation of super class method with:
Method toReplace = PowerMockito.method(SuperHuman.class, "run");
PowerMockito.suppress(toReplace);
But it seems that method replacment feature does not work for methods of super class:
createPartialMock should support mocking overridden methods in super classes.
So this does not work:
PowerMockito.replace(toReplace).with(new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Method of superclass has been invoked !");
return null;
}
});
But still you should be able to verify invocation of super method let's say indirectly, by mocking other classes which are invoked in super method only.
For instance check that System.out.println was invoked with "superhuman run" or something like this.

Groovy MOP invokeMethod

I am trying to understand how invokeMethod intercepts method calls in Groovy. I can't seem to get the most basic of examples working though.
class Person implements GroovyInterceptable {
def invokeMethod(String name,args) {
println "called invokeMethod $name $args"
}
def greet() {
println "Hello from greet()"
}
}
def p = new Person()
p.greet()
If I try and run this example I get the following error. What am I missing?
Caught: java.lang.StackOverflowError
java.lang.StackOverflowError
at Person.invokeMethod(Person.groovy:4)
at Person.invokeMethod(Person.groovy:4)
at Person.invokeMethod(Person.groovy:4)
...
You need to invoke the actual method from invokeMethod after the interception.
class Person implements GroovyInterceptable {
def invokeMethod(String name,args) {
System.out.println "called invokeMethod $name $args"
metaClass.getMetaMethod(name, args).invoke(this, args)
}
def greet() {
System.out.println "Hello from greet()"
}
}
def p = new Person()
p.greet()
And yes you are correct about println. Have to use SOP.

Groovy: Got StackOverflowError when override invokeMethod with a closure

I'm trying to enhance my Grails project with Groovy AOP approach. However I always got StackOverflowError if I override invokeMethod with a closure. Here is my test code, I can reproduce the error with groovy 2.1.3, thanks!
class A implements GroovyInterceptable
{
void foo(){
System.out.println( "A.foo");
}
}
class B extends A
{
void foo(){
System.out.println( "B.foo");
super.foo();
}
}
def mc = B.metaClass;
mc.invokeMethod = { String name, args ->
// do "before" and/or "around" work here
try {
def value = mc.getMetaMethod(name, args).invoke(delegate, args)
// do "after" work here
return value // or another value
}
catch (e) {
// do "after-throwing" work here
}
}
B b = new B();
b.foo();
Looks like, if you have a call to super() then metaClass uses the cache to find the method and throws a StackOverflow eventually. In that case if you metaClass A instead of B, it all works fine.
def mc = A.metaClass
I can infer it this way, class implementing GroovyInterceptable directly should override invokeMethod.
#Source MetaClassImpl

Resources