简体   繁体   中英

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. The plan is, that the drivers are simply shared jar archives but with a .dar suffix (driver archive). 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).

Now my problem is, that my classloader gives me an Illegal UTF8 string in constant pool in class file exception. 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:

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

Outputs Hello from dar jar

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.

You first should not rely on ZipEntry.getSize() since -1 may be returned if not known (see Javadocs).

Then InputStream.read(byte[], int , int) may read less bytes than requested. You should read the bytes in a loop until -1 is returned or use IOUtils.toByteArray like you already did one time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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