Class Loading Fun with Groovy
Sometimes you need special measures. Not to make Groovy work, but to make your applications or frameworks a little bit more powerful or versatile.
Join the DZone community and get the full member experience.
Join For FreeSometimes you need special measures. Not to make Groovy work, but to make your applications or frameworks a little bit more powerful or versatile.
Here are two class loading techniques that I've used in combination with Groovy to make life more fun and interesting.
One: getting rid of Groovy
Sometimes you have to get hold of a ClassLoader
that doesn't include the Groovy classes. Sometimes you need to have a ClassLoader
that only contains the classes shipped with the JVM.
Every Java application has a ClassLoader
that only contains JVM classes and you can get hold of it, if you know how.
The java.lang.ClassLoader
class has a static getSystemClassLoader()
method that returns a ClassLoader
that contains all classes on the CLASSPATH when the JVM started:
ClassLoader.systemClassLoader.loadClass "some.class.on.the.ClassPath"
This ClassLoader
has one or more parent ClassLoader
s. The ClassLoader
on top of this hierarchy is the one that only contains JVM classes. When you find a ClassLoader
without parent you know you've found it:
def classLoader = ClassLoader.systemClassLoader
while (classLoader.parent) {
classLoader = classLoader.parent
}
This little routine gets you the top ClassLoader
. You can now use this one to create a new ClassLoader
hierarchy. It only takes creating a new URLClassLoader
and provide the locations of the JAR files you want to include:
def classLoader = ClassLoader.systemClassLoader
while (classLoader.parent) {
classLoader = classLoader.parent
}
def newClassLoader = new URLClassLoader([new File("location/to.jar").toString().toURL()] as URL[], classLoader)
You now have a new ClassLoader
. Warning: it does not include Groovy if you don't add the Groovy JAR specifically, and any classes that have already been loaded will be loaded again, increasing the memory foot-print of your application. Also, don't create too many new ClassLoader
s in one application since they tend to cause memory leaks. Creating one or a few should be fine. Classes loaded with the new ClassLoader
will not be compatible with classes loaded by the current system ClassLoader
, except for the JVM classes.
You can improve this code by using the org.apache.tools.ant.launch.Locator
class (that ships with Ant). Its fileToURL()
method does a slightly better job at converting java.io.File
objects to java.net.URL
methods.
Two: getting hold of GroovyClassLoader
in Java code
Sometimes you have to get hold of a groovy.lang.GroovyClassLoader
object in Java code. This may for example happen when Java code is called from Groovy code. GroovyClassLoader
s tend the be at the bottom of ClassLoader
hierarchies, and sometimes you want to load classes in GroovyClassLoader
and not anywhere else in the ClassLoader
hierarchy.
Your Java code however was most likely not loaded by the GroovyClassLoader
, so how to get hold of it? You could pass is explicitly from your Groovy code but that's not practical. Instead, use the sun.reflect.Reflection
class? Never heard of it? Then it's time to meet the getCallerClass()
method.
When Groovy code calls Java code then somewhere up in the stack there is a Groovy class that's been loaded by a GroovyClassLoader
. Here's how to get hold of it from Java code:
import sun.reflect.Reflection;
import groovy.lang.GroovyClassLoader;
public class MyClass {
public void myMethod() throws Exception {
int frame = 1;
// number of levels above you in the stack
Class c = Reflection.getCallerClass(frame);
while (c != null && !(c.getClassLoader() instanceof GroovyClassLoader)) {
frame++;
c = Reflection.getCallerClass(frame);
}
if (c != null) {
GroovyClassLoader gcl = (GroovyClassLoader) c.getClassLoader();
// do Groovy stuff here
Class newClass = gcl.parseClass("class MyNewClass{}");
}
}
}
These tricks show just how much Groovy is integrated with the JVM, and how Groovy generates proper Java byte-code.
Happy Groovy coding!
Opinions expressed by DZone contributors are their own.
Comments