Groovy无法调用Classloader(NullpointerException)

时间:2013-08-21 08:06:00

标签: groovy classloader

我创建了以下Groovy脚本来使用Java库转换JSON文档。但不知何故,我无法从我需要的罐子加载类。我总是得到java.lang.ClassNotFoundException: de.is24.gis.geotools.CoordinateTransformer

Jar文件与groovy脚本位于同一目录中。我无法编辑我称之为groovy脚本的方式。它由河流自动调用。

import groovy.json.JsonSlurper

geo = new GeoTransformer()
geo.transform(ctx.document)

class GeoTransformer {
    void transform(doc) {
        this.getClass().classLoader.addURL(new File("gis-geotools-1.9.0.jar").toURL())
        def CoordinateTransformer = Class.forName("de.is24.gis.geotools.CoordinateTransformer").newInstance();

        def x = doc.realEstateCommonData.locationDto.geoCoordinateDto.xCoordinate;
        def y = doc.realEstateCommonData.locationDto.geoCoordinateDto.yCoordinate;

        def coords = CoordinateTransformer.transformFromLambertEuToWgs84(x,z)

        println coords.getLatitude()
        println coords.getLongitude()
        def jsonObj = new JsonSlurper().parseText( '{"type" : "Point", "coordinates" : [' + coords.getLatitude() + ',' + coords.getLongitude() + ']}' )

        doc.location = jsonObj       
    }
}

2 个答案:

答案 0 :(得分:0)

不确定为什么你无法访问rooLoader,它必须与Groovy脚本的执行方式有关。

你可以尝试这个(显然未经测试)

class GeoTransformer {
    void transform( doc ) {
        def urlLoader = new GroovyClassLoader()
        urlLoader.addURL( new File("gis-geotools-1.9.0.jar").toURL() )

        def coordTransformer = Class.forName( "de.is24.gis.geotools.CoordinateTransformer",
                                              true,
                                              urlLoader ).newInstance()

        def x = doc.realEstateCommonData.locationDto.geoCoordinateDto.xCoordinate;
        def y = doc.realEstateCommonData.locationDto.geoCoordinateDto.yCoordinate;

        def coords = coordTransformer.transformFromLambertEuToWgs84( x, z )

        println coords.latitude
        println coords.longitude

        doc.location = [ type:'Point',
                         coordinates:[ coords.latitude, coords.longitude ] ]
    }
}

我摆脱了底部的JsonSlurper位,直接创建了地图(我假设doc.location需要是地图)?

编辑:

这适用于Groovy控制台:

def jar = new File( '/path/to/commons-collections-3.2.1.jar' )
def loader = new GroovyClassLoader()
loader.addURL( jar.toURL() )

def bag = Class.forName( 'org.apache.commons.collections.bag.HashBag',
                         true,
                         loader ).newInstance()
bag.add( 'tim' )

println bag
println bag.getClass().name

并打印:

[tim]
org.apache.commons.collections.bag.HashBag

答案 1 :(得分:0)

通过GroovyShell从java运行groovy时遇到了同样的问题。 这个解决方案对我有用,并解决了MeiSign在tim_yates解决方案中提到的依赖加载问题。说明如下:

    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();

使用groovy.lang.GroovyShell.main从java运行groovy时,this.classLoader是GroovyClassLoader.InnerLoader 从命令行groovy.bat运行groovy时,类加载器为org.codehaus.groovy.tools.RootLoader

当您致电getRootLoader时,它会使用getParent()向上移动类加载器层次结构 - 直到找到RootLoade r的实例。如果它没有找到null。 (这个问题标题中的NPE

问题在于,当从Java运行时,heirarchy在sun.misc.Launcher.ExtClassLoader处于顶部,这显然不是一个常规类,更不用说常规的RootLoader了。 具体来说,分层结构是:

GroovyClassLoader.InnerLoader
 --> GroovyClassLoader
    ---> sun.misc.Launcher.AppClassLoader 
        ----> sun.misc.Launcher.ExtClassLoader
           ------> null

GroovyMain中如何以这种方式结束是非常模糊的(但是如果你真的想自己设置它是一个带有ClassLoader的GroovyShell构造函数)。

无论如何,Tim的解决方案并没有深入开展,因为您正在创建的新ClassLoader仅用于加载该类而不是后续类。您确实需要将类路径条目添加到 root 类路径中。 因此,当groovy root失败时,我只使用了真正的root。

以下是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;
}