How do I undo meta class changes after executing GroovyShell?
Asked Answered
H

4

13

For example, if I execute a Groovy script, which modifies the String meta class, adding a method foo()

GroovyShell shell1 = new GroovyShell();
shell1.evaluate("String.metaClass.foo = {-> delegate.toUpperCase()}");

when I create a new shell after that and execute it, the changes are still there

GroovyShell shell2 = new GroovyShell();
Object result = shell2.evaluate("'a'.foo()");

Is there a way to undo all meta class changes after executing the GroovyShell? I tried

shell1.getClassLoader().clearCache();

and

shell1.resetLoadedClasses();

but that did not make a change.

Homograph answered 23/10, 2009 at 10:25 Comment(1)
Can I do this with some classloader messing?Homograph
C
13

You can use

GroovySystem.metaClassRegistry.removeMetaClass(String.class);

to revert all changes made to the String meta class.

Alternatively you could only change the meta class of a certain String instance, thus not all instances of String would be affected.

Chiliasm answered 28/10, 2009 at 9:53 Comment(2)
Thanks. But what, if I don't know what meta class changes have been made. Think of an application like the Groovy Web Console, where different users execute Groovy scripts. I want these users not to interfere.Homograph
See my answer below, which solves your problem of not knowing which metaclasses have changed.Stylet
S
4

You can use MetaClassRegistryCleaner too.

Before doing some metaclass changes, you can do

MetaClassRegistryCleaner registryCleaner = MetaClassRegistryCleaner.createAndRegister()
GroovySystem.metaClassRegistry.addMetaClassRegistryChangeEventListener(registryCleaner)

And when you want to reset the metaclass changes to the state they were earlier.

You can do

registryCleaner.clean()  
GroovySystem.metaClassRegistry.removeMetaClassRegistryChangeEventListener(registryCleaner)

This way you can reset all the metaclass changes made during the duration.

Stylet answered 12/2, 2016 at 14:22 Comment(0)
H
2

I realise that this is a somewhat older question, but it's the first result on Google when I was searching for exactly the same issue.

The solution I chose was to put groovy into a new classloader (by using plexus-classworlds), so when the script is finished, the classloader is disposed (and so any changes to the metaclass are also disposed).

Hoban answered 14/3, 2012 at 21:16 Comment(2)
@JohnMercier did not my answer above help ?Stylet
This is a better approach than registry cleaning in many ways, especially to allow concurrent executions.Radcliffe
E
0

This is related but more focused on groovy codes, not shell. I haven't try it there but at least in codebases

You can set it to null If you are doing unit tests, then you have a @Before and @After you can do it in which ever is convenient, I would recommend the @After

// how you modify a method, notice args types must be the same but you can also only do an array if you are not going to use it

ClasName.metaClass.nameOfMethod{String arg1, Boolean arg2, String arg3 = "default" ->
  println("Inside metaclass")
}



@After
void cleanUp()
{
   ClasName.metaClass = null
}
Egmont answered 23/5 at 4:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.