# JAR, Pfad und Properties



## Camino (14. Apr 2011)

Hallo,

ich hänge da nun schon seit einiger Zeit an einem Problem und bräuchte mal Hilfe.

Und zwar hab ich eine Swing-Anwendung, die ich über Eclipse zu einem ausführbaren JAR erstellen lasse. Als Ergebnis erhalte ich in einem festgelegten Ordner eine JAR-Datei und ein Ordner mit den benötigten LIBs. Für meine Anwendung brauche ich auch eine Properties-Datei, welche ausserhalb der JAR (aber im gleichen Ordner) liegt, da ich dort während der Programmausführung sowohl lesen als auch schreiben möchte. Das Erstellen der JAR-Datei klappt soweit ganz gut, nur hab ich wohl noch Probleme mit dem Pfad zur Properties-Datei. Ich hab das mal getestet, indem ich zu Programmstart eine Properties-Datei speicher. Die Properties-Datei ist noch nicht vorhanden, wird also neu erstellt.

Wenn ich mit einem Doppelklick auf die JAR-Datei die Anwendung starte, wird die Properties-Datei in mein home-Verzeichnis geschrieben (ich arbeite mit Debian Linux). Wenn ich die JAR-Datei über die Konsole ausführe, also im Verzeichnis, wo die JAR liegt, 
	
	
	
	





```
java -jar datei.jar
```
 eingebe, dann wird die Properties-Datei in den Ordner mit der JAR und dem LIB-Ordner geschrieben, so wie ich es haben möchte.

In meiner Anwendung speicher ich die Properties so ab:

```
// Properties in Datei speichern
	public void saveProperties( Properties props ) {
		
		try {
			FileOutputStream propOutFile = new FileOutputStream( "user.conf" );
			props.store( propOutFile, "Lokale Einstellungen für Anwendung" );
		}
		catch ( FileNotFoundException ex ) {
			System.err.println( "Can't find " + propFile );
		}
		catch ( IOException ex ) {
			ex.printStackTrace();
		}
		
	}
```
Ich hab auch schon beim Dateinamen 
	
	
	
	





```
./user.conf
```
 probiert, klappt aber auch nicht. Hab ich einen Fehler bei der Pfadangabe? Liegt der Fehler in der Methode zum Schreiben der Properties? Oder liegt es an der Manifest-Datei:

```
Manifest-Version: 1.0
Class-Path: . semtix_lib/truezip-6.8.jar semtix_lib/postgresql-8.4-701.jdbc4.jar
Main-Class: main.Semtix
// -----LEERZEILE-----
```
Bin schon seit einigen Tagen am suchen und probieren, aber es klappt irgendwie nicht. Vielleicht kann mir ja jemand weiterhelfen...

Gruss
Camino


----------



## nrg (14. Apr 2011)

probiers mal mit classloader.
ungetestet:

```
public void saveProperties(Properties props) throws IOException {
		URL config = ClassLoader.getSystemClassLoader().getResource("user.conf");
		if (config == null)
			throw new FileNotFoundException("user.conf konnte nicht gefunden werden");

		props.store(new FileOutputStream(config.getFile()), "Lokale Einstellungen für Anwendung");
	}
```

edit: hatte mich vertippt ^^


----------



## Camino (14. Apr 2011)

```
config = null
java.io.FileNotFoundException: user.conf konnte nicht gefunden werden
```


----------



## nrg (14. Apr 2011)

dann konnte die datei nicht gefunden werden . testest du das gerade in der IDE oder mit der JAR? wo liegt denn die Datei?


----------



## Camino (14. Apr 2011)

Hatte das gerade mit Eclipse getestet. Die Datei user.conf liegt im Workspace auf der gleichen Ebene wie meine src-Verzeichnis.
Mit meiner vorherigen Methode wurde die Datei so gefunden und konnte auch gelesen und geschrieben werden. Nur mit dem JAR gab es da die Probleme...


----------



## nrg (14. Apr 2011)

nein jetzt nicht mehr . mit classloader muss die datei im bin-verzeichnis liegen.


----------



## Camino (14. Apr 2011)

Dann bekomme ich mit Eclipse diese Meldung:

```
config = file:/home/heyho/Semtix/Java%20workspace/Semtix/bin/user.conf
java.io.FileNotFoundException: /home/heyho/Semtix/Java%20workspace/Semtix/bin/user.conf (No such file or directory)
```


----------



## nrg (14. Apr 2011)

sieht mir nach einem encoding-problem mit leerzeichen aus. ich habs jetzt auch mal getestet. funktionierte nur hatte ich keine leerzeichen im pfad . ich teste das nochmal, mom

probiers mal so:



```
public static void saveProperties(Properties props) throws IOException, URISyntaxException {
		URL config = ClassLoader.getSystemClassLoader().getResource("user.conf");
		if (config == null)
			throw new FileNotFoundException("user.conf konnte nicht gefunden werden");
		props.store(new FileOutputStream(config.toURI().getPath()), "Lokale Einstellungen für Anwendung");
	}
```

damit sollte es aber funktionieren. die URISE kannste imho auch ignorieren.


----------



## FArt (14. Apr 2011)

Classloader funktioniert nur, wenn die Datei nur gelesen werden soll.
Zum Schreiben wird immer das aktuelle Verzeichnis genommen. Das kannst du aber nicht festlegen.

Der saubere Weg: schreibe die Properties immer in das Userverzeichnis und nutze zum Ermitteln dieses Verzeichnisses das Systemproperty.
Der unsaubere Weg: lade eine Klasse aus deinem JAR, lass dir den kompletten Pfad zu der Ressource geben, parse den Pfad und schon weißt du, wo das JAR liegt.

Ich empfehle grundsätzlich den Weg, der sich nicht wie ein Hack anfühlt.


----------



## nrg (14. Apr 2011)

warum? geht doch..

nachdem user.conf nach einer benutzerconfig aussieht, könnte man aber wirklich 
	
	
	
	





```
..getProperty("user.dir")
```
 nehmen.


----------



## Camino (14. Apr 2011)

FArt hat gesagt.:


> Classloader funktioniert nur, wenn die Datei nur gelesen werden soll.
> Zum Schreiben wird immer das aktuelle Verzeichnis genommen. Das kannst du aber nicht festlegen.


Was heisst "aktuelles Verzeichnis"? Dort, wo auch die Anwendung liegt und ausgeführt wird? Oder das User-Verzeichnis? Weil, wie ich oben geschrieben hatte, wird die Datei unterschiedlich angelegt: wenn ich die JAR-Datei über die Konsole starte, landet die Datei im Verzeichnis neben dem JAR. Wenn ich die Anwendung mit Doppelklick starte, landet die Datei im home-Verzeichnis.



> Der saubere Weg: schreibe die Properties immer in das Userverzeichnis und nutze zum Ermitteln dieses Verzeichnisses das Systemproperty. Der unsaubere Weg: lade eine Klasse aus deinem JAR, lass dir den kompletten Pfad zu der Ressource geben, parse den Pfad und schon weißt du, wo das JAR liegt.


OK, muss ich mir mal überlegen, was mir lieber ist...


----------



## FArt (14. Apr 2011)

nrg hat gesagt.:


> warum? geht doch..



In diesem Fall, aber nicht immer. Somit bin ich für den Fall, der immer funktioniert. Das nennt man defensiv entwickeln. 
Nicht alles was hier und jetzt technisch möglich ist, ist eine gute Lösung. Oft ist es sogar nicht einmal eine Lösung.


----------



## Camino (14. Apr 2011)

nrg hat gesagt.:


> warum? geht doch..


Na ja, wenn (wie FArt schrieb) mit dem ClassLoader nur gelesen werden kann... Ich möchte in die Datei ja auch schreiben können.



> nachdem user.conf nach einer benutzerconfig aussieht, könnte man aber wirklich
> 
> 
> 
> ...


In meiner user.conf sollen einfach lokale Einstellungen für jeden einzelnen Benutzer drin stehen. Mit getProperty("user.dir") bekomme ich dann den Pfad zu dem Verzeichnis, in dem die JAR (und auch die user.conf) drinliegen? Das wäre ja eigentlich das, was ich bräuchte...


----------



## FArt (14. Apr 2011)

Camino hat gesagt.:


> Was heisst "aktuelles Verzeichnis"? Dort, wo auch die Anwendung liegt und ausgeführt wird?


Das aktuelle Verzeichnis ist das, aus dem die Anwendung gestartet wurde. Das mag häufig das Verzeichnis sein, in dem die Anwendung liegt, muss es aber nicht. Das sieht man schon daran, dass du bei deinen zwei Usecases unterschiedliches Verhalten erzeugst.


----------



## FArt (14. Apr 2011)

Camino hat gesagt.:


> Na ja, wenn (wie FArt schrieb) mit dem ClassLoader nur gelesen werden kann... Ich möchte in die Datei ja auch schreiben können.
> 
> 
> In meiner user.conf sollen einfach lokale Einstellungen für jeden einzelnen Benutzer drin stehen. Mit getProperty("user.dir") bekomme ich dann den Pfad zu dem Verzeichnis, in dem die JAR (und auch die user.conf) drinliegen? Das wäre ja eigentlich das, was ich bräuchte...



Der Anforderung nach wäre alles andere sogar falsch. Wenn die Applikation auf einem Rechner installiert wird und sie unter verschiedenen Usern gestartet werden kann, wird ein User erwarten, dass seine Konfiguration nicht global verwendet wird oder verändert wird.

Mein Tipp bei so was: die Defaulteinstellungen im JAR ausliefern und aus dem Klassenpfad lesen, die persönlichen Einstellungen im Benutzerverzeichnis speichern. Suchreihenfolge: erst Einstellungen aus dem Benutzerverzeichnis.


----------



## nrg (14. Apr 2011)

naja gut, klär mich auf. ich habe bis jetzt in meinen projekten auch nur properties über den classloader _geladen_. warum spricht was gegen speichern? ich bin jetzt in o.g. code auch fälschlicherweiße davon ausgegangen, dass die properties datei existieren muss. nachdem das nicht immer der Fall ist, könnte man den code aber auch einfach umschreiben: 


```
public static void saveProperties(Properties props) throws IOException, URISyntaxException {
		URL config = ClassLoader.getSystemClassLoader().getResource(".");
		props.store(new FileOutputStream(config.toURI().getPath() + File.separator + "user.conf"), "Lokale Einstellungen für Anwendung");
	}
```

wüsste jetzt nicht, was dagegen spräche.

edit: aber nachdem das anscheinend benutzerbezogene einstellungen sind, eignet sich wohl user.dir eher dafür.


----------



## Camino (14. Apr 2011)

FArt hat gesagt.:


> Der Anforderung nach wäre alles andere sogar falsch. Wenn die Applikation auf einem Rechner installiert wird und sie unter verschiedenen Usern gestartet werden kann, wird ein User erwarten, dass seine Konfiguration nicht global verwendet wird oder verändert wird.
> 
> Mein Tipp bei so was: die Defaulteinstellungen im JAR ausliefern und aus dem Klassenpfad lesen, die persönlichen Einstellungen im Benutzerverzeichnis speichern. Suchreihenfolge: erst Einstellungen aus dem Benutzerverzeichnis.


OK, werde das wohl dann so umsetzen.

Ich danke euch beiden für eure Hilfe...

Camino


----------



## FArt (14. Apr 2011)

nrg hat gesagt.:


> wüsste jetzt nicht, was dagegen spräche.



Abhängig von der Laufzeitumgebung kann es passieren, dass die URL des Classloaders nicht in eine gültige URI für Zugriffe auf das Filesystem konvertiert werden kann.
Unabhängig davon kann es sein, dass der ausführende Benutzer gar keine Schreibrechte auf das Verzeichnis hat, in dem die Applikation liegt.


----------

