I had the same problem when running groovy from java via GroovyShell.
This solution works for me and solves the dependency-loading problems that MeiSign mentions in tim_yates solution. Explanation follows:
def thisLoader = this.class.classLoader
// try the "proper" way to find the root classloader
def rootLoader = DefaultGroovyMethods.getRootLoader(thisLoader)
if (rootLoader == null) {
// Root classloader is not a groovy RootLoader, but we still need it,
// so walk up the hierarchy and get the top one (whose parent is null)
// When running from Java this is sun.misc.Launcher.ExtClassLoader
rootLoader = thisLoader
ClassLoader parentLoader = rootLoader.getParent()
while (parentLoader != null) {
rootLoader = parentLoader
parentLoader = parentLoader.getParent()
}
}
rootLoader.addURL(new File("gis-geotools-1.9.0.jar").toURL())
def CoordinateTransformer =
Class.forName("de.is24.gis.geotools.CoordinateTransformer",
true,
rootLoader).newInstance();
When running groovy from java using groovy.lang.GroovyShell.main, the this.classLoader is GroovyClassLoader.InnerLoader
When running groovy from command line groovy.bat, the class loader is org.codehaus.groovy.tools.RootLoader
When you call getRootLoader
, it walks up the classloader hiearchy - with getParent()
- until it finds an instance of a RootLoade
r. If it doesn't it finds null. (That's the NPE
in the title of this question)
The problem is that when running from Java the heirarchy tops out at sun.misc.Launcher.ExtClassLoader
which is clearly not a groovy class at all, let alone the groovy RootLoader.
Specifically the hiearchy is:
GroovyClassLoader.InnerLoader
--> GroovyClassLoader
---> sun.misc.Launcher.AppClassLoader
----> sun.misc.Launcher.ExtClassLoader
------> null
How it ends up that way is pretty obscure in GroovyMain
(but if you really want to set it yourself there's a GroovyShell
constructor that takes a ClassLoader).
Anyway, Tim's solution doesn't work in depth, because the new ClassLoader you are creating on the fly is used only to load that class and not subsequent classes. You really do need to add the classpath entries to the root classpath.
So I simply used the real root when the groovy root fails.
Here's the original code from org.codehaus.groovy.runtime.DefaultGroovyMethodsSupport
/**
* Iterates through the classloader parents until it finds a loader with a class
* named "org.codehaus.groovy.tools.RootLoader". If there is no such class
* <code>null</code> will be returned. The name is used for comparison because
* a direct comparison using == may fail as the class may be loaded through
* different classloaders.
*
* @param self a ClassLoader
* @return the rootLoader for the ClassLoader
* @see org.codehaus.groovy.tools.RootLoader
* @since 1.5.0
*/
public static ClassLoader getRootLoader(ClassLoader self) {
while (true) {
if (self == null) return null;
if (isRootLoaderClassOrSubClass(self)) return self;
self = self.getParent();
}
}
private static boolean isRootLoaderClassOrSubClass(ClassLoader self) {
Class current = self.getClass();
while(!current.getName().equals(Object.class.getName())) {
if(current.getName().equals(RootLoader.class.getName())) return true;
current = current.getSuperclass();
}
return false;
}