自定义 classloader 加载一个 jar 在 class 文件的常量池中给我一个非法的 UTF8 字符串
Custom classloader loading a jar gives me an Illegal UTF8 string in constant pool in class file
我需要使用自定义 ClassLoader 来加载第 3 方驱动程序。计划是,驱动程序只是共享的 jar 存档,但带有 .dar 后缀(驱动程序存档)。然后将这些存档简单地添加到 class 路径,同时 jvm 忽略此文件扩展名并像处理资源一样处理 dar 文件(这意味着忽略里面的 class 文件)。
现在我的问题是,我的 class 加载程序在 class 文件异常中给了我一个 Illegal UTF8 string in constant pool。我该如何解决这个问题?
public class DriversClassLoader extends ClassLoader {
public DriversClassLoader() {
super(ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if( name.startsWith("java.")) {
return super.loadClass(name, resolve);
} else {
// see if we have already loaded the class.
Class<?> c = findLoadedClass(name);
if (c != null) return c;
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println("loading: " + entry);
byte[] cBytes = new byte[(int) entry.getSize()];
jis.read(cBytes, 0, cBytes.length);
// this is where I get the exception
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
}
}
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// else delegate to parent
return super.loadClass(name, resolve);
/*
URL res = getResource(name);
if (res == null) throw new ClassNotFoundException("Class " + name + "not found in:");
try {
byte[] cBytes = IOUtils.toByteArray(new InputStreamReader(res.openStream()));
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// delegate to parent
return super.loadClass(name, resolve);*/
}
}
@Override
public URL getResource(String name) {
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println(entry);
return new URL("jar:" + driversUrl + "!/" + entry);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// delegate to parent
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
//super.get
return super.getResources(name);
}
}
那我干脆去
Class<?> aClass = Class.forName("a.class", false, new DriverClassLoader());
System.out.println("aclass = " + aClass);
编辑 1:
但是该文件包含有效的 classes:
java -cp ../rangeCache/src/main/resources/drivers.dar pkg.test.Test
从 dar jar 输出 Hello
虽然这不是:
java -cp target/libs/range-cache-302-SNAPSHOT.jar:drivers/ com.data.cache.range.drivers.DriversClassLoader
jar:file:/home/rangeCache/target/libs/range-cache-302-SNAPSHOT.jar!/drivers.dar
loading: com/data/cache/range/drivers/DatastaxCassandraDriver.class
Exception in thread "main" java.lang.ClassFormatError: Illegal UTF8 string in constant pool in class file com/data/cache/range/drivers/DatastaxCassandraDriver/class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at com.mindbusters.data.cache.range.drivers.DriversClassLoader.loadClass(DriversClassLoader.java:40)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at com.data.cache.range.drivers.DriversClassLoader.main(DriversClassLoader.java:105)
您从 ZipEntry
读取字节的代码已损坏。
您首先不应依赖 ZipEntry.getSize()
,因为如果不知道 -1
可能会返回(请参阅 Javadocs)。
然后 InputStream.read(byte[], int , int)
可能会比请求读取 少 字节。
您应该循环读取字节,直到返回 -1
或像您曾经做过的那样使用 IOUtils.toByteArray
。
我需要使用自定义 ClassLoader 来加载第 3 方驱动程序。计划是,驱动程序只是共享的 jar 存档,但带有 .dar 后缀(驱动程序存档)。然后将这些存档简单地添加到 class 路径,同时 jvm 忽略此文件扩展名并像处理资源一样处理 dar 文件(这意味着忽略里面的 class 文件)。
现在我的问题是,我的 class 加载程序在 class 文件异常中给了我一个 Illegal UTF8 string in constant pool。我该如何解决这个问题?
public class DriversClassLoader extends ClassLoader {
public DriversClassLoader() {
super(ClassLoader.getSystemClassLoader());
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if( name.startsWith("java.")) {
return super.loadClass(name, resolve);
} else {
// see if we have already loaded the class.
Class<?> c = findLoadedClass(name);
if (c != null) return c;
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println("loading: " + entry);
byte[] cBytes = new byte[(int) entry.getSize()];
jis.read(cBytes, 0, cBytes.length);
// this is where I get the exception
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
}
}
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// else delegate to parent
return super.loadClass(name, resolve);
/*
URL res = getResource(name);
if (res == null) throw new ClassNotFoundException("Class " + name + "not found in:");
try {
byte[] cBytes = IOUtils.toByteArray(new InputStreamReader(res.openStream()));
c = defineClass(name, cBytes, 0, cBytes.length);
if (resolve) resolveClass(c);
return c;
} catch (Exception e) {
throw new ClassNotFoundException("loading of class " + name + " failed", e);
}
// delegate to parent
return super.loadClass(name, resolve);*/
}
}
@Override
public URL getResource(String name) {
try {
URL driversUrl = super.getResource("drivers.dar").toURI().toURL();
System.out.println(driversUrl);
JarInputStream jis = new JarInputStream(driversUrl.openConnection().getInputStream());
ZipEntry entry;
while ((entry = jis.getNextEntry()) != null) {
if (entry.getName().matches(name)) {
System.out.println(entry);
return new URL("jar:" + driversUrl + "!/" + entry);
}
}
} catch (Exception e) {
e.printStackTrace();
}
// delegate to parent
return super.getResource(name);
}
@Override
public Enumeration<URL> getResources(String name) throws IOException {
//super.get
return super.getResources(name);
}
}
那我干脆去
Class<?> aClass = Class.forName("a.class", false, new DriverClassLoader());
System.out.println("aclass = " + aClass);
编辑 1: 但是该文件包含有效的 classes:
java -cp ../rangeCache/src/main/resources/drivers.dar pkg.test.Test
从 dar jar 输出 Hello
虽然这不是:
java -cp target/libs/range-cache-302-SNAPSHOT.jar:drivers/ com.data.cache.range.drivers.DriversClassLoader
jar:file:/home/rangeCache/target/libs/range-cache-302-SNAPSHOT.jar!/drivers.dar
loading: com/data/cache/range/drivers/DatastaxCassandraDriver.class
Exception in thread "main" java.lang.ClassFormatError: Illegal UTF8 string in constant pool in class file com/data/cache/range/drivers/DatastaxCassandraDriver/class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at com.mindbusters.data.cache.range.drivers.DriversClassLoader.loadClass(DriversClassLoader.java:40)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at com.data.cache.range.drivers.DriversClassLoader.main(DriversClassLoader.java:105)
您从 ZipEntry
读取字节的代码已损坏。
您首先不应依赖 ZipEntry.getSize()
,因为如果不知道 -1
可能会返回(请参阅 Javadocs)。
然后 InputStream.read(byte[], int , int)
可能会比请求读取 少 字节。
您应该循环读取字节,直到返回 -1
或像您曾经做过的那样使用 IOUtils.toByteArray
。