Interface PluginSystem ClassNotFound exception für library Klassen

Moin,

ich hatte in den letzten Tagen ein einfaches Pluginsystem entwickelt und meine Plugins werden auch geladen und ausgeführt. Allerdings bekomme ich immer eine ClassNotFound Exception wenn der URLClassLoader versucht mein Plugininterface zu laden. Eigentlich sollte der ClassLoader das Interface finden das ist ja auch in der jar.

Hier ist meine Pluginloading Klasse:
[CODE lang="java" title="PluginAdministration.java"]package plugintestsystem;

import pluginsystem.Plugin;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class PluginAdministration {
final String pluginDir = "E:/ProgrammierProjekte/Intellij/Plugin/out/artifacts/Plugin_jar";

public List<Plugin> loadPlugins() throws IOException{
File pluginDirFile = new File(pluginDir);

if (!pluginDirFile.isDirectory()) {
System.err.println("Der angegebene Pfad ist kein Ordner!");
return null;
}
if (!(pluginDirFile.listFiles().length > 0)) {
System.err.println("Es gibt keine Plugins im Ordner!");
return null;
}

URL[] urls = new URL[pluginDirFile.listFiles().length];
int i = 0;

for (File pluginFile : pluginDirFile.listFiles()) {
urls[i++] = pluginFile.toURI().toURL();
}

URLClassLoader ucl = new URLClassLoader(urls, PluginAdministration.class.getClassLoader());

List<Plugin> plugins = new ArrayList<>();

for (File pluginFile : pluginDirFile.listFiles()) {
JarInputStream jarInputStream = new JarInputStream(new FileInputStream(pluginFile));
JarEntry jarEntry;

while ((jarEntry = jarInputStream.getNextJarEntry()) != null) {
try {
if (jarEntry.getName().endsWith(".class")) {
Class<?> pluginClass = ucl.loadClass(jarEntry.getName().substring(0, jarEntry.getName().length()-6));
for (Class<?> it : pluginClass.getInterfaces()) if (it.getName().equals(Plugin.class.getName())) plugins.add((Plugin) pluginClass.newInstance());
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (NoClassDefFoundError e) {
e.printStackTrace();
}
}
}

return plugins;
}
}
[/CODE]

Der Output:
[CODE title="Output"]Applikation wird gestartet!
java.lang.NoClassDefFoundError: IllegalName: pluginsystem/PluginAdministration
at java.base/java.lang.ClassLoader.preDefineClass(ClassLoader.java:890)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1015)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:821)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:719)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:642)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:600)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:576)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at plugintestsystem.PluginAdministration.loadPlugins(PluginAdministration.java:50)
at Main.main(Main.java:24)
java.lang.NoClassDefFoundError: IllegalName: pluginsystem/Plugin
at java.base/java.lang.ClassLoader.preDefineClass(ClassLoader.java:890)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1015)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:821)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:719)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:642)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:600)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:576)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at plugintestsystem.PluginAdministration.loadPlugins(PluginAdministration.java:50)
at Main.main(Main.java:24)
java.lang.NoClassDefFoundError: IllegalName: plugintestsystem/PluginAdministration
at java.base/java.lang.ClassLoader.preDefineClass(ClassLoader.java:890)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1015)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:821)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:719)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:642)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:600)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:576)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at plugintestsystem.PluginAdministration.loadPlugins(PluginAdministration.java:50)
at Main.main(Main.java:24)
Plugin wurde erfolgreich gestartet!
Plugin wurde erfolgreich gestoppt!
Applikation wurde gestoppt!

Process finished with exit code 0
[/CODE]

Vielleicht habe ich das mit dem Pluginloading auch noch nicht so ganz verstanden...
Falls ihr noch was braucht um mir zu helfen gerne fragen.
Danke schonmal im voraus 🙂

LG
Markus
 
Aus dem bauch heraus "plugintestsystem/PluginAdministration" ist kein gültiger Klassennamen. Der Klassenname ist vermutlich "plugintestsystem.PluginAdministration"

Du müsstest, wenn du die Jar-Entries in Klassennamen umwandelst, noch die Slashes durch Punkte ersetzen.
 
Zum Laden aller Klassen, die ein bestimmtes Interface implementieren, gibt es die ServiceLoader-API. Selber die Jar durchgehen und alle Klassen laden und per Hand testen muss man dafür nicht. 🙂
 
Okay ich habe das mit dem ServiceLoader auch mal probiert:

[CODE lang="java" title="PluginManager.java"]package pluginsystemServiceLoader;

import pluginsystem.Plugin;
import plugintestsystem.PluginAdministration;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;

public class PluginManager {

final String pluginDir = "E:/ProgrammierProjekte/Intellij/Plugin/out/artifacts/Plugin_jar";

public List<Plugin> loadPlugins() throws MalformedURLException {
File pluginDirFile = new File(pluginDir);

if (!pluginDirFile.isDirectory()) {
System.err.println("Der angegebene Pfad ist kein Ordner!");
return null;
}
if (!(pluginDirFile.listFiles().length > 0)) {
System.err.println("Es gibt keine Plugins im Ordner!");
return null;
}

URL[] urls = new URL[pluginDirFile.listFiles().length];
int i = 0;

for (File pluginFile : pluginDirFile.listFiles()) {
urls[i++] = pluginFile.toURI().toURL();
}

URLClassLoader ucl = new URLClassLoader(urls, PluginAdministration.class.getClassLoader());

System.out.println(Arrays.toString(ucl.getURLs()));

List<Plugin> plugins = new ArrayList<>();

ServiceLoader<Plugin> services = ServiceLoader.load(Plugin.class, ucl);

for (Plugin plugin : services) {
plugins.add(plugin);
System.out.println("hi");
}

System.out.println("hiEnd");

return plugins;
}

}
[/CODE]

Jedoch funktioniert das nicht. Irgendeine Idee?

LG
Markus
 
Ich glaube, Du hast das mit der ServiceLoaderAPI etwas missverstanden.

Die Idee dabei ist doch, dass beim Aufruf die jar Dateien im ClassPath sind. Da diese Plugin jar Dateien alle eine entsprechende
META-INF/services/pluginsystem.Plugin Datei, die die Klassen auflistet, die das Plugin implementieren.

Da der Entwickler des Plugins weiss, was er a macht, kann er alles auflisten und dann kannst Du da entsprechend drauf zugreifen.

Bestes Beispiel ist die JDBC Schnittstelle. Das früher notwendige laden der Treiberklasse konnte so entfallen.

Also nicht mehr von Hand ein Plugin-Verzeichnis auswerten sondern einfach in den Classpath mit aufnehmen....
 
@kneitzel
Erstmal danke für deine Antwort. Nun kann schon sein, dass ich das etwas falsch verstanden habe ich habe das auch noch nicht so ganz verstanden... Wollte es nur mal mit dem ServiceLoader ausprobieren. Die servicedatei in dem META-INF/services/ Verzeichnis hatte ich auch schon angelegt. Und wegen dem ClassPath dachte ich, dass es reichen würde einen ClassLoader zu erstellen ihm das verzeichnis mit den Jar Dateien zu geben und den ClassLoader dann weiter an den ServiceLoader zu geben um nicht manuell den Classpath angeben zu müssen...

@mrBrown
Auch dir erstmal danke für deine Antwort. Auf deine Frage:

[CODE lang="java" title="Plugin.java"]import pluginsystem.Plugin;

public class MyPlugin implements Plugin {
public MyPlugin() {

}

@Override
public boolean start() {
System.out.println("Plugin wurde erfolgreich gestartet!");
SampleLib sampleLib = new SampleLib();
sampleLib.write("Plugin started!");
return true;
}

@Override
public boolean stop() {
System.out.println("Plugin wurde erfolgreich gestoppt!");
return true;
}
}[/CODE]
Dies ist mein TestPlugin in dem pluginDir habe ich sonst keine weiteren jars. Das mit der SampleLib war nur ein Test ob auch externe Librarys welche von Plugins implementiert werden geladen werden.

LG
Markus
 
Aha was dieses Forum alles so bringt 🙂 Ich hatte noch einen Tippfehler in der servicedatei jetzt wird das Plugin geladen jedoch kommt noch eine Exception wegen der implementierten SampleLib ich schaue mir die gleich mal an jedoch erstmal danke für die Hilfe.

LG
Markus
 

Neue Themen


Zurück
Oben