简体   繁体   中英

Adding IntegerMetaClass to GroovyShell dynamically

I would like to use a custom IntegerMetaClass only in a given GroovyShell context.

The reason why is to not pollute the whole runtime with my potentially 'disturbing' IntegerMetaClass .

It works like a charm when I put my IntegerMetaClass.java implementation in the magic package groovy.runtime.metaclass.java.lang . But, when I try to add it manually to an intermediate GroovyClassLoader, it stops working.

// Pseudo-code without try catch, etc
// Within my eval factory
GroovyClassLoader gcl = new GroovyClassLoader(getClass().getClassLoader());
URL url = getClass().getClassLoader().getResource("groovy/runtime/metaclass/java/lang/IntegerMetaClass.groovy"); // I rename it to .groovy file
GroovyCodeSource gcs = new GroovyCodeSource(url);
Class<?> clazz = gcl.parseClass(gcs);
// clazz is not null here and equals to what I expect:
//   Class<groovy.runtime.metaclass.java.lang.IntegerMetaClass>

// Now trying to use it in a groovy shell
GroovyShell gs = new GroovyShell(gcl);
gs.evaluate("10.minutes"); // Where .minutes is part of my IntegerMetaClass
// Fail with an NoSuchProperty exception

Do I miss something to do on GroovyClassLoader more than just 'parsing' the MetaClass ? Anywhere else ?

Update1:

As mentioned above, IntegerMetaClass.minutes lookup is working when I put it directly in my java sources classpath.

package groovy.runtime.metaclass.java.lang;

import groovy.lang.DelegatingMetaClass;
import groovy.lang.MetaClass;

public class IntegerMetaClass extends DelegatingMetaClass {
    public IntegerMetaClass(Class<Integer> delegate) {
        super(delegate);
    }

    public IntegerMetaClass(MetaClass delegate) {
        super(delegate);
    }

    @Override
    public Object getProperty(Object object, String property) {
        if ("minutes".equals(property)) {
            Integer q = (Integer) object;
            return new Minutes(q);
        }
        return super.getProperty(object, property);
    }
}

Update2:

A possible but not satisfying solution :

Adding the following just after the gcl.parseClass call

Constructor<?> constructor = clazz.getConstructor(Class.class);
DelegatingMetaClass dmc = (DelegatingMetaClass) constructor.newInstance(Integer.class);
dmc.initialize();
InvokerHelper.getMetaRegistry().setMetaClass(Integer.class, dmc);

But this solution has to maintain a sort of 'mapping' between MetaClass sources and original targeted class to support more than Integer ...

You can load the class with the GroovyShell classloader:

    GroovyShell gs = new GroovyShell()
    gs.loader.loadClass('groovy.runtime.metaclass.java.lang.IntegerMetaClass')
    gs.evaluate("10.minutes")

Note: I got a java.lang.IncompatibleClassChangeError when IntegerMetaClass was a groovy file but no issues when it was java. That could just be related to my environment setup

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM