JNLP class 加载:截断 Class 文件(不是 Maven 缓存问题!)
JNLP class loading: Truncated Class File (not a maven cache issue!)
我有一个包含多个 jar 的项目,其中一部分是我们的,另一部分是第 3 方库。应用程序必须通过 Java WebStart (JNLP) 运行。同时我遇到了两次,如果第 3 方库是 "too new",则加载失败并出现以下异常:
java.lang.ClassFormatError: Truncated class file
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access0(URLClassLoader.java:73)
at java.net.URLClassLoader.run(URLClassLoader.java:368)
at java.net.URLClassLoader.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at com.sun.jnlp.JNLPClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at com.sun.jnlp.JNLPClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at <...>.ApplicationImpl.<init>(ApplicationImpl.java:63)
类似的问题经常被回答为"clear maven cache",但这没有关系。因为如果我 运行 它作为常规 java 应用程序(不是 WebStart),一切正常。但是,是的,我还是试过了。
JDK 目前试过的版本是jdk1.8.0_151, jdk1.8.0_161.
我的测试代码如下:
try {
LOG.debug("@@@ jackson-core");
Class.forName("com.fasterxml.jackson.core.Versioned");
LOG.debug("@@@ jackson-annotations");
Class.forName("com.fasterxml.jackson.annotation.JsonAutoDetect");
LOG.debug("@@@ jackson-databind");
Class.forName("com.fasterxml.jackson.databind.ObjectMapper");
} catch (ClassNotFoundException ex) {
LOG.debug(ex);
}
到目前为止,我已经测试了这个版本的库:
- jackson-databind/core/annotations 2.5.0(2015 年 1 月)及更高版本不加载
- jackson-databind/core/annotations 2.2.0(2013 年 4 月)负载
- io.nats.jnats 1.0(2017 年 2 月)未加载
- io.nats.jnats 0.3.1(2016 年 1 月)负载
- org.apache.httpcomponents.httpasyncclient 4.1(2015 年 4 月)负载
- org.apache.httpcomponents.httpasyncclient 4.1.1(2015 年 11 月)及更高版本不加载
该项目是用 target_jdk=8 编译的。罐子是用自签名证书签名的,但我用生产(官方)证书进行了测试,问题仍然存在。我试过 -XX:+TraceClassLoadinga
和 -verbose:class
但没用。编辑:将其移至单独的问题:
也是最有趣的部分。如果我将这些库重新打包到我自己的 jar 中(jar-with-dependencies),即使通过 JNLP 也可以很好地加载它们。
可能是什么问题?
我明白了。这件事太棒了。事实证明,当构建为常规桌面 Java 应用程序时,jar 确实不同于构建为 JNLP 的那些。一个自定义的 maven 插件已经到位,可以重建 jar。当它将 类 从 JarInputStream
复制到 JarOutputStream
时,它使用 ZipEntry::size
来获取要传输的字节数。但事实证明,这种方法 不能保证报告正确的值 (认真的,作者?)。它适用于较旧的库,因为报告的值对它们来说是正确的,但并不总是适用于较新的库。
我有一个包含多个 jar 的项目,其中一部分是我们的,另一部分是第 3 方库。应用程序必须通过 Java WebStart (JNLP) 运行。同时我遇到了两次,如果第 3 方库是 "too new",则加载失败并出现以下异常:
java.lang.ClassFormatError: Truncated class file
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access0(URLClassLoader.java:73)
at java.net.URLClassLoader.run(URLClassLoader.java:368)
at java.net.URLClassLoader.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at com.sun.jnlp.JNLPClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at com.sun.jnlp.JNLPClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at <...>.ApplicationImpl.<init>(ApplicationImpl.java:63)
类似的问题经常被回答为"clear maven cache",但这没有关系。因为如果我 运行 它作为常规 java 应用程序(不是 WebStart),一切正常。但是,是的,我还是试过了。
JDK 目前试过的版本是jdk1.8.0_151, jdk1.8.0_161.
我的测试代码如下:
try {
LOG.debug("@@@ jackson-core");
Class.forName("com.fasterxml.jackson.core.Versioned");
LOG.debug("@@@ jackson-annotations");
Class.forName("com.fasterxml.jackson.annotation.JsonAutoDetect");
LOG.debug("@@@ jackson-databind");
Class.forName("com.fasterxml.jackson.databind.ObjectMapper");
} catch (ClassNotFoundException ex) {
LOG.debug(ex);
}
到目前为止,我已经测试了这个版本的库:
- jackson-databind/core/annotations 2.5.0(2015 年 1 月)及更高版本不加载
- jackson-databind/core/annotations 2.2.0(2013 年 4 月)负载
- io.nats.jnats 1.0(2017 年 2 月)未加载
- io.nats.jnats 0.3.1(2016 年 1 月)负载
- org.apache.httpcomponents.httpasyncclient 4.1(2015 年 4 月)负载
- org.apache.httpcomponents.httpasyncclient 4.1.1(2015 年 11 月)及更高版本不加载
该项目是用 target_jdk=8 编译的。罐子是用自签名证书签名的,但我用生产(官方)证书进行了测试,问题仍然存在。我试过 -XX:+TraceClassLoadinga
和 -verbose:class
但没用。编辑:将其移至单独的问题:
也是最有趣的部分。如果我将这些库重新打包到我自己的 jar 中(jar-with-dependencies),即使通过 JNLP 也可以很好地加载它们。
可能是什么问题?
我明白了。这件事太棒了。事实证明,当构建为常规桌面 Java 应用程序时,jar 确实不同于构建为 JNLP 的那些。一个自定义的 maven 插件已经到位,可以重建 jar。当它将 类 从 JarInputStream
复制到 JarOutputStream
时,它使用 ZipEntry::size
来获取要传输的字节数。但事实证明,这种方法 不能保证报告正确的值 (认真的,作者?)。它适用于较旧的库,因为报告的值对它们来说是正确的,但并不总是适用于较新的库。