# Alle Klassen ermitteln, die Interface implementieren / Reflection



## reflector4sure (18. Jan 2012)

Hi,

wie kann ich in herausfinden, welche Klassen ein bestimmtes Interface implementieren? Angeblich soll es mit Java Reflection funktionieren, habe aber noch nicht herausgefunden wie. Hat jemand vielleicht ein Beispiel parat oder ist die Antwort auf meine Frage gar nicht so banal? 

Danke euch.


----------



## irgendjemand (18. Jan 2012)

java.lang.reflect.Class.getInterfaces()


----------



## tfa (18. Jan 2012)

> wie kann ich in herausfinden, welche Klassen ein bestimmtes Interface implementieren?


Gar nicht. Es geht nur umgekehrt.


----------



## Gast2 (18. Jan 2012)

Wenn du das Class Objekt hast, dann kannst du einfach getInterfaces() aufrufen:
Class (Java 2 Platform SE 5.0)


----------



## irgendjemand (18. Jan 2012)

du müsstest also über alle klassen die der ClassLoader geladen hat *kommt man da überhaupt ran ?* durchiterieren und prüfen ob das im returnten Class-array mit [c]getSimpleName()[/c] der name deines interfaces auftaucht


----------



## reflector4sure (18. Jan 2012)

java.lang.reflect.Class gibt es nicht.


----------



## tfa (18. Jan 2012)

Es heißt java.lang.Class.
Damit kannst du alle Interfaces ermitteln, die eine bestimmte Klasse implementiert. Aber ich glaube, das wolltest du nicht.


----------



## reflector4sure (18. Jan 2012)

Das ist wirklich nicht das, was ich möchte. Ich habe in meinem Package z.B. 5 Klassen, die ein bestimmtes Interface implementieren. 

Das würde ich gerne geschickt machen, ohne explizit in Eclipse alle Klassen nachzuschauen, welche Klasse was für ein Interface hat.


----------



## tfa (18. Jan 2012)

D.h. du kennst alle Klassen, die du überprüfen willst?


----------



## Gast2 (18. Jan 2012)

Hm, hört sich merkwürdig an. Wofür genau brauchst du das?


----------



## knoppers (18. Jan 2012)

reflector4sure hat gesagt.:


> Das ist wirklich nicht das, was ich möchte. Ich habe in meinem Package z.B. 5 Klassen, die ein bestimmtes Interface implementieren.
> 
> Das würde ich gerne geschickt machen, ohne explizit in Eclipse alle Klassen nachzuschauen, welche Klasse was für ein Interface hat.




Wenn du dieses nur in der Eclipse wissen möchtest, dann öffne dein Interface, öffne das Kontextmenü und klicke auf "Open Type Hierachy". Dann werden dir alle Klassen angezeigt, die dieses Interface implementieren.

Wenn das nicht deine Frage beantwortet, dann solltest du deine Frage etwas genauer beschreiben was du machen möchtest.


----------



## reflector4sure (18. Jan 2012)

Ja, ich kenne sie, da ich explizit im Package nachgeschaut habe, welche Klasse sie implementiert.

Wofür ich das brauche? Ich möchte eine bestimmte Operation mit der Klasse durchführen, falls sie genau das Interface auch wirklich implementiert.


----------



## knoppers (18. Jan 2012)

Wenn ich deine Sache jetzt richtig interpretiere würde doch schon ...


```
if ( klasse instanceof interface) {
      macheirgendetwas...
}
```

vollkommen ausreichen.


----------



## irgendjemand (18. Jan 2012)

reflector4sure hat gesagt.:


> java.lang.reflect.Class gibt es nicht.



ja sorry ... habe mich in der doc verklickt ... dachte wäre in java.lang.reflect .. war aber in werklichkeit nur in java.lang ... und natürlich gibt es java.lang.reflect.Class nicht *warum ist mir das nicht aufgefallen ?* und natürlich muss es java.lang.Class heißt ...

da aber java.lang das base-package ist *z.b. Object, Class, System, Runtime, Thread, Throwable, Exception und Error* braucht man dies nicht explizit implementieren


wofür du das nun aber brauchst wird leider trotzdem nicht klar ...

@knoppers
VORSICHT : funktioniert NUR wenn du auch wirklich ne instanz einer klasse hast ...
bei Class.getInterfaces() reicht es wenn du durch ein objekt von Class<?> eine referenz auf die klasse selbst im ClassLoader hast ... brauchst also keine instanz der klasse selbst *was z.b. bei factory-klassen unmöglich wäre wenn diese nach singleto-pattern mit privatem null-konstruktor programmiert sind*


----------



## knoppers (18. Jan 2012)

irgendjemand hat gesagt.:


> @knoppers
> VORSICHT : funktioniert NUR wenn du auch wirklich ne instanz einer klasse hast ...
> bei Class.getInterfaces() reicht es wenn du durch ein objekt von Class<?> eine referenz auf die klasse selbst im ClassLoader hast ... brauchst also keine instanz der klasse selbst *was z.b. bei factory-klassen unmöglich wäre wenn diese nach singleto-pattern mit privatem null-konstruktor programmiert sind*



Stimmt.


----------



## bygones (18. Jan 2012)

reflector4sure hat gesagt.:


> Ja, ich kenne sie, da ich explizit im Package nachgeschaut habe, welche Klasse sie implementiert.
> 
> Wofür ich das brauche? Ich möchte eine bestimmte Operation mit der Klasse durchführen, falls sie genau das Interface auch wirklich implementiert.


wenn du schon mit reflections arbeitest hol dir direkt die gewuenschte methode ueber getMethod(...) oder so...


----------



## ARadauer (18. Jan 2012)

EikeB hat gesagt.:


> Hm, hört sich merkwürdig an. Wofür genau brauchst du das?



Vermutlich für ein plugin system....


----------



## irgendjemand (18. Jan 2012)

ARadauer hat gesagt.:


> Vermutlich für ein plugin system....



gut .. sowas löst an aber in der regel anderst da man bei sowas in der regel vorgibt wie dem system bekannt zu machen ist welche klasse das interface implementiert *und wenns nur über einen missbrauchten "main-class" eintrag im manifest ist *was man mit dem java.util.jar-package auslesen kann*


----------



## reflector4sure (18. Jan 2012)

Aradauer hat recht. Es geht um ein Plugin-System.


----------



## faetzminator (18. Jan 2012)

Man kann sich auch eine kleine Config schreiben, in welcher steht, was zu laden ist und wo es sich befindet. XML würde sich da sehr eignen. Denn das Konstrukt wird normalerweise immer komplexer, als man anfangs dachte


----------



## irgendjemand (18. Jan 2012)

@TO
und warum sagst du das dann nicht gleich ?
dann hätte ich dir sofort einen code an den kopf werfen können


```
try
		{
			File pluginDir=new File(PATH);
			File[] pluginJars=pluginDir.listFiles(pluginFilenameFilter);
			for(File pluginJar : pluginJars)
			{
				String name="FAIL";
				JarFile jarFile=new JarFile(pluginJar);
				for(Enumeration<JarEntry> entries=jarFile.entries(); entries.hasMoreElements(); )
				{
					JarEntry entry=entries.nextElement();
					if(entry.getName().endsWith("Plugin.rsf"))
						name=(new BufferedReader(new InputStreamReader(jarFile.getInputStream(entry)))).readLine();
				}
				jarFile.close();
				if(name.equals("FAIL"))
					continue;
				URLClassLoader urlCL=new URLClassLoader(new URL[] { pluginJar.toURI().toURL() });
				Class<?> clazz=urlCL.loadClass(name);
				Plugin plugin=(Plugin)clazz.newInstance();
				plugin.loadPlugin(this);
				plugins.add(plugin);
			}
			for(Plugin plugin : plugins) { plugin.startPlugin(); }
		}
```

das ist die innere klasse PluginFilenameFilter *welche in meinem code vorher instanziert wird*

```
static final class PluginFilenameFilter implements FilenameFilter
	{
		public boolean accept(File path, String name)
		{
			if(name.toLowerCase().endsWith(".jar"))
				return true;
			else
				return false;
		}
	}
```

dazu gehören dann noch diese interfaces

```
public interface Plugin
{
	public void loadPlugin(PluginLoader loader) throws Exception;
	public void startPlugin() throws Exception;
	public void stopPlugin() throws Exception;
}
```


```
public interface PluginLoader
{
	public Object invoke(String pluginName, String pluginMethod, Object... args) throws Exception;
	public String getPath();
	public String getVersion();
	public void shutdown();
}
```

ein sample-plugin

```
public class zGUIShow implements Plugin
{
	private PluginLoader pluginLoader=null;
	public void loadPlugin(PluginLoader pluginLoader) throws Exception { this.pluginLoader=pluginLoader; }
	public void startPlugin() throws Exception
	{
		//code
	}
	public void stopPlugin() throws Exception { }
}
```
das ganze kannst du dann in JAR-files verpacken ...
in die plugin-jars muss zusätzliche eine datei welche auf "Plugin.rsf" endet und den namen der plugin-klasse samt package enthält
also z.b. : "plugins.gui.zGUIShow"


----------



## reflector4sure (18. Jan 2012)

Die Idee mit dem XML-File gefällt mir ziemlich gut. Das werde ich glaube ich bauen.


----------



## reflector4sure (18. Jan 2012)

Sorry, ich dachte das wäre nicht von Belang.  Daher hatte ich es nicht erwähnt.

Danke für den Code, werd ihn mir anschauen!!


----------



## mvitz (18. Jan 2012)

Oder man macht es direkt mit "Java-Boardmitteln": ServiceLoader (Java Platform SE 6)


----------



## irgendjemand (18. Jan 2012)

falls du genaue erklärungen zum code brauchst *ist leider un-doc da ich ihn mal eben so aus meiner code-sammlung kopiert hab* kannst du dich gerne via icq : 345-169-219 melden ... hab das ganze mit hilfe des Java @ tutorials.de: Tutorials, Forum & Hilfe - forums in langer arbeit entworfen und weitreichend getestet ...
auch bei beispielen kannst du gerne noch mal nachfragen ... hab im aktuellen projekt genug davon rumfliegen ...
nur eine fertige lib als JAR zum einbinden in eine IDE hab ich noch nicht ... könnt ich dir aber zusammenbauen


----------



## irgendjemand (18. Jan 2012)

mvitz hat gesagt.:


> Oder man macht es direkt mit "Java-Boardmitteln": ServiceLoader (Java Platform SE 6)



habe ich mit dem user "Thomas Diramont" im tut.de - forum erörtert ...

problem : es muss beim start von java der CP manuell auf alle gewünschten jars festgelegt werden damit der service loader diese finden kann ... was gerade beim dynamischen laden/entladen von plugins hinderlich ist


----------



## reflector4sure (18. Jan 2012)

@irgendjemand: Ich adde dich gleich morgen früh! Bin gerade am Rechner meiner Freundin.  

Danke nochmals an alle!


----------



## irgendjemand (18. Jan 2012)

kein problem ...

wie gesagt ... die basis des codes stammt nicht von mir allein ... da haben bestimmt 20 user über 3 wochen dran gesessen um was halbwegs vernünftiges auf die beine zu stellen ...

ich hatte es nur so schnell parat da ich damit viel arbeite und es auch in meinem aktuellen projekt wieder eine große rolle spielt ...

alternativ melde dich mal im tut.de forum und sprich das "plugin-system" des users "SPiKEe" an *müsste mal die links der threads raussuchen"


----------



## schalentier (18. Jan 2012)

irgendjemand hat gesagt.:


> habe ich mit dem user "Thomas Diramont" im tut.de - forum erörtert ...
> 
> problem : es muss beim start von java der CP manuell auf alle gewünschten jars festgelegt werden damit der service loader diese finden kann ... was gerade beim dynamischen laden/entladen von plugins hinderlich ist



Biste da sicher? Wozu gibts dann die ServiceLoader.load(Class<S> service, ClassLoader loader) Methode? Sollte das nicht klappen, kann man auch den Classpath zur Laufzeit um Eintraege erweitern (URLClassLoader.addURL).


----------



## irgendjemand (18. Jan 2012)

genau DAS habe ich mit genanntem user lang und breit erörtert und auch selbst sehr viel getestet ...
zumindest in J6 und den pre-builds von J7 ging genau DAS eben NICHT ... habe es seit dem final von J7 auch nicht noch mal versucht ...

das war auch der grund warum ich dort damals eine thread losgetreten habe um ein halbwegs vernünftiges plugin-system zu entwickeln ... anfangs wurde auch ich auf den ServiceLoader verwiesen ... da ich aber beispiele posten konnte die damals auch bei anderen nicht funktionierten hat sich dann die gruppe der interessenten *mich eingeschlossen* daran gemacht etwas eigenes zu entwickeln ...


@TO
ich weis das der code so erstmal nicht schön aussieht *throws Exception* ...
es macht aber durch aus sinn durch den reflection-ähnlichen aufbau alle fehler bis zum caller weiter zu werfen so lange man das aufgetretene problem nicht innerhalb der methode in der es aufgetreten ist vollständig beheben kann und eventuell eine meldung an den user machen muss


----------



## irgendjemand (19. Jan 2012)

@schalentier
mal noch ein anderes beispiel wesshalb ServiceLoader für dynamische systeme ausscheidet

mach dir mal den spass den aktuellen mysql-jdbc-driver zu laden ...
den packst du an einen ort der NICHT in deinem classpath steht ...
dann nimmst du folgenden code

```
import java.io.*;
import java.net.*;
import java.sql.*;
public class Test
{
	public static void main(String[] args) throws Exception { (new Test()).test(); }
	private Test() { }
	private void test() throws Exception
	{
		URLClassLoader cl=new URLClassLoader(new URL[] { (new File("G:/java/lib/mysql-con.jar")).toURI().toURL() });
		cl.loadClass("com.mysql.jdbc.Driver");
		Connection con=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql", "root", "mysql");
		con.close();
	}
}
```
und compilest ihn ...

dann machst du eine instanz von CMD.exe auf und löschst erstmal temporär deinen classpath durch eingabe von

```
set CLASSPATH=
```
dadurch wird der variable CLASSPATH für diese instanz kein wert zugewiesen und sie wird aus der liste entfernt *durch

```
echo %CLASSPATH%
```
zu prüfen ... wenn du alles richtig gemacht hast wird %CLASSPATH% ausgegeben*

dann startest du die klasse *natürlich vor dem compilen pfad und user angepasst*

du wirst überrascht sein das du eine SQLException bekommst das angeblich kein brauchbarer treiber vorhanden sei obwohl du diesen durch einen URLClassLoader geladen hast *da das DriverManager.register() in einem static-block von Driver steht sollte dieser ja eigentlich registriert werden*

der grund liegt daran das java nur jdbc-driver verwenden kann welche zum zeitpunkt des starts der vm auch im classpath liegen ... *was ja durch die explizite aufhebung des classpath's ausgeschlossen wird*

und so ein ähnliches problem würdest du auch beim ServiceLoader erhalten wenn die zu ladenen plugins beim start NICHT im classpath liegen ...

nun kann man sich bei beiden zum glück abhilfe verschaffen ...

zum jdbc-driver *für java7* :

```
import java.io.*;
import java.net.*;
import java.sql.*;
import java.util.*;
import java.util.logging.*;
public class DummyDriver implements Driver
{
	private Driver driver=null;
	public DummyDriver(Driver driver) { this.driver=driver; }
	public boolean acceptsURL(String url) throws SQLException { return driver.acceptsURL(url); }
	public Connection connect(String url, Properties info) throws SQLException { return driver.connect(url, info); }
	public int getMajorVersion() { return driver.getMajorVersion(); }
	public int getMinorVersion() { return driver.getMinorVersion(); }
	public Logger getParentLogger() throws SQLFeatureNotSupportedException { return driver.getParentLogger(); }
	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { return driver.getPropertyInfo(url, info); }
	public boolean jdbcCompliant() { return driver.jdbcCompliant(); }
}
```

wenn du nun folgenden code verwendest

```
import java.io.*;
import java.net.*;
import java.sql.*;
public class Test
{
	public static void main(String[] args) throws Exception { (new Test()).test(); }
	private Test() { }
	private void test() throws Exception
	{
		URLClassLoader cl=new URLClassLoader(new URL[] { (new File("G:/java/lib/mysql-con.jar")).toURI().toURL() });
		Object o=cl.loadClass("com.mysql.jdbc.Driver").newInstance();
		DriverManager.registerDriver(new DummyDriver((Driver)o));
		Connection con=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql", "root", "mysql");
		con.close();
	}
}
```
wirst du sehen das die verbindung fehlerfrei hergestellt werden kann

das liegt daran das du den jdbc-driver-loader damit austrickst das eine Driver-klasse während des startens der VM im classpath liegt ...
diese kannst du auf grund ihrer beschaffenheit das sie lediglich alle aufrufe an den darunterliegenden driver druchleitet zum laden von jdbc-drivern während der runtime nutzen ...


und ähnlich verhält es sich auch mit einem plugin-system und dem oben von mir gepostetem code ...
dadurch das du gegen das interface Plugin programmierst ... welches ja zum start der VM im classpath liegt kannst du jede intanz die durch einen URLClassLoader geladen wurde durch diese klasse kapseln und mit ihr arbeiten ...


ich hoffe ich konnte dir was deinen hinweis auf den ServiceLoader und die damit verbundenen schwierigkeiten beim dynamischen laden angeht etwas neues beibringen ...


----------



## schalentier (19. Jan 2012)

Ja klar, du machst das dort auch nicht richtig ;-) Der URLClassLoader, den du dir dort erzeugst, wird ja nicht vom SystemClassLoader benutzt.

So funktionierts und auf analogem Wege sollte es auch mit dem ServiceLoader klappen:


```
public class LoaderTest {
    public void addURL( URL url ) {
        URLClassLoader loader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class cls = URLClassLoader.class;
        try {
            Method method = cls.getDeclaredMethod("addURL", new Class[]{URL.class});
            method.setAccessible(true);
            method.invoke(loader, url);
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, SQLException {
        try {
            ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
        } catch( ClassNotFoundException e) {
            System.out.println("NOT FOUND");
        }
        LoaderTest l = new LoaderTest();
        l.addURL(new File("mysql-con.jar").toURI().toURL());
        ClassLoader.getSystemClassLoader().loadClass("com.mysql.jdbc.Driver");
        Connection con= DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql", "root", "mysql");
        System.out.println(con);
        con.close();
    }
}
```
Achtung, Code is HAAAAESSLICH ;-)

Ausgabe:

```
NOT FOUND
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
	at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1116)
	at com.mysql.jdbc.MysqlIO.<init>(MysqlIO.java:344)
	at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2332)
	at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2369)
	at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2153)
	at com.mysql.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:792)
```

(Ich hab hier keine mysql Datenbank, aber der Connect Aufruf wurde vom mysql Treiber versucht)


----------



## irgendjemand (19. Jan 2012)

ok ... proof of concept ...

ja klar ... funktioniert ... ist aber wirklich nicht schön ...
und meines erachtens um ein plugin-system zu bauen *wofür man eh interfaces braucht* mit reflections am SystemClassLoader rumspielen ... ja klar : würde mit sicherheit auch in richtung ServiceLoader funktionieren ... *habs jetzt leider nicht ausgetestet da mir ein KSKB dafür auch grad leider nicht einfällt* ... aber ich denke das das in falsche richtung für eine doch sehr simple aufgabe geht ...
wie gesagt : es wäre im weitesten sinne nur eine nachträgliche manipulation des classpath während der runtime ... und dafür gibt es ja eigentlich den URLClassLoader : um eben während der runtime daten dynamisch nachzuladen *das es hier und da natürlich kleine schwierigkeiten gibt ist klar* ...

aber hab trotzdem wieder was gelernt =D ... *symbolisch DANKE-button drück*


----------



## bygones (19. Jan 2012)

ok hab nicht alles genau gelesen, daher vielleicht redundant.

Wenn man schon ein PluginSystem entwickelt, dann sollte man einfach gewisse Regeln aufsetzen, wie alle Plugins muessen in einen Folder rein (notfalls in der Applikation ein "Install Plugin" Aktion anbieten die das erledigt. In dem Jar selber muss im manifest oder irgendeiner Datei die Klasse angegeben werden, die das PluginInterface implementiert angegeben werden.

Alles andere ist imo unsinn wenn man schon ein PluginSystem baut


----------



## irgenjemand (19. Jan 2012)

bygones hat gesagt.:


> ok hab nicht alles genau gelesen, daher vielleicht redundant.
> 
> Wenn man schon ein PluginSystem entwickelt, dann sollte man einfach gewisse Regeln aufsetzen, wie alle Plugins muessen in einen Folder rein (notfalls in der Applikation ein "Install Plugin" Aktion anbieten die das erledigt. In dem Jar selber muss im manifest oder irgendeiner Datei die Klasse angegeben werden, die das PluginInterface implementiert angegeben werden.
> 
> Alles andere ist imo unsinn wenn man schon ein PluginSystem baut



full ack

wobei gerade das mit "im manifest" angeben dank java.util.jar.Manifest sehr einfach ist ...
zur not könnte man sogar soweit rumtricksen das man die plugins als runnable jar baut und sich dann im loader den main-class eintrag holt *sind ide's in der lage runnable jar mit einer main-class zu erzeugen welche KEIN public static void main(String[]) enthält ?*
oder man nutzt halt andere key's ...
ist auf jeden fall besser als mein rumgemurkse da mit durchs jar laufen und versuchen irgendein file zu laden ...

ich werds mal soweit umbauen ... macht auch irgendwie mehr sinn ...


----------



## Sym (19. Jan 2012)

tfa hat gesagt.:


> Gar nicht. Es geht nur umgekehrt.


Das geht schon. Du kannst alle Klassen beim Start scannen. Google Reflections hilft dabei z.B.


----------



## irgendjemand (19. Jan 2012)

ich denke doch das wir mitlerweile wissen was TO vorhat und wie man das eleganz lösen kann ...
sowohl dein quote als auch die antwort sind zeitlich etwas veraltet und so nicht mehr relevant


----------



## tfa (19. Jan 2012)

Sym hat gesagt.:


> Das geht schon. Du kannst alle Klassen beim Start scannen. Google Reflections hilft dabei z.B.



Dann sag mir doch mal, wie du "alle Klassen beim Start" bekommst.


----------



## bygones (19. Jan 2012)

tfa hat gesagt.:


> Dann sag mir doch mal, wie du "alle Klassen beim Start" bekommst.


er meint reflections - java runtime metadata analysis - Google Project Hosting, die *den Classpath* auslesen.... das kann wahrscheinlich gleich "alle Klassen" bedeutet, muss aber nicht


----------



## tfa (19. Jan 2012)

Achso, ich hab das "Google" überlesen.


----------



## Sym (19. Jan 2012)

bygones hat gesagt.:


> er meint reflections - java runtime metadata analysis - Google Project Hosting, die *den Classpath* auslesen.... das kann wahrscheinlich gleich "alle Klassen" bedeutet, muss aber nicht


Damit funktioniert das ziemlich gut. Das Problem ist natürlich immer die Zeit, die dafür benötigt wird. Darum sollte man das vorsichtig und paketeinschränkend verwenden.


----------



## bygones (19. Jan 2012)

Sym hat gesagt.:


> Damit funktioniert das ziemlich gut. Das Problem ist natürlich immer die Zeit, die dafür benötigt wird. Darum sollte man das vorsichtig und paketeinschränkend verwenden.


fuer mich klingt das ein bisschen wie "habe einen hammer gefunden, nun ist alles ein Nagel". Wie schon geschrieben, nur weil etwas moeglich ist, sollte man sich mehrfach ueberlegen, ob es auch das richtige fuer einen ist.
Und fuer das Szenario hier ist es def. nicht die erste Wahl


----------



## schalentier (19. Jan 2012)

Ich raffs irgendwie nicht. Ich hab doch nun schon ein Stueckel Code gepostet, wie man das machen kann, damit es mit dem ServiceLoader funktioniert. Wieso willst du/wollt ihr das nun doch alles selbst implementieren? Wo ist der Vorteil?


----------



## irgendjemand (19. Jan 2012)

der vorteil ist das wir den nachteil den du mit der reflection-manipulation des systemclassloaders erzeugst umgehen ...

wie gesagt : ja - es ist proof-of-concept ... es funktioniert ... aber wenn du schon sowas machst um alleine die plugins zu laden *welche ja dann im endeffekt auch nur ein gemeinsames interface implementieren *Service** dann würde es darauf hinaus laufen das dann das gesamte system mit reflections vollgestopft wird ... und das ist auch nicht gerade die beste wahl ...

es ist halt einfacher mit zwei interfaces , einem loader und plugin-implementierungen etwas dynamisches zubasteln als mit dem ServiceLoader ... welcher wie ich bereits gesagt habe den nachteil entweder muss man den classpath beim start angeben ... oder über solche "tricks" den system-CL manipulieren ...


----------



## Spacerat (19. Jan 2012)

@Schalentier: Ich raff's auch nicht, denn faktisch funktioniert der ServiceLoader, sogar ohne die grossen Verrenkungen, die du in deinem Code anstellst.
Man benötigt lediglich Kenntnis über ein spezielles API genannt ServiceProviderInterface (SPI). Na, klingelts?
Hier mal der ClassLoader mit dem ich alle möglichen Datentypen zur Laufzeit lade sofern dafür ein Codec gefunden wurde.
	
	
	
	





```
package datatypes;

import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.JarEntry;

import datatypes.codecs.system.JarFile;


final class DTClassLoader
extends URLClassLoader
{
	private static final String SERVICE = "META-INF/services/datatypes.CodecProviderService";

	DTClassLoader()
	{
		super(new URL[0], ClassLoader.getSystemClassLoader());
	}

	protected void registerDatatypes(URL url)
	throws Exception
	{
		JarFile jar = DataType.getFile(url); // ist nicht das JarFile auf welches die Forum API-DOC zeigt!
		jar.extract(new JarEntry(SERVICE));
		addURL(url);
	}
}
```
Leider ist er so nicht zum testen geeignet, da er Teil einer bereits recht umfangreichen API ist.
[EDIT]@Schalentier: Da fällt's mir auf... kann es sein, das du auch den gerne genommenen Kardinalfehler begehst und diese [c]META-INF/services[/c]-Geschichte einfach missachtest?
@all: Tut ihr dieses vllt. sogar alle? :bahnhof:[/EDIT]


----------



## irgendjemand (20. Jan 2012)

@Spacerat
gut ... und dein code ist jetzt WO GENAU anderst als meiner ?
der einzige unterschied ist das du anstatt ein eigenes interface eines aus der API nimmst und anstatt nach einem eigenen file / dem manifest zu suchen den service-descriptor verwendest ... macht summa sumarum auch nichts anderes als mein code oder der reflection-"hack" des System-CL

btw : ich habe mich wie gesagt nur soweit mit der ganzen ServiceLoader sache befasst um sagen zu können das zum zeitpunkt meines letzten tests das problem beim dynamischen laden OHNE manipulation des system-CL bestand ... verdeutlich auch durch das jdbc-driver beispiel ...

da es in einem dynamischen system eher unerwünscht ist parameter anzugeben *z.b. modifizierter CP* bzw diese durch reflections während der runtime zu manipulieren ... ist es eh ein etwas krasser eingriff ins system nur um eine klasse dynamisch zu laden ...
das geht auch einfacher mit nem URLClassLoader und einem dem loader und damit der VM bekannten interface ... weil nichts anderes ist der serviceloader wenn man ihn mal aus ein ander nehmen würde ... mit dem unterschied das dieser intern von java beim start durch die app gejagt wird und man ihn nicht selber schreiben muss ...

@TO
da du dich bis jetzt nicht gemeldet hast und wir uns hier nur noch gegenseitig mit "hacks" der VM belagern wäre mal eine stellungnahme deiner seits ganz hilfreich um den sinn hinter der diskusion noch zu haben ...
dir wurden jetzt 3 möglichkeiten gegeben
1) ServiceLoader
2) manipulation des system-CL
3) eigener code
alle 3 funktionieren ... und sollte man nach möglichkeit nicht mischen *hab da schon einiges gesehen ... dann hast du plötzlich 2 oder 3 instanzen der selben klasse ... ist auch eher sub-optimal* ... aber ohne konkrete fragen zur umsetzung und einer entscheidung welches system du nun benutzen möchtest wird es dir eher weniger bringen wenn wir hier mal eben die VM auseinander nehmen ...

daher nur die bitte *@rest : bitte nicht als destruktive kritik verstehen* : sag uns einfach welche der möglichkeiten du dir ausgesucht hast / für dich am ehesten in frage kommt ... und dann können wir uns speziell darauf konzentrieren ...

ich möchte mich hier nur ungern mit dem rest anlegen *mal wieder* ohne wenigstens eine antwort zu bekommen ob meine vorschläge (nicht) gebraucht werden ...


----------



## Spacerat (20. Jan 2012)

Damit da keine Misverständnisse aufkommen... die Zeilen 5, 7, 13, 23 und 24 können getrost aus meinem Code entfernt werden, denn sie prüfen nur die Presents dieses Eintrags. Ohne diese Prüfung würde jede beliebige URL schlicht geaddet, auch die, die keinen Service-Eintrag haben. Über den ServiceLoader aber bekommt man sie aber trotzdem erst, wenn dieser Eintrag vorhanden ist - ganz ohne Reflection und ganz ohne andere Tricks. Ich habe auch alles zunächst alles über Reflection gelöst, bis ich mir zwecks Inspiration Quellcode von [c]java.sound.sampled.spi[/c] und [c]java.imageIO.spi[/c] näher betrachtete. Getestet hab' ich das dann auch zur Genüge, denn es gibt nur noch sehr wenig Dateien (in der Theorie nämlich gar keine, kommt halt auf die registrierten Codecs an), welche ich nicht per [c]Datatype.getFile(URL url)[/c] geladen und instanziert bekomme.
[EDIT]





irgendjemand hat gesagt.:


> ... dann hast du plötzlich 2 oder 3 instanzen der selben klasse ...


[IRONIE]"die alle samt und sonders auch nicht mal annäherd funktionieren"[/IRONIE] hast du vergessen zu erwähnen :lol:
...und zum "mit dem Rest anlegen"... Nun ja, mit mir nicht, warum auch, mir ist deine bzw. eure "fehlerhafte" Nutzung des SPI hinlänglich bekannt und mehr als diesen Hinweis auf diesen dubiosen [c]META-INF/service[/c]-Eintrag habe ich ohnehin nicht.[/EDIT]


----------



## Spacerat (20. Jan 2012)

Sorry für den Doppelpost, aber ich hab' mich mal daran versucht, euch die "Magie" des SPIs ([c]META-INF/service[/c]) näher zu bringen.
1. Klassen des PlugInLoaders: (2; das PlugIn-Interface und der PlugInProviderService
1.1. Der PlugInProviderService

```
package spiplugin.loader;

import java.util.List;


public abstract class PlugInProviderService
{
	public abstract List<PlugIn> getPlugIns();
}
```
1.2. Das PlugIn Interface

```
package spiplugin.loader;

public interface PlugIn
{
	void start();
	void stop();
}
```

2. Klassen für PlugIn Tests
2.1. SimpleTest1 (Single PlugIn)

```
package spiplugin.plugintest;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import spiplugin.loader.PlugInProviderService;
import spiplugin.loader.PlugIn;

public class SimpleTest1
extends PlugInProviderService
{
	private static final List<PlugIn> plugins;

	static {
		ArrayList<PlugIn> rc = new ArrayList<PlugIn>();
		rc.add(new PlugIn()
		{
			@Override
			public void stop()
			{
				System.out.println("Single PlugIn stopped");
			}

			@Override
			public void start()
			{
				System.out.println("Single PlugIn started");
			}
		});
		plugins = Collections.unmodifiableList(rc);
	}

	@Override
	public List<PlugIn> getPlugIns()
	{
		return plugins;
	}
}
```
2.2. SimpleTest2 (10 PlugIns)

```
package spiplugin.plugintest;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import spiplugin.loader.PlugInProviderService;
import spiplugin.loader.PlugIn;

public class SimpleTest2
extends PlugInProviderService
{
	private static final List<PlugIn> plugins;

	static {
		final MutableInt number = new MutableInt();
		ArrayList<PlugIn> rc = new ArrayList<PlugIn>();
		for(int n = 0; n < 10; n++) {
			rc.add(new PlugIn()
			{
				private final int value;

				{
					value = number.value++;
				}
				
				@Override
				public void stop()
				{
					System.out.println("PlugIn " + value + " stopped");
				}
				
				@Override
				public void start()
				{
					System.out.println("PlugIn " + value + " started");
				}
			});
		}
		plugins = Collections.unmodifiableList(rc);
	}

	@Override
	public List<PlugIn> getPlugIns()
	{
		return plugins;
	}

	private static final class MutableInt
	{
		private int value;
	}
}
```

3. Eine Test Klasse

```
package spiplugin;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ServiceLoader;

import spiplugin.loader.PlugIn;
import spiplugin.loader.PlugInProviderService;

public class SPIPlugInTest
{
	public static void main(String[] args)
	throws Throwable // Tests bitte ohne Try-Catch ;)
	{
		// Ermitteln des nächst höheren Verzeichnisses.
		// wenn man keinen ClassPath angibt, dürfte dieses
		// standardmässig kaum darin enthalten sein.
		final URL parentPath = new File("..").getAbsoluteFile().toURI().toURL();
		// instanzieren des eigenen URLClassLoaders
		final MyURLClassLoader classLoader = new MyURLClassLoader();
		// hinzufügen der Plugin-Archive, welche sich
		// im parentPath befinden sollten.
		classLoader.addURL(new URL(parentPath, "plugIns1.jar"));
		classLoader.addURL(new URL(parentPath, "plugIns2.jar"));
		// und jetzt... TESTEN
		for(PlugInProviderService pps : ServiceLoader.load(PlugInProviderService.class, classLoader)) {
			for(PlugIn pi : pps.getPlugIns()) {
				pi.start();
				pi.stop();
			}
		}
	}

	// Der hauseigene URLClassLoader... (freischalten der add-Methode)
	private static final class MyURLClassLoader
	extends URLClassLoader
	{
		private MyURLClassLoader()
		{
			super(new URL[0]);
		}

		@Override
		protected void addURL(URL url)
		{
			super.addURL(url);
		}
	}
}
```

4. PlugInService Descriptoren (jeweils die Datei im PI-Archiv [META-INF/services/spiplugin.loader.PlugInProviderService[/c]
4.1. plugIns1.jar

```
spiplugin.plugintest.SimpleTest1
```
4.2. plugIns2.jar

```
spiplugin.plugintest.SimpleTest2
```

5. Klassen und Dateien der Archive
5.1. Hauptarchiv (muss logischerweise im CP sein)
     - Die Testklasse mit der "main()"
     - Alle Klassen des Paketes spiplugin.loader
     Das Archiv evtl. startbar machen.
5.2. PlugInArchive 
  Lt. Testklasse ein Verzeichnis über dem Hauptarchiv und dafür sorgen, dass dieses nicht im CP auftaucht, damit der Effekt sichtbar wird.
5.2.1. PlugIns1.jar
     - Die PlugInProvider Klasse SimpleTest1
     - Der erste Servicedescriptor (4.1)
5.2.1. PlugIns2.jar
     - Die PlugInProvider Klasse SimpleTest1
     - Der zweite Servicedescriptor (4.2)

6. Ausgabe

```
PlugIn 0 started
PlugIn 0 stopped
PlugIn 1 started
PlugIn 1 stopped
PlugIn 2 started
PlugIn 2 stopped
PlugIn 3 started
PlugIn 3 stopped
PlugIn 4 started
PlugIn 4 stopped
PlugIn 5 started
PlugIn 5 stopped
PlugIn 6 started
PlugIn 6 stopped
PlugIn 7 started
PlugIn 7 stopped
PlugIn 8 started
PlugIn 8 stopped
PlugIn 9 started
PlugIn 9 stopped
Single PlugIn started
Single PlugIn stopped
```
Bei Fragen einfach fragen... viel Erfolg 
[EDIT]Zu guter Letzt sei noch erwähnenswert, dass das Interface [c]PlugIn[/c] eigentlich überflüssig ist, solange die PlugIns nicht andere Klassen erweitern sollen.[/EDIT]


----------



## irgendjemand (23. Jan 2012)

gut .. auch wenn dein post schon etwas her ist möchte ich noch mal ein grundlegendes problem einwerfen

java hat leider probleme bei der korrekten unterstützung von URLs ...

wenn man in nem pfad z.b. leerzeichen hat werden diese ja noch halbwegs korrekt mit %20 codiert ... so bald es aber an sonderzeichen geht gibts probleme ...
alles was im ASCII steht geht noch ... aber spätestens bei deutschen umlauten wird es katastrophal ...
wenn man sich mal die ausgabe von File.toURI().toURL() eines pfades ansieht der z.b. leerzeichen , deutsche umlaute , das euro-zeichen und meinet wegen noch andere ungültige zeichen enhält ist nicht mal mehr der URLDecoder in der lage diese wieder richtig darzustellen ...

am schlimmsten fällt dies unter Windows auf ... da wir ja alle wissen das Windows sowas grundsätzlich in Win-1251 codiert ... java sowas aber rfc getreu mit utf-8 macht kommt es zu schweren fehlern welche zu nicht mehr auflösbaren pfaden führen ...

soweit wie es meine tests gezeigt haben mag es wohl noch möglich sein eine URL welche aus einem File-objekt generiert wurde zu verwenden ... so bald man aber den umgekehrten weg versucht , also ein File-objekt zu erstellen dem man z.b. URL.toString() übergibt scheitern viele operationen an FileNotFoundException oder anderen ...


@Spacerat
na das beispiel ist wohl eher schlecht gewählt ... weil mehr als anonyme objekte erstellen machst du da auch nicht ...
ein richtiges beispiel für ein plugin fehlt leider bei dir ...
und ob es wirklich so sinnvoll ist [c]extends Provider[/c] anstatt [c]implements Plugin[/c] zu nutzen ... naja kommt auf den anwendungsfall an ..


@Mod
da sich TO weder bei mir noch scheinbar bei jemand anderen gemelt hat könnte man dieses thema ruhig mal als "erledigt" makieren ...


----------



## Spacerat (23. Jan 2012)

irgendjemand hat gesagt.:


> java hat leider probleme bei der korrekten unterstützung von URLs ...


Äehm... [c]java.net.URLEncoder[/c] und [c]java.net.URLDecoder[/c] funktionierten bei mir bisher immer prächtig.


irgendjemand hat gesagt.:


> @Spacerat
> na das beispiel ist wohl eher schlecht gewählt ... weil mehr als anonyme objekte erstellen machst du da auch nicht ...
> ein richtiges beispiel für ein plugin fehlt leider bei dir ...
> und ob es wirklich so sinnvoll ist [c]extends Provider[/c] anstatt [c]implements Plugin[/c] zu nutzen ... naja kommt auf den anwendungsfall an ..


Klassennamen sind in Beispielen [c]extends Schall[/c] und [c]implements Rauch[/c]. Provider kann man für echte Projekte auch in Plugin umbenennen, wie man es macht, bleibt jedem selbst überlassen. Die PlugIns fehlen auch nicht, sondern manifestieren sich in den Klassen SimpleTest1 und SimpleTest2. Dort werden die PlugIns zwar anonym instanziert, aber wer sagt denn, dass man das in echten Projekten so machen muss? Fakt ist, dass wenn man das SPI (ServiceLoader) verwenden will, es so verwenden sollte und nicht anders, zumindest lt. den meisten Tutorials oder gar [c]javax.sound.sampled.spi[/c] bzw. [c]javax.imageio.spi[/c]. Auf jeden Fall aber ist es besser, als irgend etwas unnötig mit Reflection verhunzen zu müssen, sogar [c]PluginClass.class.newInstance()[/c] entfällt.


----------



## Andreas29 (23. Jan 2012)

Hi,

irgendwie klingt alles hier beschriebene so, als könne man es auch mit OSGI sehr bequem lösen. Sowie es eclipse wunderbar vormacht. Nur um mal ein weiteres Stichwort in den Raum zu schmeißen...

Grüße,
Andreas


----------



## irgendjemand (23. Jan 2012)

Spacerat hat gesagt.:


> Äehm... [c]java.net.URLEncoder[/c] und [c]java.net.URLDecoder[/c] funktionierten bei mir bisher immer prächtig.


dann versuch das mal unter windows über CMD mit nem pfad der sonderzeichen enhält ...
dann machst du [c]this.getClass().getResource(this.getClass().getSimpleName()+".class")[/c] und lässt dir das mal durch den URLDecoder ausgeben ... ich wette mit dir das auch bei dir da nur schrott rauskommen wird ...

*witziger weise unterstützt zwar cmd.exe auch unicode ... führt man diesen befehl jedoch in einem verzeichnis mit sonderzeichen aus stürzt cmd.exe einfach ab da es selbst probleme hat die in Win-1251 vorliegenden daten nach unicode zu converten ... selbes problem hat auch java*



Spacerat hat gesagt.:


> als irgend etwas unnötig mit Reflection verhunzen zu müssen, sogar [c]PluginClass.class.newInstance()[/c] entfällt.


na das beispiel kam ja mal nich von mir

@Andreas29
jo .. könnte man auch verwenden ...


wie gesagt : da TO nicht mehr reagiert und wir nun alle unsere vorschläge gepostet haben kann das hier eig close ...


----------

