2

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!

2 Answers2

2

If the class exists in a different ClassLoader, you need to use reflection to get to it:

ClassLoader classLoader = new URLClassLoader(
    new URL[] { firstJarURL, secondJarURL });

String[] args = { arg1, arg2 };

try {
    Class<?> mainClass = classLoader.loadClass("com.somepackage.ClassName");
    mainClass.getMethod("main", String[].class).invoke(null, args);
} catch (ReflectiveOperationException e) {
    throw new RuntimeException(e);
}
VGR
  • 40,506
  • 4
  • 48
  • 63
  • I'm afraid it didn't work, it generated the same error as the edit in my question :( – Imanol Barba Sabariego Mar 26 '15 at 13:48
  • 1
    Have you verified that "alice/tuprolog/lib/InvalidObjectIdException.class" is present in one of the jar files? – VGR Mar 26 '15 at 13:54
  • 1
    This solves at least the correct call to `main`. However first try to find the class `alice.tuprolog.lib.InvalidObjectIdException`, with similar code. First look with a zip tool in the jars. You might also add as second parameter to the URLClassLoader your current class loader / `ClassLoader.getSystemClassLoader()`. – Joop Eggen Mar 26 '15 at 13:56
  • Yup, it's in the second one – Imanol Barba Sabariego Mar 26 '15 at 13:56
0

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

  • Since the class name is hard-coded in the Java code, why do you use reflection. Why not simply replace your 5 lines by `className.main(args)`? Also, you shouldn't create an instance of className to invoke main(). main() is a static method. Pass null as the first argument of method.invoke(). – JB Nizet Mar 27 '15 at 11:46
  • True, I'll add it to the answer – Imanol Barba Sabariego Mar 28 '15 at 10:34