Summary: Loading a jar from a running Java program raises a NoClassDefFoundError caused by a ClassNotFoundException , caused by interclass dependencies (like import ). How can I get around this?
The problem is more detailed:
I am trying to programmatically download the jar file - let it "Server" - into the Java virtual machine through my own Java program - let it "ServerAPI" - and use the extension and some other tricks to change the behavior and interact with the Server. ServerAPI is server dependent, but if there is no server, the ServerAPI should still be able to start and load the server from the website.
To avoid errors caused by loading ServerAPI, not satisfying its dependencies on the Server, I created a launcher - let it launch "Launcher" - this means that you need to load the Server and configure ServerAPI as needed, then load Server and ServerAPI, then run ServerAPI .
However, when I try to load jars from Launcher, I get errors caused by the fact that ClassLoaders cannot resolve other classes in the file that the loading class depends on. In short, if I try to load Class A , it will throw an error if A imports B because I have not loaded B yet. However, if B also imports A , I am stuck because I cannot figure out how to load two classes at once or how to load a class without checking the JVM.
Why all the restrictions led me to this problem:
I am trying to change and add to the server behavior, but for complex legal reasons, I can not directly change the program, so I created a ServerAPI that depends on and can configure the behavior of the Server from the outside.
However, for more complex legal reasons, the Server and ServerAPI cannot simply be downloaded together. Launcher (see above) must be loaded from ServerAPI, then Launcher needs to download Server. Finally, ServerAPI can be started using Server as a dependency. That is why this problem is so complex.
This issue also applies to a later part of the project, which will include a plug-in-based API, which should be able to load and unload plug-ins from jar files at runtime.
Research I have already done on this issue:
I read and could not help:
- This question , which concerns only one method and does not take into account dependency errors between classes;
- this question , which will not work, because I can not turn off and restart the program every time the box is loaded or unloaded (mainly for the part of the plugin that I briefly mentioned);
- This question , which only works in situations where dependencies are present when the program starts;
- This question , which has the same problem as # 2;
- This question , which has the same problem as # 3;
- this article , from which I learned about the hidden method loadClass (String, boolean), but tried using true and false did not help;
- This question , which has the same problem as # 1;
and much more. Nothing worked.
// EDIT: Attempts I have made so far:
I tried using URLClassLoaders to load jar using JarEntries from JarFile similar to this question . I tried this both with and using the URLClassLoader loadClass(String) method and creating a class that extends URLClassLoader so that I can use loadClass(String, boolean resolve) to try to get ClassLoader to allow all the classes that it loads. In both cases, I had the same error:
I couldn't find the class in the JarEntry! entry name="org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.class" class name="org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter" java.lang.NoClassDefFoundError: javax/persistence/AttributeConverter at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:760) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:455) at java.net.URLClassLoader.access$100(URLClassLoader.java:73) at java.net.URLClassLoader$1.run(URLClassLoader.java:367) at java.net.URLClassLoader$1.run(URLClassLoader.java:361) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:360) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at Corundum.launcher.CorundumClassLoader.load(CorundumClassLoader.java:52) at Corundum.launcher.CorundumLauncher.main(CorundumLauncher.java:47) Caused by: java.lang.ClassNotFoundException: javax.persistence.AttributeConverter at java.net.URLClassLoader$1.run(URLClassLoader.java:372) at java.net.URLClassLoader$1.run(URLClassLoader.java:361) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:360) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 12 more
// END EDIT
// EDIT 2:
Here is an example of the code that I used to load the class, trying to solve it. It was inside a class that I made that extends URLClassLoader . In the line starting with Class<?> clazz = loadClass( , I tried to use true and false as a boolean argument; both attempts led to the same error above.
public boolean load(ClassLoadAction class_action, FinishLoadAction end_action) { // establish the jar associated with this ClassLoader as a JarFile JarFile jar; try { jar = new JarFile(jar_path); } catch (IOException exception) { System.out.println("There was a problem loading the " + jar_path + "!"); exception.printStackTrace(); return false; } // load each class in the JarFile through its JarEntries Enumeration<JarEntry> entries = jar.entries(); if (entries.hasMoreElements()) for (JarEntry entry = entries.nextElement(); entries.hasMoreElements(); entry = entries.nextElement()) if (!entry.isDirectory() && entry.getName().endsWith(".class")) try { /* this "true" in the line below is the whole reason this class is necessary; it makes the URLClassLoader this class extends "resolve" the class, * meaning it also loads all the classes this class refers to */ Class<?> clazz = loadClass(entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", "."), true); class_action.onClassLoad(this, jar, clazz, end_action); } catch (ClassNotFoundException | NoClassDefFoundError exception) { try { close(); } catch (IOException exception2) { System.out.println("There was a problem closing the URLClassLoader after the following " + exception2.getClass().getSimpleName() + "!"); exception.printStackTrace(); } try { jar.close(); } catch (IOException exception2) { System.out.println("There was a problem closing the JarFile after the following ClassNotFoundException!"); exception.printStackTrace(); } System.out.println("I couldn't find the class in the JarEntry!\nentry name=\"" + entry.getName() + "\"\nclass name=\"" + entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", ".") + "\""); exception.printStackTrace(); return false; } // once all the classes are loaded, close the ClassLoader and run the plugin main class(es) load() method(s) try { jar.close(); } catch (IOException exception) { System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar.getName() + "\""); exception.printStackTrace(); return false; } end_action.onFinishLoad(this, null, class_action); System.out.println("loaded " + jar_path); // TODO TEST try { close(); } catch (IOException exception) { System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar_path + "\""); exception.printStackTrace(); return false; } return true; }
// END EDIT 2
I understand that there should be a simple solution to this, but for the life of me I cannot find it. Any help would make me thank forever. Thanks.