Search code examples
javajarclassloader

How to run a java class from a jar file dynamically


I'm working on a java project that needs a third-party java program running as a server to work.

Normally, I'd do:

java -cp jarfile1.jar:jarfile2.jar className arg1 arg2

And then I'd run my java code. This way it works.

I'd like to know if there is any way to, including the two .jars required into my project, run the class directly from my code instead of having to manually start it.

I've tried to use URLClassLoader as I saw in some examples, but either I'm doing it wrong or none cover this specific use case.

URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{new URL("file:///tmp/jarfile1.jar"),new URL("file:///tmp/jarfile2.jar")});
Class<?> cls = classLoader.loadClass("className");
Method method = cls.getDeclaredMethod ("main");
Object instance = cls.newInstance();
Object result = method.invoke (instance);

yields

Exception in thread "main" java.lang.NoClassDefFoundError: alice/tuprolog/lib/InvalidObjectIdException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2615)
at java.lang.Class.getDeclaredMethod(Class.java:2007)
at pkg1.MainClass.main(MainClass.java:54)
Caused by: java.lang.ClassNotFoundException: alice.tuprolog.lib.InvalidObjectIdException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 4 more

Please note that I copied the .jars to /tmp to isolate the failure cause. The files exist and are accessible.

How can I make that run the class as specified above within java code?

Thanks!


Solution

  • I finally fixed it! Things done:

    1. I forgot to add the second jar to the classpath of the project (duh!)
    2. Since everything was on the project classpath, I just reused the current classloader

    Final working code:

    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    Class<?> cls = classLoader.loadClass("className");
    Method method = cls.getDeclaredMethod("main", String[].class);
    Object instance = cls.newInstance();
    Object result = method.invoke(null, (Object)args);
    

    Thanks everyone and specially to VGR and Joop Eggen in the comments for pointing out the error with the second jar!

    EDIT: As JB Nizet pointed out in the comments, calling the class's main() method directly is simpler:

    className.main(args);
    

    And you're done