在使用Graalvm打包一个项目,在运行的时候出现了如下的报错:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19Exception in thread "main" java.lang.ExceptionInInitializerError
at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:290)
at java.lang.Class.ensureInitialized(DynamicHub.java:467)
at okhttp3.OkHttpClient.<clinit>(OkHttpClient.java:127)
at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)
at java.lang.Class.ensureInitialized(DynamicHub.java:467)
at okhttp3.OkHttpClient$Builder.<init>(OkHttpClient.java:475)
at io.fabric8.kubernetes.client.utils.HttpClientUtils.createHttpClient(HttpClientUtils.java:73)
at io.fabric8.kubernetes.client.utils.HttpClientUtils.createHttpClient(HttpClientUtils.java:64)
at io.fabric8.kubernetes.client.BaseClient.<init>(BaseClient.java:51)
at io.fabric8.kubernetes.client.BaseClient.<init>(BaseClient.java:43)
at io.fabric8.kubernetes.client.DefaultKubernetesClient.<init>(DefaultKubernetesClient.java:89)
at ch.frankel.kubernetes.extend.Sidecar.main(Sidecar.java:13)
Caused by: java.nio.charset.UnsupportedCharsetException: UTF-32BE
at java.nio.charset.Charset.forName(Charset.java:531)
at okhttp3.internal.Util.<clinit>(Util.java:75)
at com.oracle.svm.core.hub.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:350)
at com.oracle.svm.core.hub.ClassInitializationInfo.initialize(ClassInitializationInfo.java:270)打包命令为:
native-image -jar simple-rpc-server.jar -H:+ReportExceptionStackTraces
从堆栈可以看出,有问题的代码位于:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39public final class Util {
private static final ByteString UTF_8_BOM = ByteString.decodeHex("efbbbf");
private static final ByteString UTF_16_BE_BOM = ByteString.decodeHex("feff");
private static final ByteString UTF_16_LE_BOM = ByteString.decodeHex("fffe");
private static final ByteString UTF_32_BE_BOM = ByteString.decodeHex("0000ffff");
private static final ByteString UTF_32_LE_BOM = ByteString.decodeHex("ffff0000");
public static final Charset UTF_8 = Charset.forName("UTF-8");
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private static final Charset UTF_16_BE = Charset.forName("UTF-16BE");
private static final Charset UTF_16_LE = Charset.forName("UTF-16LE");
private static final Charset UTF_32_BE = Charset.forName("UTF-32BE");
private static final Charset UTF_32_LE = Charset.forName("UTF-32LE");
public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException {
if (source.rangeEquals(0, UTF_8_BOM)) {
source.skip(UTF_8_BOM.size());
return UTF_8;
}
if (source.rangeEquals(0, UTF_16_BE_BOM)) {
source.skip(UTF_16_BE_BOM.size());
return UTF_16_BE;
}
if (source.rangeEquals(0, UTF_16_LE_BOM)) {
source.skip(UTF_16_LE_BOM.size());
return UTF_16_LE;
}
if (source.rangeEquals(0, UTF_32_BE_BOM)) {
source.skip(UTF_32_BE_BOM.size());
return UTF_32_BE;
}
if (source.rangeEquals(0, UTF_32_LE_BOM)) {
source.skip(UTF_32_LE_BOM.size());
return UTF_32_LE;
}
return charset;
}
}打包后的可执行文件中,在加载类的时候,每个对应的
Charset
运行自己的Charset.forName()
,在操作系统中,没有对应的字符集。于是报错了。此问题在Graalvm的github上也有提过Issues
解决方案
编译时添加所有字符集
- 在打包命令中添加参数
-H:+AddAllCharsets
- 缺点:会导致打包后的文件更大一点。
修复库
- 下载库的代码(如OkHttp)然后干掉这部分问题代码,自己打包构建,Maven引入。
使用substratevm
注:关于substratevm这块没有找到官方文档,以下大部分内容是直接谷歌来的。
项目中添加依赖:
1
2
3
4
5
6<dependency>
<groupId>com.oracle.substratevm</groupId>
<artifactId>svm</artifactId>
<version>19.2.1</version>
<scope>provided</scope>
</dependency>编写替换的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33@TargetClass(Util.class)
public final class okhttp3_internal_Util {
@Alias
private static ByteString UTF_8_BOM;
@Alias
private static ByteString UTF_16_BE_BOM;
@Alias
private static ByteString UTF_16_LE_BOM;
@Alias
public static Charset UTF_8;
@Alias
private static Charset UTF_16_BE;
@Alias
private static Charset UTF_16_LE;
@Substitute
public static Charset bomAwareCharset(BufferedSource source, Charset charset) throws IOException {
if (source.rangeEquals(0, UTF_8_BOM)) {
source.skip(UTF_8_BOM.size());
return UTF_8;
}
if (source.rangeEquals(0, UTF_16_BE_BOM)) {
source.skip(UTF_16_BE_BOM.size());
return UTF_16_BE;
}
if (source.rangeEquals(0, UTF_16_LE_BOM)) {
source.skip(UTF_16_LE_BOM.size());
return UTF_16_LE;
}
return charset;
}
}- 其中:
- @TargetClass:被这个注解标注的类会在NativeImage编译期间替换方法执行的逻辑而无需改变原有代码
- @Substitute:被这个注解标注的方法会被替换。
- @Alias:被这个注解标注的变量会被替换。
- 其中:
添加配置文件:
1
2
3
4
5
6
7
8
9
10
11
12[
{
"annotatedClass": "用于替换的类的全限定类名",
"originalClass": "需要被替换的类的全限定类名",
"methods": [
{
"annotatedName": "方法名",
"substitute": true
}
]
}
]然后native-image时指定该文件:
-H:SubstitutionFiles=xxx.json