1

I have a program through which I have the following three classes. These first two are in jar1.jar:

Main (uses the jar loading trick found here):

package prob1;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Main {
    static List<Class<?>> classes = new ArrayList<Class<?>>();

    static String pathToJar = "/Users/vtcakavsmoace/Desktop/jar2.jar";

    public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
        loadJar();

        classes.get(0).asSubclass(A.class).newInstance().someMethod();
    }

    private static void loadJar() throws IOException, ClassNotFoundException {
        JarFile jarFile = new JarFile(pathToJar);
        Enumeration<JarEntry> e = jarFile.entries();

        URL[] urls = { new URL("jar:file:" + pathToJar + "!/") };
        URLClassLoader cl = URLClassLoader.newInstance(urls);

        while (e.hasMoreElements()) {
            JarEntry je = e.nextElement();
            if (je.isDirectory() || !je.getName().endsWith(".class")) {
                continue;
            }

            String className = je.getName().substring(0, je.getName().length() - 6);
            className = className.replace('/', '.');
            classes.add(cl.loadClass(className));
        }

        jarFile.close();
    }
}

A:

package prob1;

public interface A {
    void someMethod();
}

And a second class found in jar2.jar, B:

package prob2;

import prob1.A;

public class B implements A {

    @Override
    public void someMethod() {
        System.out.println("Hello, world!");
    }

}

As you can see, class B obviously implements interface A. However, when loading class B, and then attempting to call asSubclass on it, fails with the following exception:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader.main(JarRsrcLoader.java:58)
Caused by: java.lang.ClassCastException: class prob2.B
    at java.lang.Class.asSubclass(Class.java:3404)
    at prob1.Main.main(Main.java:20)
    ... 5 more

Note that this does not fail when run through Eclipse.

What am I doing wrong here? How can I fix this?

Community
  • 1
  • 1
Addison Crump
  • 666
  • 5
  • 19
  • 4
    Create a [mcve], please. – Tom Jun 27 '16 at 02:04
  • @Tom Is it okay now? :P – Addison Crump Jun 27 '16 at 02:42
  • And this example results in the same issue? Because it works fine for me. The jar version and an even "smaller" version with `classes.add(B.class);` instead of `loadJar();` – Tom Jun 27 '16 at 02:55
  • 1
    It means that your class `A` was loaded twice on different classloaders; when it loaded `B`, it took class `A` from a different classloader than when it loaded your class `Main`. Could be because you also added `A` to jar2.jar or other reasons – Erwin Bolwidt Jun 27 '16 at 02:57
  • @Tom Using `B.class` means that the class is packaged in the same jar without loading it dynamically, no? – Addison Crump Jun 27 '16 at 03:03
  • @ErwinBolwidt Oh. So, `ClassLoader.getSystemClassLoader().loadClass(...)`? Wait, that wouldn't work because I'm loading it from another jar... – Addison Crump Jun 27 '16 at 03:03
  • @ErwinBolwidt Also, no, I did not package `A` or `Main` with `B`'s jar or vice versa. – Addison Crump Jun 27 '16 at 03:07
  • Yes, then you use `B.class` directly, then it won't be read from a jar file. But that approach verifies if `classes.get(0).asSubclass(A.class).newInstance().someMethod()` works as expected. – Tom Jun 27 '16 at 07:43

1 Answers1

0

Okay, found the answer using the knowledge that Erwin Bolwidt gave me; in class Main, replacing:

URLClassLoader cl = URLClassLoader.newInstance(urls);

with:

URLClassLoader cl = URLClassLoader.newInstance(urls, Main.class.getClassLoader());

Fixes the problem. :P

Community
  • 1
  • 1
Addison Crump
  • 666
  • 5
  • 19