[Java] Pfadangaben von Resourcen in jar Dateien

Quidoff

Lieutenant
Registriert
Feb. 2005
Beiträge
897
Hallo,
ich wollte gerade ein Java-Programm in eine jar-Datei auslagern, habe aber das Problem, dass
dieses Programm eine Ressourcendatei laden muss (genauer gesagt eine xml-Datei), die in einem
Unterordner "data" des Stammverzeichnisses liegt.
Innerhalb des Codes wird die Datei einfach durch "data/simple.xml" geladen, wenn das Programm
aber (mit "data"-Verzeichnis) in eine jar-Datei ausgelagert wird, dann wird nach der Datei im
Verzeichnis, in dem java ausgeführt wird, gesucht. Wie kann ich den Code dahingegen ändern, dass
die Ressourcendatei innerhalb des jar-Archivs gesucht wird, falls sie sich in einer jar-Datei befindet,
oder innerhalb des Stammverzeichnisses, falls das Programm als entpackt ausgeführt wird.
Wäre schön, wenn es eine Lösung gäbe, bei denen man zwischen dieses Fällen nicht
unterscheiden muss. Das die Lösung also sowohl bei den entpackten Sourcen, als auch in einem
jar-Archiv funktioniert.

Mein zweites Problem ist, dass ich innerhalb des Programms auf Klassen zugreife, die in einem
anderen externen jar-Archiv liegen. Auf diese habe ich anscheinend keinen Zugriff mehr,
wenn ich das Programm in eine jar-Datei auslagere. Jedenfalls bekomme ich auch dann noch einen
java.lang.NoClassDefFoundError Fehler, wenn ich die externe jar-Datei per -cp an den classpath
anhänge.
 
Versuche es mal mit:

Code:
String pfad = "data/";
Code:
this.getClass().getClassLoader().getResource(pfad + "dateiname.endung")

Der zweite gibt die URL zurück. Also da einfügen, wo du den Pfad ganz normal angegeben hast.
 
Code:
String pfad = "data/";
URL url = this.getClass().getClassLoader().getResource(pfad + "simple.glade");
if(url==null) System.out.println("url is null");
Meine Verzeichnisstruktur sieht so aus:
unbenanntarq.jpg

Die Ausgabe auf der Konsole erfolgt. :(

//edit
Oder ist diese Lösung nur zu gebrauchen, falls sich das Programm in einer jar befindet?
 
Nein, in Eclipse wird des auch gehen.
Aber dein "data" Ordner muss in dem Ordern sein, in dem sich dein "bin" Verzeichnis befindet (also die .class Dateien).

'tschuldige, hab ich total vergessen zu erwähnen.
 
Ok. Jetzt funktioniert es in Eclipse, aber wenn ich das Programm in eine jar packe, dann erhalte ich folgende Ausgabe:
Code:
$ java -Djava.library.path=/usr/lib -jar Glade.jar 
Exception in thread "main" java.io.FileNotFoundException: 
Can't find the specified Glade XML file:
/home/korn/Desktop/file:/home/korn/Desktop/Glade.jar!/data/simple.glade
        at org.gnome.glade.Glade.parse(Glade.java:142)
        at org.testing.Simple.<init>(Simple.java:25)
        at org.testing.Simple.main(Simple.java:49)
Die jar Datei scheint aber richtig aufgebaut zu sein.
Code:
$ unzip -l Glade.jar | grep -v gnome | grep -v freedesktop
Archive:  Glade.jar
  Length     Date   Time    Name
 --------    ----   ----    ----
       71  02-23-08 12:15   META-INF/MANIFEST.MF
        0  02-23-08 11:38   org/
        0  02-23-08 11:38   org/testing/
     1275  02-23-08 11:41   org/testing/Simple.java
    18620  02-23-08 04:23   typeMapping.properties
      572  02-23-08 08:20   .classpath
      364  02-22-08 01:51   .project
        0  02-23-08 11:39   data/
     1949  02-23-08 06:29   data/simple.glade
     3427  02-23-08 03:51   data/simple.glade.bak
      273  02-23-08 03:51   data/simple.gladep
      273  02-23-08 03:51   data/simple.gladep.bak
      786  02-23-08 11:41   org/testing/Simple$1.class
      944  02-23-08 11:41   org/testing/Simple$2.class
     2442  02-23-08 11:41   org/testing/Simple.class
 --------                   -------
  2345257                   1707 files

Das Manifest sieht so aus:
Code:
Manifest-Version: 1.0
Sealed: true
Main-Class: org.testing.Simple
Wobei ich mir der Bedeutung von Sealed noch nicht ganz bewusst bin.

(Wo ich zu meiner anderen Frage komme. Ich benutze in meinem Programm eine Klasse, die in einer
anderen jar Datei deklariert ist. Ich habe diese jar Datei entpackt und zu meinem Programm hinzugefügt,
wodurch mein Programm einen ziemlichen Overhead bekommt, wie man an der Anzahl der Dateien sehen kann.)

//edit
Das kopieren der Ressourcen in den bin-Ordner wird übrigens dadurch erspart, dass man in Eclipse einen zusätzlichen Ordner
resources anlegt, den man als Source-Ordner klassifiziert und den data-Ordner in diesen verschiebt. Dadurch wird
automatisch eine Kopie in bin angelegt. Die resultierende jar-Datei bleibt aber die selbe, wodurch der erste Fehler auch
dadurch nicht behoben ist.

//edit2
Bin glaube ich der Lösung etwas näher gekommen.
Zum einen ist mir aufgefallen, dass bei der Ausgabe der Pfad zweimal auftaucht.
Ich habe deshalb in Eclipse [ ] Add directory entries deaktiviert.

Das ist mein Code:
Code:
URL url = this.getClass().getClassLoader().getResource("/data/simple.glade");

Ich erhalte sowohl in Eclipse als auch mit der jar die Ausgabe:
Code:
url is null
Exception in thread "main" java.lang.NullPointerException
        at org.testing.Simple.<init>(Simple.java:24)
        at org.testing.Simple.main(Simple.java:48)

Wenn ich den Code so ändere, dass ich das führende / entferne, dann läuft das Programm in Eclipse, aber die Ausgabe der jar in der Konsole bringt:
Code:
$ java -Djava.library.path=/usr/lib -jar Glade.jar
Exception in thread "main" java.io.FileNotFoundException: 
Can't find the specified Glade XML file:
/home/korn/Desktop/file:/home/korn/Desktop/Glade.jar!/data/simple.glade
        at org.gnome.glade.Glade.parse(Glade.java:142)
        at org.testing.Simple.<init>(Simple.java:24)
        at org.testing.Simple.main(Simple.java:48)

Meine Verzeichnisstruktur sieht so aus:
unbenanntfoa.png


Die jar sieht so aus:
Code:
$ unzip -l Glade.jar | grep -v gnome | grep -v freedesktop
Archive:  Glade.jar
  Length     Date   Time    Name
 --------    ----   ----    ----
       57  02-23-08 14:14   META-INF/MANIFEST.MF
      786  02-23-08 14:13   org/testing/Simple$1.class
      944  02-23-08 14:13   org/testing/Simple$2.class
     2183  02-23-08 14:13   org/testing/Simple.class
     1949  02-23-08 06:29   data/simple.glade
     3427  02-23-08 03:51   data/simple.glade.bak
      273  02-23-08 03:51   data/simple.gladep
      273  02-23-08 03:51   data/simple.gladep.bak
    18620  02-23-08 04:23   typeMapping.properties
      619  02-23-08 13:21   .classpath
      364  02-22-08 01:51   .project
 --------                   -------
  2343756                   1691 files

Das Manifest so:
Code:
Manifest-Version: 1.0
Main-Class: org.testing.Simple

//edit3
Da ich gar keine Idee mehr habe, habe ich den Code mal hochgeladen.
Wäre nett, wenn es jemand in eine lauffähige jar-Datei packen könnte und dann
hier posten könnte, wo der Fehler war und wie man ihn behebt.
Glade.rar
libgtkjni-4.0.so (Muss als native library des Projekts eingetragen werden)
Ausführung der jar Datei dann mit: java -Djava.library.path=/usr/lib -jar Glade.jar (/usr/lib zum Pfad ändern, in dem die Datei liegt)

//edit4
Also es ist wohl nicht möglich einen Pfad zu einer Resource innerhalb einer jar-Datei anzugeben. Es liese sich wohl ein File Objekt erstellen, aber damit kann der Konstruktor nicht umgehen, weshalb noch die Möglichkeit bleibt, eine lokale Kopie zu erstellen.

//edit5
So, wie gesagt, musste die temporäre Kopie des xml-Skripts erzeugt werden:
Code:
private void loadGUIFile() throws IOException {
		f = File.createTempFile("simple", "glade");
		InputStream is = this.getClass().getResourceAsStream("/data/simple.glade");
		FileOutputStream os = new FileOutputStream(f);

		byte by;

		while ((by = (byte) is.read()) != -1) {
			os.write(by);
		}
		f.deleteOnExit();
		os.close();
	}
 
Zuletzt bearbeitet: (Lösung: resourcen ins bin-Verzeichnis schieben; Der Lösung etwas näher; Nahe der Kapitulation (Code geht Open-Source ;-);)
Zurück
Oben