Java Plugin-System mit jar-Dateien

NetzUnity und Informatik-forum wurden zusammengelegt. Eine entsprechende Ankündigung wird demnächst noch folgen. Für 2025 ist hier einiges geplant! Bei Fragen bitte per DM an Maximilian Rupp wenden.
  • Gute Nacht :)

    Ich hab da eine Java-Anwendung die man per Plugins (JAR-Files, wo alles drin is was vom Plugin gebraucht wird) erweitern kann. Dh. die Applikation soll beim Starten den Ordner "plugins" scannen und man kann dann Plugins auswählen, deren Hauptklassen ein gemeinsames Interface implementieren. Hab im Internet nur prinzipielle Ansätze für Plugins gefunden, aber nix mit JAR-Dateien.

  • Was ist das Problem?

    In Aufgabe 2 von "Technologien für verteilte Systeme" war dieses Semester unter anderem "Dynamic Plugin Loading" zu implementieren. Da wurde ein bestimmtes Verzeichnis überwacht, ob Dateien hinzugekommen sind. Handelte es sich um eine .jar-Datei, wird diese analysiert:

    • JarFile hab ich eingesetzt, um eine Liste der im Archiv enthaltenen Dateien zu erhalten.
    • Der Name wird dann entsprechend angepasst ("." durch "/" ersetzt etc.); die Klasse mit diesem Namen wird dann mit einem URLClassLoader aus dem Archiv geladen.
    • Die Reflection API von Java bietet nun eine Reihe von Möglichkeiten, unter anderem, um herauszufinden, welche Interfaces eine bestimmte Klasse implementiert. Siehe Class. Wenn du irgendwelche Typhierarchien verwendest, musst du darauf achten, dass Class.getInterfaces() keine Interfaces zurückliefert, die von einem Obertyp implementiert werden.
  • Der uebliche Weg, um alle Klassen, die ein bestimmtes Interface implementieren, aus dem Jar File zu laden ist dieses Service Provider Pattern. Das hab ich auch heuer in "Technologien für verteilte Systeme" gelernt.

    Uebrigens:
    Ein wenig bloed ist, das diese Service Klasse in dem sun.* namespace liegt und nicht in java.* oder org.*. D.h. eigentlich sollte man von sun.* packages die Finger lassen, weil die nicht Teil der JDK Spec sind.. Ich habe da aber auch Diskussionen zu gefunden, und die Klasse wird wohl demnachst in einen oeffentlichen Namespace wandern.

    lg, Benjamin Ferrari, bookworm.at

    2 Mal editiert, zuletzt von a9bejo (5. Juli 2008 um 10:21)

  • hihi, das hat mich jetzt aber interessiert.
    hab mal schnell was zusammen gehackt:

  • Der uebliche Weg, um alle Klassen, die ein bestimmtes Interface implementieren, aus dem Jar File zu laden ist dieses Service Provider Pattern. Das hab ich auch heuer in "Technologien für verteilte Systeme" gelernt.

    hm.... service provider? warum nicht eine einfache variante davon nachprogrammieren?! eine "service.properties" datei in das plugin jar file reinhaun mit inhalt:

    Code
    pkg.plugininterfaces.ISub = pkg.plugin1.Sub1

    dann geht auch das:

    Einmal editiert, zuletzt von phudy (5. Juli 2008 um 10:48)

  • OK, das ist natuerlich ein guter Grund. Ich habe

    Zitat


    hm.... service provider? warum nicht eine einfache variante davon nachprogrammieren?!

    als einen Loesungsvorschlag fuer Stephes Problem gelesen, als bessere Variante gegenueber der fertigen Implementierung. Vielleicht wegen des entsetzten "?!" .

  • das funktioniert nur, wenn das plugin-jar bereits beim start der vm als teil des classpath angegeben wird.
    diese art der einbindung von plugins ist aber nicht wirklich schön und funktioniert zu laufzeit überhaupt nicht. dafür braucht man einen URLClassLoader, wie oben schon beschrieben.

    "All through my life I've had this strange unaccountable feeling that something was going on in the world, something big, even sinister, and no one would tell me what it was."
    "No," said the old man, "that's just perfectly normal paranoia. Everyone in the Universe has that."

    😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😒😓😔😖😘😚😜😞😠😡😢😣😥😨😩😪😫😭😰😱😲😳😵😶😷

  • Keine Ahnung was jetzt schön ist und was nicht aber ich habs jetzt so gelöst und es funktioniert wunderbar mit Jar-Archiven:

    (jarfiles ist ein String-Array mit zB. "Plugin1.jar", "Plugin2.jar")

    Code
    URL urlList[] = {new File("plugins/"+jarfiles[i]).toURI().toURL()};
    classLoader = new URLClassLoader(urlList);
    Class pluginclass = classLoader.loadClass("main.Main");
    Object plugin = pluginclass.newInstance();
    Method method_render = pluginclass.getMethod("render", new Class[]{});
    
    
    method_render.invoke(plugin, new Object[]{});

    Im Endeffekt hab ich jetzt ein openGL-Canvas in meiner Swing-Applikation und kann jetzt mittels Buttons die Plugins auswählen, die dann jeweils was anderes in dieses Canvas rendern. :bounce:

    Einmal editiert, zuletzt von Stephe (12. Dezember 2008 um 12:45)

  • theoretisch könntest du alle deine plugins auch ein gemeinsames interface implementieren lassen, dann könntest du die klasse aufs interface casten und könntest die methoden direkt aufrufen...
    würd ich jedenfalls so machen, weil ich reflection vermeide wo es nur geht.

    "All through my life I've had this strange unaccountable feeling that something was going on in the world, something big, even sinister, and no one would tell me what it was."
    "No," said the old man, "that's just perfectly normal paranoia. Everyone in the Universe has that."

    😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😒😓😔😖😘😚😜😞😠😡😢😣😥😨😩😪😫😭😰😱😲😳😵😶😷

  • also, ich hab mich jetzt einige zeit intensivst mit JPF auseinandergesetzt (vorm schlafen gehn denk ich immer nur ), und muss sagen dass es aeusserst einfach und bequem einzusetzen ist im vergleich zu anderen frameworks a la osgi. es ist sogar ein selbstgestecktes ziel von den JPF leuten nicht zuuu komplex zu sein.
    von daher: JPF :thumb:

    interessant koennten auch noch folgende blogeintraege sein ueber "sich-selber-ein-plugin-framework-schreiben":
    java-dynamic-loading-of-class-and-jar-file
    develop-a-java-plugin-framework
    develop-a-java-plugin-framework-search-for-plugin-dynamically

    bzw noch besser: du verwendest neue moeglichkeiten von java 6 mit hilfe des service providers:
    http://java.sun.com/developer/tech…ase/extensible/

    ---- edit ----
    wenn man java6 zur verfuegung hat, geht das mit dem ServiceLoader echt ur geschmeidig:

    1) jar bauen, welches eine datei /META-INF/services/com.example.ISomeServiceInterface mit inhalt com.example.plugin.SomeServiceImpl hat (natuerlich auch noch diese impl klasse korrekt erstellen)
    2) die hauptanwendung hat dann dieses jar im classpath und folgende zwei zeilen reichen dann aus um die angemeldeten service provider zu nutzen:

    Code
    ServiceLoader<ISomeServiceInterface> loader = ServiceLoader.load(ISomeServiceInterface.class);
    for (ISomeServiceInterface service : loader) { /* do something with service */ }

    Einmal editiert, zuletzt von phudy (27. Dezember 2008 um 00:43)

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!