adding metamethods to pojos from java

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

adding metamethods to pojos from java

Hi, it's stubborn me again :-)

I have a POJO (let's call it `Context`) from a big Java library.
This library has been historically used only from Java, but I've been wanting to add Groovy scripting to it.

The Context class works similarly to a Map: it has a few `get(Object key)` and `set(Object key, Object value)` methods.
I've spent many days trying to provide a Groovy syntax, so that users of the class can say `ctx.prop= val` instead of calling `ctx.put("foo", val)`

Context it's not a Map, it *will not* be a Map, it *will not* be a GroovyObject, etc.
(I don't want to add any Groovy dependency to the project, only to the add-on module that I'm writing)

I know perfectly well how to do it *from Groovy code*, something like:
(1)   Context.metaClass.getProperty = { String k -> return delegate.get(k) }

Yes, adding the above in a Groovy script works for POJOs (despite some doubts by Jochen a few days ago :-) )

But for some reason (call my stubborn all you want), I want to do it *from Java*, in the code that sets up the *optional* Groovy module for this library.
I've tried dozens of combinations using tricks such as creating an ExpandoMetaClass and calling `registerInstanceMethod` with a Closure, or with a
MetaMethod, and setting the MetaClass in the registry.
Or using `InvokeHelper.setProperty` in its different forms.
I've done all kinds of initializingRegisteringGettingSettingMetaDoingCalling... nothing works.

To be fair, I can add new methods with the equivalent of `expandoMCforContext.registerInstanceMethod("someMethod", someClosure)`
and then doing  `ctx.someMethod()` from Groovy will work!

But it won't work for `get/setProperty`.

I've also tried an extension module, which works nicely for `someStupidMethod()`, but not for `get/setProperty`
Let's be clear: if I add for example `getProperty(String)`, I can call it as long as I do `ctx.getProperty("foo")` but that's not the idea! I want to
say ``

I've gone the length of step-debugging the code to see what happens with the groovy way of doing things, as given in (1), but I haven't been able to
reproduce it from Java.

1) I would like to know if there's SOME WAY to do it from Java which is not overly complicated (I've googled and googled and found zero examples).

2) For the time being, I've humiliated myself and done the following, and I'd also like to know if it's a "proper" way:
I created a short groovy script `ContextMagic.groovy` which I put under the jar resources, similar to
    def mc= Context.metaClass
    mc.getProperty= { String name -> return delegate.get(name) }
    mc.setProperty= { String name, Object val ->  delegate.put(name, val) }

And then, from the initializing Java code I do
    ClassLoader thisCL= this.getClass().getClassLoader();
    URL scriptURL= thisCL.getResource("path/to/ContextMagic.groovy");
    GroovyShell sh= new GroovyShell();
    sh.evaluate(new GroovyCodeSource(scriptURL));

Is the above correct? It works, but is this a proper way to do it?
Could the meta methods be "forgotten" after a while if classes/classloaders/whatever gets unloaded by garbage collecting? (how can I prevent that?)

sorry for the long and frustation-filled mail :-)