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.
Related
I just cannot understand how to get closures to work as I would expect them. For example let's say I have a class
class Bar {
public void greeting(String name) {
println "Hello: ${name}"
}
}
When I delegate Bar in a Closure as such:
class Foo {
void helloBob(){
bar {
greeting("Bob")
}
}
def bar(#DelegatesTo(value = Bar, strategy = Closure.DELEGATE_ONLY) Closure cl) {
cl.rehydrate(new Bar(), this, this)
cl.call()
}
static void main(String[] args) {
new Foo().helloBob()
}
}
I get the stacktrace when running:
Exception in thread "main" groovy.lang.MissingMethodException: No signature of method: Foo.greeting() is applicable for argument types: (String) values: [Bob]
Possible solutions: getAt(java.lang.String)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:70)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:76)
at
at ...
The: No signature of method: Foo.greeting() makes no sense to me because it should be calling Bar.greeting() as it is in a Closure which has #DelegatesTo(value = Bar. Why is the closure not referring to Bar.greeting? How do I get it to do that? My IDE (IntelliJ) seems to think it's Bar.greeting which is what I want, but when I run it I get a stacktrace.
EDIT
Weirdly enough if I remove a whole bunch of type information it then seems to work with this:
Bar bar(closure) {
def bar = new Bar()
closure.delegate = bar
// closure.rehydrate(bar, this, this) // this causes error why?
closure.call()
return bar
}
With the IDE happy and all. I also don't understand why I cannot use rehydrate it seems to cause an error, yet setting the delegate manually is fine.
Rehydrate doesn't modify the closure - it returns a new copy of it. So call the new closure.
cl.rehydrate(new Bar(), this, this).call()
Don't forget to set the resolveStrategy to what you are claiming in the annotation.
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 am running into a problem while trying to use property access in Groovy. Take the following class:
class Foo {
Map m = [:]
String bar
void getProperty(String name) {
m.get name
}
def setProperty(String name, value) {
m.set name, value
}
String getBarString() {
return bar // local access, does not go through getProperty()
}
}
It overrides the getter and setter to simply place the values into a Map rather than into the object's normal property space. In the abstract this is a bit silly, but imagine that instead of placing the data into a map we were persisting it to a DB or something else useful.
Unfortunately, the following code now won't work:
foo = new Foo()
foo.bar = "blerg" // using foo.bar invokes the setProperty interceptor
assert foo.bar == "blerg" // this will work fine as foo.bar here uses the getProperty interceptor
assert foo.getBarString() == "blerg" // explosion and fire! getBarString accesses bar locally without going through the getProperty interceptor so null will actually be returned.
Certainly there are workarounds for this, setProperty could set both the MetaProperty and the Map value, etc. However, all of the strategies I've thought of require a lot of extra caution from the programmer to make sure they are accessing class properties in the exact way that they mean to.
Furthermore, some of the built in awesome stuff in Groovy (like #Delegate for example) uses direct MetaProperty access rather than going through getProperty so the following would never work:
class Meep {
String getMyMeep() {
return "MEEP!!!"
}
}
class Foo {
Map m = [:]
String bar
#Delegate Meep meep
void getProperty(String name) {
m.get name
}
def setProperty(String name, value) {
m.set name, value
}
String getBarString() {
return bar
}
}
foo = new Foo()
foo.meep = new Meep() // uses setProperty and so does not place the Meep in the Map m
foo.getMyMeep()
A null pointer exception is thrown on the last line as #Delegate uses MetaProperty direct access (effectively this.meep.getMyMeep() rather than the getProperty interceptor. Unfortunately 'meep' is null, though getProperty('meep') would not be.
In short what I'm looking for is a strategy to solve the following criteria:
intercept property read/write to enable automatic alternative data storage
transparent or near-transparent interface for other developers (I don't want to make other folks' lives significantly harder)
allow for local access of variables using the MetaProperty/this/etc. access methods
Thanks in advance!
You could use
foo.#meep = new Meep()
in order to directly access properties bypassing setProperty method.
That doesn't completely solves your problem though as the foo.meep still triggers set/getProperty.
Another way you could go about is by using getter and setter of the meet directly, i.e.
foo.setMeep(new Meep())
So, one unified way would be to define all of the variables as private and use get/set*PropertyName*
By using an AST Transformation I can do the following:
walk a class's structure and rename all local fields to something like x -> x.
add a getter/setter for each renamed field like this
def get_x_() {
x
}
...in order to access x as a field rather than as a Groovy property
- now apply the transformation on the following class
class Foo {
def x
def y
Map m = [:]
#Delegate Date date // for testing if non-local fields work
def getProperty(String name) {
if (this.respondsTo("get__${name}__")) // if this is one of our custom fields
return "get__${name}__"()
"get${Verifier.capitalize(name)}"() // pass to specific getter method
}
void setProperty {
if (this.respondsTo("set__${name}__")) {
"set__${name}__"(value)
m[name] = value
if (name == "x") y = x + 1
return
}
"set${Verifier.capitalize(name)}"(value)
}
}
now run a testing method like this:
public void testAST() {
def file = new File('./src/groovy/TestExample.groovy')
GroovyClassLoader invoker = new GroovyClassLoader()
def clazz = invoker.parseClass(file)
def out = clazz.newInstance()
out.x = 10
assert out.y == 11
out.y = 5
assert out.y == 5
out.x = 2
assert out.m.containsKey('x')
assert out.m.x == 2
assert out.m.y == 3
out.date = new Date()
assert out.time && out.time > 0
}
And everything should work out including m getting updated, date delegate method time getting accessed properly, etc.
-Glenn
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.
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.