Plugins / Zugriff auf den Code des Hauptprogramms?

antimon

Mitglied
Hallo zusammen,

um mich mit den ganzen Plugin-Konzepten auseinanderzusetzen, habe ich folgendes Tutorial für den Einstieg verwendet: http://www.java-blog-buch.de/d-plugin-entwicklung-in-java/

Das funktioniert soweit auch, allerdings habe ich einige Verständnisprobleme:
Angenommen, es gibt zwei Projekte in Eclipse, eines für das Hauptprogramm, eines für das Plugin. Das Plugin hält sich an irgendein Interface und kann damit vom Hauptprogramm gesteuert werden. Wenn aus dem Plugin eine JAR erzeugt wird, kommt die im Hauptprogramm in das Plugins-Verzeichnis und wird von dort geladen.
Was ist nun aber, wenn ich aus dem Plugin auf den Code des Hauptprogramms zugreifen möchte? Beispielsweise weil ich ein Statusfenster instanzieren möchte, dessen Quelltext im Hauptprogramm definiert ist?
Oder ist davon abzuraten, weil sich der Quelltext des Hauptprogramms ändern könnte und das Plugin dann nicht mehr funktioniert?
Falls ja - wie sollte beispielsweise folgendes Szenario behandelt werden:
Das Hauptprogramm verwendet abstrakte Klassen, die von JPanel abgeleitet sind und zusätzliche Methoden (load()/save()) vorschreiben. Diese sind dafür da, die Einstellungen des Programms zu bearbeiten - da die Plugins ebenfalls veränderbare Einstellungen haben können, sollen diese auch die abstrakten Klassen verwenden. Aber dazu müssten sie den Quelltext kennen...

In Eclipse kann ich ja im Plugin das Hauptprojekt referenzieren - allerdings, wenn ich dann eine JAR für das Plugin erstellen möchte (per ANT-Task) meckert er, weil er die Klassen nicht findet - nutze ich die Export-Funktion von Eclipse für JARs, funktioniert es auch nicht. Woran könnte das liegen - kann das prinzipiell funktionieren? Klar, im Moment des Kompilierens sind ja nur die Plugin-Klassen vorhanden, die Hauptprogramm-Klassen gibt es ja erst wenn das Plugin vom Hauptprogramm gestartet wurde...

Und wie kann ich effizient Plugins entwickeln? Muss ich erst das Plugin-Projekt in eine JAR packen, die dann ins Plugin-Verzeichnis der Hauptanwendung kopieren, diese starten und dann schauen ob der Code läuft? Oder kann ich zu Debugging-Zwecken den Plugin-Code direkt (also ohne JAR) einbinden und testen und erst wenn alles läuft, eine JAR draus bilden?

Hoffe ich habe nicht zu verwirrend geschrieben, ist schon ziemlich spät... ;)
 
Hallo,

schau dir hier mal das Beispiel zum ServiceLoader an:
http://www.tutorials.de/forum/java/357126-wieder-mal-java-und-plug-ins.html
http://www.tutorials.de/forum/java/310207-eine-art-plugin-system.html
Das ist ein sehr einfacher Plugin-Mechanismus mit Standard Java API ohne manuelle ClassLoader-Geschichten.

Natürlich gibts noch zahlreiche andere Plugin-Systeme (osgi, was eigenes mit URLClassLoader etc.)
Diese sind IMHO aber oft ziemlich Umständlich zu benutzen und bieten oftmals funktionalität die man gar nicht benötigt.

Was ist nun aber, wenn ich aus dem Plugin auf den Code des Hauptprogramms zugreifen möchte? Beispielsweise weil ich ein Statusfenster instanzieren möchte, dessen Quelltext im Hauptprogramm definiert ist?
Das Plugin hat doch auch die Klassen vom "Hauptprogramm" im Classpath und kann von daher auch auf dessen Klassen zugreifen.

Oder ist davon abzuraten, weil sich der Quelltext des Hauptprogramms ändern könnte und das Plugin dann nicht mehr funktioniert?
Da ist die Frage wie generisch der Code sein kann. Wenn du nur mit groben Schnittstellen arbeitest und die Semantik / den Contract weitestgehend
einhälst und keine Implementierungsdetails nach ausen leaken lässt dann kannst du auch Änderungen an den Implementierung im "Hauptprogramm" machen.

In Eclipse kann ich ja im Plugin das Hauptprojekt referenzieren - allerdings, wenn ich dann eine JAR für das Plugin erstellen möchte (per ANT-Task) meckert er, weil er die Klassen nicht findet - nutze ich die Export-Funktion von Eclipse für JARs, funktioniert es auch nicht. Woran könnte das liegen - kann das prinzipiell funktionieren?
Dein Ant-Taks für die Plugins muss dann so gestrickt sein, dass du die Klassen aus dem Hauptprogramm auch zur Compile-Zeit verfügbar hast.

Klar, im Moment des Kompilierens sind ja nur die Plugin-Klassen vorhanden, die Hauptprogramm-Klassen gibt es ja erst wenn das Plugin vom Hauptprogramm gestartet wurde...
Du musst das Hauptprogramm natürlich zuerst bauen... das Hauptprogramm sollte natürlich nicht von den Plugins abhängig sein ...

nd wie kann ich effizient Plugins entwickeln? Muss ich erst das Plugin-Projekt in eine JAR packen, die dann ins Plugin-Verzeichnis der Hauptanwendung kopieren, diese starten und dann schauen ob der Code läuft? Oder kann ich zu Debugging-Zwecken den Plugin-Code direkt (also ohne JAR) einbinden und testen und erst wenn alles läuft, eine JAR draus bilden?

Klar. Beispielsweise kannst du in deinem Plugin-Projekt eine launch-Konfiguration erstellen in dem die dein Haputprogramm mit dem entsprechenden Plugin ausführst. Je nachdem wie dein Mechanismus ausschaut um Plugins zu finden musst du gegebenenfalls noch weitere Einstellungen mitgeben (wo findet das System Plugins... etc.)

Gruß Tom
 
Hallo Thomas, danke schon mal für deine schnelle und kompetente Antwort!

Du hast mir schon sehr weitergeholfen - allerdings bin ich mir noch nicht ganz sicher, wie ich das Ganze einschätzen soll... wenn ich mir den Code aus deinem Beispiel (den ich mir übrigens vorher schon zu Gemüte geführt habe) ansehe, muss ich sagen dass das doch aufwändiger und komplizierter scheint, als wie in dem Tutorial mit dem eigenen ClassLoader, von dem ich den Link gepostet habe. Was hat deine Variante für Vorteile?

Mit der Funktionalität, die man oft nicht braucht, gebe ich dir vollkommen Recht. Denn das ist es auch, was mich bisher immer von OSGI & Co. abgeschreckt hat. Wobei ich allerdings denke, dass ich früher oder später um OSGI nicht herumkomme, denn wenn es dann um Plugin-Dependencies etc. geht, dürfte eine eigene Geschichte nicht mehr rentabel sein, weil der Entwicklungsaufwand zu sehr in die Höhe schnellt.

Vielleicht kannst du mir da auch ein wenig Klarheit verschaffen, da der Ansatz in deinem Beispiel auch vorkommt... momentan stelle ich mir meine Applikation so vor, dass sie an definierter Stelle im Programm anfängt, die Plugins zu laden. Verwendet man hingegen OSGI beispielsweise, hat man eine Plattform und selbst das "Hauptprogramm" ist ein Plugin/Modul, das von der Plattform zusammen mit den anderen Plugins geladen wird - ist das richtig?

Könntest du mir eine Empfehlung aussprechen, wie ich mich am besten einarbeite? Also das Ziel ist es, eine Anwendung auch um "große" Plugins/Module erweiterbar zu machen, nicht nur ein paar kleine Features, die durch Plugins hinzukommen sollen. Macht es Sinn, erst mal selbst was zu coden, um das Prinzip zu verstehen und dann ein größeres Framework nutzen zu können? Oder sollte ich mich eher gleich "richtig" mit OSGI befassen?

Vielleicht dazu noch eine, nicht ganz zum Thema passende Frage: Das Programm soll eine Update-Funktion bekommen. Normalerweise würde ich ein eigenes Programm dafür schreiben, dieses starten, welche die JAR des Hauptprogramms aus dem Internet lädt und einfach tauscht - mit den Plugins könnte ich ja nur einzelne Module tauschen, die aktualisiert wurden - sehe ich das richtig?
 
Hallo,

Du hast mir schon sehr weitergeholfen - allerdings bin ich mir noch nicht ganz sicher, wie ich das Ganze einschätzen soll... wenn ich mir den Code aus deinem Beispiel (den ich mir übrigens vorher schon zu Gemüte geführt habe) ansehe, muss ich sagen dass das doch aufwändiger und komplizierter scheint, als wie in dem Tutorial mit dem eigenen ClassLoader, von dem ich den Link gepostet habe. Was hat deine Variante für Vorteile?

Ich finde sie einfacher :) Mit 3-Zeilen Code / Konfiguration hat man einen einfachen Plugin Mechanismus der wohl für die meisten
Anwendungen vollkommen ausreichend ist.

Hier unsere Hauptanwendung:

Java:
package de.tutorials.app;

import java.util.ServiceLoader;

import de.tutorials.app.plugins.Plugin;

public class Main {
    public static void main(String[] args) {
        System.out.println("Starting application...");
        
        System.out.println("Starting plugins...");
        for(Plugin plugin : ServiceLoader.load(Plugin.class)){
            plugin.start();
        }
        
        System.out.println("Application started.");
    }
}

Plugin Interface:
Java:
package de.tutorials.app.plugins;

public interface Plugin {
    void start();
}

Neues Java Projekt (PluginA) anlegen und dieses Projekt vom Hauptprojekt abhängig machen.
Plugin-Projekt PluginA:
Java:
package de.tutorials.app.pluginA;

import de.tutorials.app.plugins.Plugin;

public class PluginA implements Plugin{
    public void start() {
        System.out.println("Starting: " + this.getClass());
    }
}

META-INF/services/de.tutorials.app.plugins.Plugin Descriptor:
Java:
de.tutorials.app.pluginA.PluginA

Um zum testen aus der Eclipse IDE starten zu können müssen wir folgendes einstellen:
Launch Konfiguration für Java-Projekt PluginA erstellen.
Als "Main-class" geben wir de.tutorials.app.Main an. Bei classpath fügen wir unter Advanced
das PluginA Projekt-Root Verzeichnis hinzu und klicken dann auf Run.

Ausgabe:
Code:
Starting application...
Starting plugins...
Starting: class de.tutorials.app.pluginA.PluginA
Application started.

Wie die Anwendung aus der Konsole gestartet werden kann wird in den anderen Threads erklärt.
Hier gibts auch noch eine Variante bei der die Anwendung sich die Plugins zusammen suchen kann:
http://www.tutorials.de/forum/java/358931-services-dynamisch-laden.html

Gruß Tom
 
Zurück