简体   繁体   English

加载jar的自定义类加载器在类文件的常量池中给我一个非法的UTF8字符串

[英]Custom classloader loading a jar gives me an Illegal UTF8 string in constant pool in class file

I need to use custom ClassLoader to load 3rd party drivers. 我需要使用自定义ClassLoader加载第三方驱动程序。 The plan is, that the drivers are simply shared jar archives but with a .dar suffix (driver archive). 计划是,驱动程序只是共享的jar存档,但带有.dar后缀(驱动程序存档)。 These archives are then simply added to the classpath while the jvm is ignoring this file extension and handling the dar files like resources (which means ignoring the class files inside). 然后,当jvm忽略该文件扩展名并像资源一样处理dar文件时(这意味着忽略其中的类文件),可以将这些归档文件简单地添加到classpath中。

Now my problem is, that my classloader gives me an Illegal UTF8 string in constant pool in class file exception. 现在我的问题是,我的类加载器在类文件异常的常量池中给我一个非法的UTF8字符串。 How could I get around that? 我该如何解决?

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

Then I simply to 然后我只是

Class<?> aClass = Class.forName("a.class", false, new DriverClassLoader());
System.out.println("aclass = " + aClass);

EDIT 1: But the file contains valid classes: 编辑1:但是文件包含有效的类:

java -cp ../rangeCache/src/main/resources/drivers.dar pkg.test.Test

Outputs Hello from dar jar 从dar jar输出Hello

While this does not: 虽然这不是:

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)

Your code to read the bytes from the ZipEntry is broken. 您从ZipEntry读取字节的代码已损坏。

You first should not rely on ZipEntry.getSize() since -1 may be returned if not known (see Javadocs). 首先,您不应该依赖ZipEntry.getSize()因为如果未知,则可能返回-1 (请参阅Javadocs)。

Then InputStream.read(byte[], int , int) may read less bytes than requested. 然后InputStream.read(byte[], int , int)读取的字节数可能少于请求的字节数。 You should read the bytes in a loop until -1 is returned or use IOUtils.toByteArray like you already did one time. 您应该循环读取字节,直到返回-1为止,或者像已经做过一次一样使用IOUtils.toByteArray

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM