Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the behaviour of a Java app dynamically using Groovy

I am investigating methods of dynamically modifying the behaviour of a Java application (specifically, I'm trying to make a Minecraft mod that allows users to modify the behaviour of the objects they find by writing code without the need to restart the game) and I stumbled upon Groovy. My question is: is it possible to integrate Java and Groovy in such way they "share" objects? (I'm thinking about having a specific set of classes that are actually Groovy code so you can change the code during runtime, similarly to what you can do in any Smalltalk implementation)

like image 392
Laura Avatar asked May 07 '26 19:05

Laura


2 Answers

Take a look at Integrating Groovy in a Java Application. It shows examples of how you can run a Groovy script from inside a Java application and share data between them using groovy.lang.Binding.

like image 191
Anderson Vieira Avatar answered May 09 '26 09:05

Anderson Vieira


What a cool idea!

1. Groovy: Java and Groovy can share objects and call back and forth. Groovy classes that implement Java interfaces are easily called from Java. (There are other ways, like calling groovyObject.invokeMethod("methodName", args) from Java.) Of JVM languages, Groovy has the tightest integration with Java. It's also easy for Java programmers to learn since it shares so much with Java.

The book Groovy in Action has a chapter on "Integrating Groovy" that explains and compares the approaches (in more detail than the reference docs do): GroovyShell, GroovyScriptEngine, GroovyClassLoader, Spring integration, and JSR-223 ScriptEngineManager. GroovyClassLoader is the most capable choice.

However, while it's easy to compile and load Groovy code at runtime, I'm puzzled about how to change behavior of existing object instances (short of the notes below on hot swapping). (It might depend on whether the class overrides a Java interface or subclasses a Java class.) Consider:

class G implements Runnable {
  void run() { println 'Groovy' }
}

g = new G()
g.run()

This prints Groovy. Now redefine the class:

class G implements Runnable {
  void run() { println 'Groovy!' }
}

g1 = new G()
g.run()
g1.run()

This prints

Groovy
Groovy!

Now use the meta-class to change methods at runtime:

G.metaClass.run = { println 'Groovy!!!' }

g2 = new G()
g.run()
g1.run()
g2.run()

This prints

Groovy
Groovy!
Groovy!

If we omitted implements Runnable from those class definitions, then the last step would instead print

Groovy
Groovy!
Groovy!!!

But with our class that does implement Runnable, now do:

G.metaClass.run = { println 'Very Groovy!!!' }

g3 = new G()
g.run()
g1.run()
g2.run()
g3.run()

this prints:

 Groovy
 Groovy!
 Very Groovy!!!
 Very Groovy!!!

A workaround would implement the methods in closures held in class variables.

2. Hot Swapping: If the main point is to redefine method bodies at run time for classes with existing instances, then you can simply run within an IDE's debugger and use hot swapping.

E.g. for IntelliJ, here are the instructions to configure hot swapping of Java and Groovy code.

3. Expanded Hot Swapping: If you also want to be able to add/remove methods and instance variables at run time, then see this JetBrains article on extended hot swapping via DCEVM (Dynamic Code Evolution VM).

See Hot Swap code code at https://github.com/HotswapProjects

Also see this SO Q&A on hot swapping techniques.

like image 25
Jerry101 Avatar answered May 09 '26 07:05

Jerry101