Defining the same class twice using a ClassLoader

classloader, java

Solution

I found the solution to my problem. Basically, rather than overriding the behavior of `defineClass` I should have overriden `findClass`. Here is the code that works.

public class ModifiableClassLoader extends ClassLoader {

    private final Map<String, byte[]> definitions;

    public ModifiableClassLoader(JarFile jar, Map<String, byte[]> definitions) throws MalformedURLException {
        super(new URLClassLoader(new URL[] { new URL("jar:" + new File(jar.getName()).toURI().toURL() + "!/") }));
        this.definitions = definitions;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classBytes = definitions.remove(name);
        if (classBytes != null) {
            return defineClass(name, classBytes, 0, classBytes.length);
        }
        return super.findClass(name);
    }
}

It wrote it based on an answer to another Stack Overflow question which I cannot find right now. I must have overlooked it before.

Problem

I am working a project which involves reading all of the classes in a given JAR file, modifying some of these classes (at the bytecode level, using ASM), and creating a `ClassLoader` of these modified classes. Currently, after modifying the classes, I create a custom instance of the `ClassLoader` class which makes `defineMethod(String, byte[])` visible. Here is the code for the custom `ClassLoader` (it is a nested class): ``` private static class ExposingClassLoader extends ClassLoader { private ExposingClassLoader(JarFile jar) throws MalformedURLException { super(new URLClassLoader(new URL[] { new URL("jar:" + new File(jar.getName()).toURI().toURL() + "!/") })); } private void defineClass(String name, byte[] data) { defineClass(name, data, 0, data.length); } } ``` You may have noticed that I made the parent class loader a `URLClassLoader`. When I construct the custom class loader, I make the `URLClassLoader` refer to the JAR that contains the unmodified class files. After modification of some of the class files, I iterate through them and invoke `defineMethod(String, byte[])` for each one. Some of the modified classes depend on each other, that is, class A may be the superclass of class B, or class C may implement class D, or class E may contain references to class F, et cetera. For my tests, I am using two classes. The first is named `fn` and the second is named `fi`, and `fi` extends `fn` and contains multiple fields of type `fi`. Both of these classes are present in the JAR file that is used to construct the `ClassLoader`, the only difference is that they are unmodified. For some reason, if I define class `fn` it works fine (which it should not? The class `fn` already exists in the JAR file). However, if I try to define `fi`, which, again, refers to `fn` multiple times, I get this exception: ``` Exception in thread "main" java.lang.LinkageError: loader (instance of Injector$ExposingClassLoader): attempted duplicate class definition for name: "fn" ``` On the line: ``` defineClass(name, data, 0, data.length); ``` And I am not sure how to deal with this. The easiest way to do this would be to "unload" the classes that are contained in the JAR, but only those that I have modified. That way, when I define my modified classes, they will not already be loaded in the `ClassLoader`. However, I have looked around and I have yet to find a clean way to do it. Some have said that I need separate (multiple) class loaders, and that would not work for my project unless there was a way to perhaps combine these multiple class loaders into one, like a wrapper. How can I redefine a class in a `ClassLoader`?

Original source

Related problems