2

So, I have a custom classloader to load classes into memory from byte arrays, and I have a problem: When I attempt to see if a class is assignable from another(ClassLoader.isAssignableFrom), it returns false, even if the compiled class extends or implements it. I assume because it's loaded by a different and custom classloader rather than the system one, so how would I fix this? The reason that I need to do this is I want to check if class files in a jar file are ClassLoaders themselves, because I'm making a java virus scanner for jar files. Custom ClassLoader:

public class CL extends ClassLoader {

byte[] jar = null;

private HashMap<String, Class<?>> classes = new HashMap<String, Class<?>>();
private HashMap<String, byte[]> resources = new HashMap<String, byte[]>();

public CL(byte[] jar) {
    this.jar = jar;
}

private JarInputStream getStream() {
    try {
        return new JarInputStream(new ByteArrayInputStream(jar));
    }catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

public InputStream getResourceAsStream(String name) {
    if (!resources.containsKey(name)) {
        try {
            JarInputStream stream = getStream();
            JarEntry entry = stream.getNextJarEntry();
            ArrayList<JarEntry> ent = new ArrayList<JarEntry>();
            while (entry != null) {
                String en = entry.getName().replace("/", ".");
                if (en.contains(".")) {
                    en = en.substring(0, en.lastIndexOf("."));
                }
                if (en.equals(name)) {
                    break;
                }
                ent.add(entry);
                entry = stream.getNextJarEntry();
            }
            if (entry == null) {
                for (JarEntry e : ent) {
                    String en = e.getName().replace("/", ".");
                    if (en.contains(".")) {
                        en = en.substring(0, en.lastIndexOf("."));
                    }
                    if (en.lastIndexOf(".") > 0 && en.substring(en.lastIndexOf(".") + 1).equals(name)) {
                        entry = e;
                        break;
                    }
                }
            }
            if (entry == null) {
                return null;
            }
            ent = null;
            ByteArrayOutputStream byt = new ByteArrayOutputStream();
            while (true) {
                int r = stream.read();
                if (r < 0) break;
                byt.write(r);
            }
            stream.close();
            byte[] reqc = byt.toByteArray();
            return new ByteArrayInputStream(reqc);
        }catch (IOException e) {
            e.printStackTrace();
        }
    }else {
        return new ByteArrayInputStream(resources.get(name));
    }
    return null;
}

public Class<?> findClass(String name) {
    if (!classes.containsKey(name)) {
        try {
            JarInputStream stream = getStream();
            JarEntry entry = stream.getNextJarEntry();
            while (entry != null) {
                String en = entry.getName().replace("/", ".");
                if (en.contains(".")) {
                    en = en.substring(0, en.lastIndexOf("."));
                }
                if (en.equals(name)) {
                    break;
                }
                entry = stream.getNextJarEntry();
            }
            if (entry == null) {
                return null;
            }
            ByteArrayOutputStream byt = new ByteArrayOutputStream();
            while (true) {
                int r = stream.read();
                if (r < 0) break;
                byt.write(r);
            }
            stream.close();
            byte[] reqc = byt.toByteArray();
            Class<?> c = defineClass(name, reqc, 0, reqc.length, CL.class.getProtectionDomain());
            classes.put(name, c);
            return c;
        }catch (IOException e) {
            e.printStackTrace();
        }
    }else {
        return classes.get(name);
    }
    return null;
}
}

My code for checking if something is assignable from a classloader(cl is an instance of my classloader):

                                    Class<?> cls = cl.findClass(fname);
                boolean isCL = false;
                if (cls.isAssignableFrom(ClassLoader.class)) {
                    isCL = true;
                }
                boolean bCL = false;
                for (Method m : cls.getMethods()) {
                    String mn = m.getName();
                    if (isCL) {
                        if (mn.contains("loadClass") || mn.contains("defineClass") || mn.contains("findClass")) {
                            bCL = true;
                        }
                    }
                }

The problem: the isAssignableFrom returns false, even if it should be true.

So, is there a fix to this? I do not want to switch classloaders, as I am initially loading from a jar, but I want to be able to load jars inside jars and zips. Thanks!

user2507230
  • 562
  • 1
  • 3
  • 12
  • It is hard to say what happens. Make a small complete example which is easy to run, which loads single class from class file, not jar, then we'll look at it. – Alexei Kaigorodov Aug 17 '13 at 02:34

2 Answers2

2

Your problem is that you are using isAssignableFrom incorrectly (albeit, it is a very confusing method). this is what you want:

ClassLoader.class.isAssignableFrom(cls)
jtahlborn
  • 52,909
  • 5
  • 76
  • 118
-2

I ended up figuring out a hack of sorts, for superclasses(superinterfaces are different) use this: cls.getSuperclass().getName().equals("java.lang.ClassLoader") Where the java.lang.ClassLoader is the fully qualified name you need to check. You do not need to have the class loaded in your main classloader.

user2507230
  • 562
  • 1
  • 3
  • 12
  • that would only work if cls was an immediate subclass of ClassLoader (which is rare since _most_ classloader implementations extend `SecureClassLoader`). – jtahlborn Aug 17 '13 at 03:00
  • One could do getSuperclass loops, but your answer worked much better, thanks! – user2507230 Aug 17 '13 at 03:11