Eine Art PlugIn-System

M_Kay

Mitglied
Hi,

ich habe vor eine Art PlugIn-System zu schreiben.
Ich habe mich dabei an folgende Seite gehalten: http://www.javangelist.de/space/Plugin

In der META-INF/services stehen nun die Klassen drin, die geladen/unterstuetzt werden sollen.
Auf der Seite ist auch beschrieben, wie ich mittels Iterator zB durch alle Klassen gehen und Methoden aufrufen kann.
Komisch finde ich, dass in dem Beispiel eine nicht statische Funktion direkt über die Klasse (oder doch nicht?) aufgerufen wird.
Wie kann ich jetzt Objekte der geladenenen Klassen erstellen, oder ist das nicht moeglich?

Gruss
M_Kay

EDIT: Ich sehe gerade, dass es die schicke funktion Class.newInstance() gibt. Mal schauen ;)
 
Zuletzt bearbeitet:

takidoso

Erfahrenes Mitglied
Hallo M_Kay,
ist je eine interssante Seite!, Früher habe ich mir so eine Art Klassenfinder aus alten Sourcen, die von "Weiß der Henker" kamen zusammen gebastelt.

Mal 'ne Blöde Frage: ist die Klasse Service in Java 6 eigetnlich immernoch im Paket sun.misc oder wurde da ein algemeineres Paket nun ersonnen? (habe da die Diskusion der Bug-Seite angesehen, da steht drauf fixed, aber nicht welches Paket)
Benötigt man eigetnlich, wenn man außerhalb des Klassenpfades Bibliotheken als Plugins anziehen will auch noch eine Datei in META-INF/service oder kann man sich die dann schenken?

mit neugierigen Grüßen

Takidoso

PS: hier ein Link der ähnliches diskutiert http://www.tutorials.de/forum/java/299124-plugins-f-r-eigenes-programm.html
 
Zuletzt bearbeitet:

M_Kay

Mitglied
Argh, ich habe die Struktur meines (noch recht winzigen) Programms überarbeitet und schon will der keine Services mehr laden :X

Code:
    public static ArrayList<ProtocolDownload> loadDownloadProtocols() {
        ArrayList<ProtocolDownload> pd = new ArrayList<ProtocolDownload>();
        
        Iterator iterator;
        iterator = Service.providers(ProtocolDownload.class);
        
        while(iterator.hasNext()) {
            ProtocolDownload dl = (ProtocolDownload) iterator.next();
            pd.add(dl);
            
            //TODO entf
            System.out.print(dl.getClass().getName());
        }
        
        return pd;
    }
Beim ersten Befehl in der While-Schleife bricht er mit folgendem Befehl ab:
Code:
Exception in thread "main" sun.misc.ServiceConfigurationError: mload.model.download.ProtocolDownload: Provider mload.model.download.protocols.HttpDownload could not be instantiated: java.lang.InstantiationException: mload.model.download.protocols.HttpDownload
    at sun.misc.Service.fail(Service.java:120)
    at sun.misc.Service.access$200(Service.java:111)
    at sun.misc.Service$LazyIterator.next(Service.java:276)
    at mload.model.Init.loadDownloadProtocols(Init.java:28)
    at mload.controller.MLController.load(MLController.java:45)
    at mload.MLoad.main(MLoad.java:24)
Caused by: java.lang.InstantiationException: mload.model.download.protocols.HttpDownload
    at java.lang.Class.newInstance0(Class.java:340)
    at java.lang.Class.newInstance(Class.java:308)
    at sun.misc.Service$LazyIterator.next(Service.java:271)
    ... 3 more
Er findet scheinbar die gewünschte Klasse, doch kann ich mit der Fehlermeldung nichts anfangen (vorher hats doch funktioniert :confused:) :suspekt:

Kann mir jemand helfen?
 

M_Kay

Mitglied
Ok, es lag scheinbar daran, dass die Klasse, die geladen werden sollte, keinen Konstruktor ohne Parameter hatte (komisch, vorher hatte es funktioniert).

Gibt es eine Möglichkeit eben das zu erreichen? Also dass es mit Konstruktor funktionert, die Parameter enthalten?
 

zeja

Erfahrenes Mitglied
Direkt geht das offenbar nicht mit sun.misc.Service ist aber ja recht leicht selber zu schreiben...

Lies halt die Datei die die Pluginnamen definieren selber per ClassLoader aus, lade die Klassen und erstelle entsprechende Instanzen mit den gewünschten Parametern.
 

M_Kay

Mitglied
Hi,

könntest du das evtl mit einem kleinen Beispiel erläutern? :)

Bisher habe ich halt die Klassen über die Service Methode geladen und später dann mit Klasse.getConstructor und Klasse.newInstance(param) die Instance mit Parametern erstellt.
Aber ich musste wie gesagt einen Konstruktor ohne Parameter erstellen, auch wenn dieser leer ist, da er sonst in der o.g. Schleife einen Fehler ausgibt.

Gruss
M_Kay
 

zeja

Erfahrenes Mitglied
Soo... nun:

Das Interface:
Java:
package de.tutorials;

public interface IPlugin {
	
	public void print();

}


Eine Implementierung:
Java:
package de.tutorials.plugin;

import de.tutorials.IPlugin;

public class Plugin implements IPlugin {
	
	private int i;
	private String strg;

	public Plugin(int i, String strg){
		this.i = i;
		this.strg = strg;
	}

	@Override
	public void print() {
		System.out.println(i + " " + strg);
	}

}

Und nun das Laden:
Java:
package de.tutorials;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;

public class PluginLoader {

	public static void main(String[] args) throws Exception {
		URLClassLoader loader = new URLClassLoader(new URL[] { new File(
				"Plugin.jar").toURI().toURL() });
		final Enumeration<URL> findResources = loader
				.findResources("META-INF/services/plugins");
		while (findResources.hasMoreElements()) {
			final URL url = findResources.nextElement();
			loadSpecifiedClasses(loader, url);
		}
                reader.close();
	}

	private static void loadSpecifiedClasses(URLClassLoader loader,
			final URL url) throws IOException, ClassNotFoundException,
			NoSuchMethodException, InstantiationException,
			IllegalAccessException, InvocationTargetException {
		final InputStream stream = url.openStream();
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(stream));
		String line = null;
		while ((line = reader.readLine()) != null) {
			loadClass(loader, line);
		}
	}

	private static void loadClass(URLClassLoader loader, String className)
			throws ClassNotFoundException, NoSuchMethodException,
			InstantiationException, IllegalAccessException,
			InvocationTargetException {
		final Class<?> clss = loader.loadClass(className);
		final Constructor<?> construct = clss.getConstructor(int.class,
				String.class);
		final IPlugin newInstance = (IPlugin) construct.newInstance(1, "Hello");
		newInstance.print();
	}

}
 

M_Kay

Mitglied
Argh, ich bekomms nicht hin.
Die Klassen liegen ja in meinem Fall im Projekt selbst, nicht in einem externen jar.
Code:
            URL thisurl = new File("./").toURI().toURL();
            URLClassLoader loader = new URLClassLoader(new URL[] {thisurl});
            
            final URL findResources = loader.findResource("/META-INF/service/mload.model.download.ProtocolDownload");
Das möchte irgendwie nicht funktionieren :suspekt:
findResources ist bei mir aber immer null.

Kann mir jemand helfen?
 

zeja

Erfahrenes Mitglied
Dann mach nen eigenes Jar von. Das macht ja keinen Sinn das Plugins in dem Hauptprojekt mit drinliegen...

Danach mußt du dich vergewissern dass in deinem Jar unter META-INF/services auch tatsächlich die gesucht Datei liegt. Diese heißt immer gleich. IN der Datei stehen dann die Namen der zu ladenden Plugins.