无法加载Groovy类

5

我正在尝试在Jenkins Pipeline中使用下面的类对Groovy对象进行序列化和反序列化。

SerializationUtil.groovy

package com.sample;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * A simple class with generic serialize and deserialize method implementations
 */
public class SerializationUtil {

    // deserialize to Object from given file
    public static Object deserialize(String fileName) throws IOException,
            ClassNotFoundException {
        FileInputStream fis = new FileInputStream(fileName);
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object obj = ois.readObject();
        ois.close();
        System.out.println(obj);
        return obj;
    }

    // serialize the given object and save it to file
    public static void serialize(Object obj, String fileName)
            throws IOException {
        FileOutputStream fos = new FileOutputStream(fileName);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);

        fos.close();
    }

}

Test.groovy

package com.sample;

public class Test implements Serializable {
    String key;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    @Override
    public String toString() {
        return "com.sample.Test{" +
                "key='" + key + '\'' +
                '}';
    }
}

Jenkins 管道脚本
Test test = new Test()
test.setKey("sample")
SerializationUtil.serialize(test,"/temp/test.txt")
Test test2 = SerializationUtil.deserialize("/temp/test.txt")

我能够序列化这个对象,但无法反序列化。我遇到以下的异常。
java.lang.ClassNotFoundException: com.sample.Test
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at org.eclipse.jetty.webapp.WebAppClassLoader.loadClass(WebAppClassLoader.java:543)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:628)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1620)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at sun.reflect.GeneratedMethodAccessor8862.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1213)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
    at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:47)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.methodCall(DefaultInvoker.java:20)
    at com.sample.SerializationUtil.deserialize(SerializationUtil.groovy:20)

从异常信息中,我看到GroovyClassLoader没有被调用,我猜想这可能是问题的原因。


顺便提一下,在Groovy中:def deserialize(String filename) { new FileInputStream(filename).withObjectInputStream { readObject() }.tap { println it } } - chrylis -cautiouslyoptimistic-
这段代码出现了相同的异常。 - Vazid
1个回答

3
问题在于,正如您已经发现的那样,没有涉及任何GroovyClassLoader...特别是目前正在使用的类。虽然ObjectOutputStream并不真正关心一个类使用哪个类加载器进行定义,但ObjectInputStream必须在这里做出假设,因为它需要创建一个实例。根据跟踪信息,“最近”的ClassLoader将被选中用于对象实例的创建,其中包含groovy运行时的类加载器。遗憾的是,这是Groovy的常见问题,因为他们在Java中引入了越来越多的调用者敏感逻辑。无论如何,如果您也有类似这样的东西(ScriptLoaderObjectInputStream.groovy):
class ScriptLoaderObjectInputStream extends ObjectInputStream {
  ScriptLoaderObjectInputStream(InputStream str) { super(str) }

  protected Class resolveClass(ObjectStreamClass desc) {
    return this.class.classLoader.loadClass(desc.getName())
  }
}

请将您使用ObjectInputStream的部分替换为这个,它应该可以工作。 它应该有效,因为这也是一个脚本文件,生成的类应该具有与其他生成的类相同的类加载器。
更深入的阅读,我发现这篇文章很不错:https://rsankarx.wordpress.com/2012/06/08/java-serialization-classloaders/ 免责声明:我在此处编写,没有使用IDE、拼写检查或测试。

这个链接帮助解决了问题。感谢@blackdrag。 - Vazid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接