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

时间:2015-11-07 08:34:34

标签: java classloader

我需要使用自定义ClassLoader来加载第三方驱动程序。计划是,驱动程序只是共享jar档案,但带有.dar后缀(驱动程序存档)。然后将这些存档简单地添加到类路径中,而jvm忽略此文件扩展名并处理dar文件(如资源)(这意味着忽略其中的类文件)。

现在我的问题是,我的类加载器在类文件异常中的常量池中给了我一个非法的UTF8字符串。我怎么能绕过那个?

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: 但该文件包含有效的类:

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)

1 个答案:

答案 0 :(得分:2)

您从ZipEntry读取字节的代码已损坏。

您首先不应该依赖ZipEntry.getSize(),因为-1如果不知道可能会被返回(请参阅Javadocs)。

然后InputStream.read(byte[], int , int)可能会读取个字节而不是请求的字节数。 您应该在循环中读取字节,直到返回-1或使用IOUtils.toByteArray,就像您已经执行过一次。