How to differenciate between static and dynamic methodMissing and avoid StackOverflowError - groovy

I use methodMissing in a delegate.
What is possible in order to avoid mistake such this:
java.lang.StackOverflowError
just because:
def methodMissing(String methodName, args) {
pritln "did you notice the method name mistake ?"
}
I was thinking of between a mechanism to differenciate static / dynamic (i.e. when .call() is called on the closure)
Any pointers ?

What about this little Groovy script?
def methodMissing(String methodName, args) {
println "in methodMissing"
if (!methodCalled()) {
pritln "did you notice the method name mistake ?"
}
}
def methodCalled(methodName = 'methodMissing') {
Thread.currentThread().getStackTrace().findAll {it.className == this.class.name}.methodName.contains methodName
}
nothingHere()

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

Confused about the invokeMethod method in the Groovy MOP

First look at the following Groovy code:
class Car {
def check() { System.out.println "check called..." }
def start() { System.out.println "start called..." }
}
Car.metaClass.invokeMethod = { String name, args ->
System.out.print("Call to $name intercepted... ")
if (name != 'check') {
System.out.print("running filter... ")
Car.metaClass.getMetaMethod('check').invoke(delegate, null)
}
def validMethod = Car.metaClass.getMetaMethod(name, args)
if (validMethod != null) {
validMethod.invoke(delegate, args)
} else {
Car.metaClass.invokeMissingMethod(delegate, name, args)
}
}
car = new Car()
car.start()
The output is:
Call to start intercepted... running filter... check called...
start called...
According to the Groovy method dispatching mechanism I think the start method in the Car should be called directly instead of being intercepted by the invokeMethod in the Car's metaClass. Why is the start method intercepted by the invokeMethod? How is the invokeMethod invoked when a method is called on an object?
If you can give me some detailed explanations about Groovy method dispatching mechanism(MOP) I will appreciate that.
In short you are not using the standard meta class, so you don't get the standard Groovy MOP.
Car.metaClass.invokeMethod = { will let Car have an ExpandoMetaClass as meta class. This meta class uses the invokeMethod you give in as open block (like you do) to intercept calls. This is very different from defining an invokeMethod in the class itself.

Changing and adding arguments of method before invocation

I want to reproduce the behavior of the default arguments by intercepting the call to some methods. The following code tries to give a default argument when the method display is called without arguments:
class Thing
{
void display(String text)
{
println(text)
}
def invokeMethod(String name, args)
{
if(name == "display" && args.length == 0)
{
metaClass.getMetaMethod(name, ["some text"]).
invoke(this, "some text")
}
else
{
metaClass.getMetaMethod(name, args).
invoke(this, args)
}
}
}
Thing thing = new Thing()
thing.display("stuff") //prints "stuff"
thing.display() //nothing happens
However, this does not work; nothing is printed when no argument is given.
This example is not very useful but I would want to make it work; my next goal is indeed to create methods whose arguments could be given in their name. Example:
Add1And2()// should return 3
Add4And9()// should return 13
Intercepting the call to these nonexistent methods and calling an existing method that would do the addition using the numbers used in the name of the nonexistent methods would make it possible...
Class Thing should implement GroovyInterceptable as below to make invokeMethod work:
class Thing implements GroovyInterceptable {
void display(String text) {
println(text)
}
def invokeMethod(String name, args) {
if(name == "display" && args.length == 0) {
metaClass.getMetaMethod(name).invoke(this, "some text")
} else {
metaClass.getMetaMethod(name, args).invoke(this, args)
}
}
}
Thing thing = new Thing()
thing.display("stuff") //prints "stuff"
thing.display()

can not override invokeMethod inherited from GroovyObject

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()

Groovy methodMissing

I have a closure within an object Foo and inside the closure i define a method called 'myStaticMethod' that I want to resolve once the closure is called outside the object Foo. I also happen to have 'on purpose' a static method within my object Foo with the same name. When I call the closure i set the 'resolve strategy' to DELEGATE_ONLY to intercept the call to myStaticMethod that is defined within the closure.
I tried to achieve that through missingMethod but the method is never intercepted. When i make the Foo.myStaticMethod non static, the method is intercepted. I don't quite understand why this is happening though my resolve strategy is set to DELEGATE_ONLY. having the Foo.myStaticMethod static or not shouldn't matter or I am missing something
class Foo {
static myclosure = {
myStaticMethod()
}
static def myStaticMethod() {}
}
class FooTest {
def c = Foo.myclosure
c.resolveStrategy = Closure.DELEGATE_ONLY
c.call()
def missingMethod(String name, def args) {
println $name
}
}
To solve the problem, I ended up overriding the invokeMethod right before calling the closure in FooTests
Foo.metaClass.'static'.invokeMethod = { String name, args ->
println "Static Builder processing $name "
}
While trying to solve this problem, i discovered a very weird way to intercept missing static methods. Might be useful to some of you in the future.
static $static_methodMissing(String name, args) {
println "Missing static $name"
}
-Ken
Static methods unfortunately aren't intercepted by the closure property resolution. The only way that I know to intercept those is to override the static metaClass invokeMethod on the class that owns the closure, ex:
class Foo {
static myclosure = {
myStaticMethod()
}
static myStaticMethod() {
return false
}
}
Foo.metaClass.'static'.invokeMethod = { String name, args ->
println "in static invokeMethod for $name"
return true
}
def closure = Foo.myclosure
assert true == closure()

Resources