# class loader. Mal wieder.



## tharo (7. April 2008)

Hallo erst einmal.

Ich habe dann also nach langem hin und her mich auch mal daran gewagt einen eigenen Class Loader zu entwerfen. Das war einfach höchste Eisenbahn bei dem Umfang meines Projektes.

Der erste Test mit einem Code wie diesem z.B. 

```
private static Plugin loadPlugin(URL[] locations, String name) 
    	throws InstantiationException, IllegalAccessException, ClassNotFoundException {
    	
    	URLClassLoader classloader = new URLClassLoader(locations, ClassLoader.getSystemClassLoader());
        Class<?> cls = classloader.loadClass(name);

        if (!Plugin.class.isAssignableFrom(cls)) {
            throw new RuntimeException("Invalid class");
        }
        return (Plugin)cls.newInstance();
    }
```
lief auch absolut problemlos.

Nun wollte ich allerdings mehr als nur ein Plugin pro JAR Archiv haben. Wenn schon denn schon. Also hatte ich den Gedanken mir ein kleines config File (das hier mal nicht Bestand des Problems sein soll) mit in das .JAR archiv zu speichern das mir den classpath der einzelnen Plugins übermittelt. Ab dort sollte alles so funktionieren wie bei oben geposteten Beispiel das man z.B. so aufrufen kann


```
File pluginFile = new File("../plugins/test.jar");
URL[] locations = new URL[] {pluginFile.toURL()};

Plugin plugin = loadPlugin(locations, "de.taliis.test.plugin.TestPlugin");
```

Ich dachte das sollte schon kein Problem darstellen. Ich habe früher ja auch schon immer eifrig Bilder und andere Ressourcen aus JAR Archiven geladen. Wenn auch immer aus dem selben wie jenes in dem auch mein Programm lag.


```
URL url = ClassLoader.getSystemResource(path);
```

Nun ... in diesem Fall will ich ja keine ressource aus dem System Path (oder dem Programm Path) sondern flexibel einfach nur etwas aus dem eingeladenen JAR archiv. Ich benutze also getResource() und nicht getSystemResource().

Da geht das Probem los. Die einzige Ressource in meinem neuen classloader scheint das jar archiv selbst zu sein ..
Was mache ich Falsch?


```
File archive = new File("../plugins/test.jar");
		try {
			classLoader = new URLClassLoader(
							new URL[] {archive.toURL()}, 
							ClassLoader.getSystemClassLoader()
						);			
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return;
		}
		if(classLoader==null) return;
		
		// catch the config file
		URL configURL = classLoader. findResource("./plugins.txt");
		URL alles[] = classLoader.getURLs();
		System.out.println(alles[0].getFile() + alles.lenght);
```


----------



## zeja (7. April 2008)

Du lässt dir doch hier die configURL gar nicht ausgeben?!


----------



## tharo (7. April 2008)

Sie ist null.
Alle files haben eine länge von genau 1 - und dieses file ist das jar archiv selbst. Die im archiv enthaltenen Dateien sind daher nicht "sichtbar" für mich .. Genau das ist das Problem das ich nicht verstehe.


----------



## zeja (7. April 2008)

Hmm also eigentlich geht das wunderbar:

```
public static void main(String[] args) throws Exception {
	URL url = new File("Plugin.jar").toURI().toURL();
	URLClassLoader loader = new URLClassLoader(new URL[]{url});
	final Enumeration<URL> resources = loader.getResources("plugin.txt");
	final InputStream resourceAsStream = loader.getResourceAsStream("plugin.txt");
	System.out.println(resourceAsStream);
	while(resources.hasMoreElements()){
		final URL ul = resources.nextElement();
		System.out.println(ul);
		System.out.println(ul.toExternalForm());
	}
}
```


----------



## tharo (7. April 2008)

Ist logisch das es geht. Das sollte meines nach meinem Verständniss eigentlich auch .. Aber kannst du mir vielleicht sagen wo mein Fehler war?
Ich gebe zu ich komm nicht ganz drauf .. 

Vielen Dank auf jeden Fall.


----------



## zeja (7. April 2008)

Dass du die Rückgabe von getResource nie verwendet hast. Wenn du vom ClassLoader getURLS abrufst nützt dir das gar nichts. Dann kriegst du nur das zurück was du reingesteckt hast. Deswegen weiß ich nicht was diese Zeile da bei dir bewirken sollten.


----------



## tharo (7. April 2008)

```
URL configURL = classLoader.findResource("./plugins.txt");
```

Ist wie gesagt null.

Selbiges bei getResource usw.


----------



## zeja (7. April 2008)

Liegt deine plugins.txt im Jar? Liegt sie an der richtigen Stelle?


----------



## tharo (7. April 2008)

Hmja. Sie liegt im root der .jar datei.

Ich habe meinen code nun abgewandelt... hab die URL über das URI object gecastet und direkt den stream geöffnet.

Die Ausgabe ist nach wie vor null ...
So sieht der Code nun aus


```
private void load(File archive) {
		// load jar file into our classloader
		try {
			classLoader = new URLClassLoader(
							new URL[] {archive.toURI().toURL()}
						  );			
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return;
		}
		if(classLoader==null) return;
		
		// catch the config file
		InputStream resourceAsStream = classLoader.getResourceAsStream("plugins.txt");
		System.out.println(resourceAsStream);
}
```


----------



## zeja (7. April 2008)

So habe dir mal das jar in nem zip angehängt mit welchem ich das gemacht habe.


----------



## tharo (7. April 2008)

Habs gefunden.
Es lag wirklich an der Position des JAR files und nicht wie gedacht am code selbst.

Ich danke dir vielmals <.< Flüchtigkeitsfehler sind leider oft die schlimmsten Fehler.


----------



## tharo (29. April 2008)

Soo ...

Leider habe ich nun ein neues kleines Problem.

Folgende Situation hat sich ergeben. Ich habe ein Plugin mit einem Treiber und eines das den Treiber benutzt. Das ganze ist in etwa so:

treiber.jar
     + treiberklasse
     + betrachter 1
betrachter.jar
     + betrachter 2

Alles wird wunderbar eingeladen und auch in den richtigen reihenfolgen initialisiert.
Betrachter 1 funktioniert tadelos ..

Betrachter 2 hingegen .. garnicht. Er findet die treiber nicht. Die Klasse ist unbekannt
Jedes jar archiv bekommt einen eigenen class loader.

Und nun die Frage: Wie löse ich das Problem am besten? :/
Was ist zu tun damit die einzelnen jar archive ihren jeweiligen klassen-raum kennen?


----------



## zeja (29. April 2008)

Ich habe deine Darstellung nicht ganz verstanden... du möchtest dass eine Klasse aus Jar A auf eine Klasse in Jar B zugreifen dürfen soll obwohl alle in einem eigenen ClassLoader geladen werden?


----------



## tharo (29. April 2008)

Genau. Die klassen von jar a müssen die klassen von jar b kennen.

WIE genau es gemacht wird ist mir inzwischen egal.


----------



## zeja (29. April 2008)

Was spricht dagegen alle mit eine ClassLoader zu laden?


----------



## tharo (29. April 2008)

Naiv wie ich bin frage ich einmal: Wie ist die Methode dafür? Habe ich sie übersehen, denn meines Wissens nach ist da nur der Konstruktor.


----------



## zeja (29. April 2008)

Der URLClassLoader bekommt nen Array von URLs da kannste also mehrere Jars reinstecken.


----------

