Best Practice Wie am besten Plugin-System erstellen?

Joew0815

Bekanntes Mitglied
Hi,

Ich habe eine Frage die ihr mir bestimmt beantworten könnt 😉

Erstmal zu dem was ich machen will:
- 1 Hauptprogramm
- n Plugins
Ich möchte die Plugins dynamisch laden können + meine Klassen aus dem Plugin mit vollqualifizierten Namen ansprechen.

Unter "normalen" Windowsanwendungen kenn ich das so, dass man einfach eine dll austauscht und man hat eine andere Implementierung der dll, ohne das man das Programm neu compilieren muss.

d.h. konkret ich möchte mit meinen Klassen aus den Plugins arbeiten können, ohne sie in Eclipse als Library hinzufügen zu müssen und ohne einen Import machen zu müssen.

Also existiert eine jar, wird sie geladen und benutzt, wenn nicht, dann nicht.

Ich habe bisher folgende Idee:
Ich habe mehrere Plugin-Projekte, ein Hauptprojekt und ein "API-Projekt". Das Api-Projekt enthält nur Interfaces. Sowohl in den Plugins, als auch im Hauptprogramm hab ich das API-Projekt gelinkt damit ich eben eine gemeinsame "Schnittstelle" habe.

Ich beschreibe mal meinen bisherigen Ansatz:

Hauptprogramm (gekürzt) :
Java:
import de.api.ITransactions;

public class Main {

	public static void main(String[] args) throws MalformedURLException {
		System.out.println("load jars...");
		File plugin1 = new File("Pfad/zum/plugin1.jar");
		File plugin1 = new File("Pfad/zum/plugin2.jar");
		URL[] url = {plugin1.toURI().toURL(), plugin2.toURI().toURL()};
		URLClassLoader ucl = new URLClassLoader(url);
		
		final Iterator<ITransactions> iter = ServiceLoader.load(ITransactions.class, ucl).iterator();
		System.out.println("load classes...");
		while (iter.hasNext()) {
			final ITransactions plugin = iter.next();
			System.out.println("output...");
			plugin.doSomething();
		}
	}

}
ITransactions ist das Interface, welches aus dem API Projekt kommt und von den Plugins + Hauptprogramm verwendet wird.

plugin1 (gekürzt) :
Java:
import de.api.ITransactions;

public class HelloFromPlugin1 implements ITransactions {
	
	@Override
	public void doSomething() {
		System.out.println("Hello from Plugin1");		
	}

}
naja...nichts besonderes... 😉
Im Plugin habe ich jedoch noch einen META-INF Ordner (innerhalb des src Ordners). Dort liegt ein Ordner "services". Dort liegt eine Datei mit den Namen "de.api.ITransactions" mit dem Inhalt "de.plugin1.HelloFromPlugin1" -> Damit das Hauptprogramm weiß welche Klasse es jetzt konkret instanziieren soll.

API (der Vollständigkeithalber) :
Java:
package de.api;

public interface ITransactions {
	
	public void doSomething();

}

Ok, ich denke jetzt habe ich alle Infors beisammen 😉
(Das Hauptprogramm hat keinen import von einem plugin!)

Dann exportiere ich die Plugins mittels Eclipse in jar-Dateien genau an den Pfad wie im Hauptprogramm angegeben.
Das hier gepostete Plugin-System funktioniert auch. Ich bekomme von jedem Plugin die "Hello ..." Ausgabe. alles super dachte ich 😉

Jetzt mein konkretes Problem:
In einem der Plugins nutze ich Librarys (.jar) von Drittanbietern. Im Plugin als Library eingebunden und mittels Imports werden die benutzt. Wenn ich jetzt das Plugin nutzen will, bekomme ich eine ClassNotFoundException im Hauptprogramm. Irgendwas was wie "Kann Klasse HelloFromPlugin1 nicht instanziiere: ClassNotFoundException, cannot load class KlasseEinesDrittanbieters".

Das eigentliche Problem habe ich verstanden, habe jedoch keine Lösung dafür 🙁
Scheinbar läd das Hauptprogramm die .class Datei aus dem plugin1.jar und will die imports auflösen, kennt aber selber die Drittanbieter-jars nicht.

Kann mir jemand helfen?

Ist mein Mechanismuss zu "kompliziert" und es geht viel einfacher?

gruß und danke!

P.S.:
Zum Hintergrund: Das Programm soll "Daten verarbeiter" und die Plugins sollen die Quellen abstrahieren. zb. ein Plugin für eine DB, ein Plugin für Datein, ein Plugin zu einer externen API (hier kommen Drittanbieterlibrarys ins Spiel 😉 ). Dem Hauptprogramm soll es aber egal sein wo die Daten herkommen. Falls jemand eine bestimme Quelle nicht nutzt, dann bekommt er auch das Plugin nicht geliefert und dann wär es ärgerlich wenn ich trotzdem das SDK mit ausliefern muss nur um das Hauptprogramm nichtmehr neucompilieren zu müssen.
 
Also Eclipse kann sowas ja hervorragend.

Vielleicht solltest Du direkt eine Eclipse-Andendung aus Deinem Programm machen...

Zum Abkupfern kannst Du Eclipse natürlich auch hernehmen, ist ja Quelloffen...

Ansonsten scheints auf einen eigenen [JAPI]URLClassLoader[/JAPI] hinaus zu laufen.

bye
TT
 
Habe keinen der beiden Threads gelesen.

sry wenn dir die Frage zu noobig ist aber es muss doch eine Möglichkeit geben das Problem zu lösen?

ich mag mich jetzt auch ungern in irgendwelche Eclipse-Anwendungs-Erstellungs-Themen einlesen.

wie läuft das denn bei anderen Technologien? Mit C++ kann ich auch auch einfach so ein .so Datei austauschen und es läuft

kann ich das DrittanbieterSDK einfach in mein plugin.jar mit reincompilieren?

gruß und danke!
 
ich mag mich jetzt auch ungern in irgendwelche Eclipse-Anwendungs-Erstellungs-Themen einlesen.
Wäre aber gut für Dich.

wie läuft das denn bei anderen Technologien? Mit C++ kann ich auch auch einfach so ein .so Datei austauschen und es läuft
aber nur, wenn die statisch gelinkt sind. Sonst hast Du da sogar noch größere Probleme...

kann ich das DrittanbieterSDK einfach in mein plugin.jar mit reincompilieren?
Das Problem ist der Classpath.
Du kannst Deinen Plugins vorschreiben, welche externen Libs sie einsetzten dürfen und nimmst diese in deinen Classpath auf auch wenn Du sie nicht selbst brauchst.

Oder Du wirfst mal einen Blick hier drauf: jspf - Java Simple Plugin Framework. 5 minutes and it works. No XML. - Google Project Hosting

bye
TT
 
habs jetzt hinbekommen, ziemlich einfach sogar.

ich hab die Drittanbieter JARs entpackt und die .class Dateien in mein plugin hinzugefügt. In Eclipse dann den Pfad bekannt gegeben ("Add Class Folder"). Damit wird das SDK ins plugin hineingepackt.

Läuft einwandfrei.

Hat den Nachteil das man das SDK nicht einfach updaten kann und man dazu in der plugin.jar rumkopieren muss, aber ist ok.

Trotzdem danke für eure Hilfe, dachte es gibt vl eine einfache "Einstellung" oder sowas.

Ob mich jetzt RCP schneller ans Ziel gebracht hat oder nicht...hab leider keine Zeit mich in etwas mit "unbekannter Größe" einzulesen.

P.S.: Das vorgeschlagene Pluginframework hab ich auch geteste und ging auch nicht, hatte das selbe Problem (wird wahrscheinlich genauso laufen wie ichs am anfang hatte)
 
ich hab die Drittanbieter JARs entpackt und die .class Dateien in mein plugin hinzugefügt. In Eclipse dann den Pfad bekannt gegeben ("Add Class Folder"). Damit wird das SDK ins plugin hineingepackt.

oh oh ... da musst du aber ganz gewaltig aufpassen ob dies auch in der lizenz des anbieters so ausdrücklich erlaubt ist ... denn wenn du die daten auspackst und in dein jar wieder mit reinpackst verkaufst du die daten als dein eigentum ... was es ja nicht ist ... wesshalb libs grundsätzlich extern liegen sollte ... es sei denn wie gesagt in der lizenz ist diese vorgehensweise ausdrücklich erlaubt worden ...


zur eigentlichen problemlösung

normalerweise ist der URLClassLoader in der lage abhängigkeiten aufzulösen wenn diese im classpath des moduls liegen ...

verlangt als Plugin1 nach Lib1 ... die so selbst aber NICHT in Main verlinkt ist (also nicht im classpath steht) schreibt man Lib1 nur ins manifest von Plugin1 ... und der URLClassLoader sollte das dann normalerweise auch hinbekommen ... zumindest funktioniert das bei mir ...

ich gehe von aus das du von Main aus gesehen ein sub-dir namens "plugins" haben wirst und dieses auch im classpath steht (dann kannst du dir den URLClassLoader komplett sparen) ... dann leg doch einfach noch ein sub-dir "libs" an und pack da alle nötigen libs rein ... dann werden diese wie die plugins selbst vom system-classloader geladen und alles sollte laufen

wenn das alles nichts hilft gehts auch noch ganz krass : deine plugins selbst so umbauen das diese nicht statisch mit den libs verlinkt sind sondern diese ebenfalls modular laden und nutzen ... das ergibt dann zwar ne schöne classloader-chain .. aber wäre auch umsetzbar ...

das dich die suche nach "java plugin system" nicht weitergebracht hat ist mir klar ... denn die meisten foren-posts befassen sich mit den grundlagen wie man sowas überhaupt baut ... allerdings nicht mit dem problem was du hast abhängige libs zu laden ... oder zumindest wird dies nur sehr selten angesprochen ...

es gehört schon etwas mehr dazu sowas auf die beine zu stellen als nur n bissl gegoogle ... man muss halt auch mal selbst handanlegen und sich was einfallen lassen ...
ds krasseste wäre wohl : FAT-JAR ... und sich dann immer die mühe machen da dran rumzubasteln ...
 
Ob mich jetzt RCP schneller ans Ziel gebracht hat oder nicht...hab leider keine Zeit mich in etwas mit "unbekannter Größe" einzulesen.

Ja, mir ist durchaus bewusst, dass der Aufwand manchmal zu groß ist sich etwas neues anzueignen. Aber für die Zukunft lohnt es sich sicherlich, wenn du dich da mal ein paar Stunden einliest und einfache Tutorials durchspielst. Denn gerade für etwas größere Anwendungen lohnt es sich darüber nachzudenken Eclipse RCP zu nutzen. Da hat man nämlich schon viele Konzepte aus der Eclipse-Welt umgesetzt und muss einfach anfangen den "Rahmen zu füllen".
 
...soso, ein PlugIn für Dateien...
@TO: Also wenn du Marcineks Link gefolgt bist und dich auch nicht gescheut hast, auch einigen Sublinks zu folgen, dürfte dir mein Werk wohl nicht entgangen sein. Das ist zwar nicht aktuell, weil ich grad' (seit einiger Zeit) gründlich "umbaue", dass alles, was "java.awt" und "javax.sound" angeht aus der Lib verschwindet, aber der Lademechanismus für einzelne Dateitypen hat sich dabei zumindest nicht ein bisschen geändert.

Gerade was Dateitypen angeht (und das gilt dummerweise nicht nur für Java) wird überall der selbe Kardinalfehler begangen. Man baut sich Reader und Writer für in seiner API verwendete Dateitypen lieber selbst, als sich dafür einem PI-System zu bedienen. So hat man am Ende 1000 verschiedene APIs, die jeweils auf ihre eigene Art ein und den selben Dateitypen laden. Obendrein kann man diesen Dateitypen dann auch nur mit dieser API verwenden und die API widerum läuft nicht auf jeder Plattform. Bisher hatte ich mir als Beispiel immer ImageIO rausgepickt ("schön, es lädt Bilder... kann ich das auf Android nutzen?") aber Fonts schlagen das Ganze noch mal um Längen. Loader in Fontklasse ohne SPI integriert und Rasterizer native... bei Android ist es nicht anders. Kein Mensch denkt auch nur entfernt daran, dass es auch noch andere Schriftarten ausser TrueType und Type1 Fonts gibt und ein Support für diese... keine Chance.

Aber Langer Rede kurzer Sinn... Du ermöglichst Anwendern mit deinem PI-System das Nachladen von PlugIns, welche von Drittanbietern entwickelt werden können. Welche Abhängigkeiten oder Vorrausetzungen Drittanbieter für ihre PIs setzen ist nicht Sache deines PI-Systems. Ich z.B. versuche mein PI-System weitestgehend unabhängig zu halten, so dass man Bilder, Schriftarten usw. auch mal ohne Java2D oder ähnlichem geladen bekommt.
 
Zuletzt bearbeitet von einem Moderator:

Zurück
Oben