Pfad der eigenen Klasse feststellen oder rel. Pfad finden
Rouven
- java
Hallo,
das da oben ist der zugegeben schlechte Versuch eine angemessenen Titel für das Problem zu finden. Die Sache ist folgende: Ich habe einige Java-Klassen die auf verschiedene XML-Konfigurationsdateien zugreifen sollen. In meiner Entwicklungsumgebung (und in der späteren Anwendungsumgebung) sollen diese relativ zur Klasse im Unterordner config liegen.
Bsp.: AgentController benötigt Zugriff auf config/ac.xml
Nun ist aber beim Instanziieren der Klasse das "aktuelle Verzeichnis" nicht unbedingt das in dem die Klasse liegt (z.B. wenn die Klasse von einer anderen Anwendung instanziiert wird). Ergebnis: Ich finde meine Konfigurationsdatei nicht mehr wieder.
Ich habe versucht mit per System.getProperty("user.dir") das aktuelle Verzeichnis geben zu lassen, nur leider ist das das Verzeichnis in dem der java-Befehl abgesetzt wurde o.ä..
Hat jemand eine Idee wie ich die Datei wiederfinde?
Nehme jeden Tipp...
Danke!
MfG
Rouven
abend,
das da oben ist der zugegeben schlechte Versuch eine angemessenen Titel für
das Problem zu finden. Die Sache ist folgende: Ich habe einige Java-Klassen
die auf verschiedene XML-Konfigurationsdateien zugreifen sollen. In meiner
Entwicklungsumgebung (und in der späteren Anwendungsumgebung) sollen diese
relativ zur Klasse im Unterordner config liegen.
ich verstehe dich/das nicht so ganz. hier herrscht doch eine feste struktur
hinsichtlich der datei-/package-hierarchie. egal von wo deine klassen
instanziiert werden, kannst du doch unabhängig davon das package der
jeweiligen klasse ermitteln. zu diesem packagenamen packst du einfach ein
'.config' dran und somit sollte der zugriff auf die XML-dateien eiegentlich
gewährleistet sein !?
mfg,
(tanz das)
Z.N.S.
Hallo,
ich verstehe dich/das nicht so ganz. hier herrscht doch eine feste struktur
hinsichtlich der datei-/package-hierarchie. egal von wo deine klassen
instanziiert werden, kannst du doch unabhängig davon das package der
jeweiligen klasse ermitteln.
Wie?
(Und wie definierst du "Package"? Und was hast du gewonnen, wenn du das
Package kennst?)
Gruß
Slyh
abend,
mist, hab mir selber geantwortet ;(
siehe [pref:t=86578&m=512506]
mfg,
(tanz das)
Z.N.S.
abend,
ok. nach deinem posting [pref:t=86578&m=512498] verstand ich die problematik ;)
mfg,
(tanz das)
Z.N.S.
Hi,
das da oben ist der zugegeben schlechte Versuch eine angemessenen Titel für das Problem zu finden. Die Sache ist folgende: Ich habe einige Java-Klassen die auf verschiedene XML-Konfigurationsdateien zugreifen sollen. In meiner Entwicklungsumgebung (und in der späteren Anwendungsumgebung) sollen diese relativ zur Klasse im Unterordner config liegen.
Nun ist aber beim Instanziieren der Klasse das "aktuelle Verzeichnis" nicht unbedingt das in dem die Klasse liegt (z.B. wenn die Klasse von einer anderen Anwendung instanziiert wird). Ergebnis: Ich finde meine Konfigurationsdatei nicht mehr wieder.
Ich habe versucht mit per System.getProperty("user.dir") das aktuelle Verzeichnis geben zu lassen, nur leider ist das das Verzeichnis in dem der java-Befehl abgesetzt wurde o.ä..
Hat jemand eine Idee wie ich die Datei wiederfinde?
Wenn ich mich richtig erinnere, dürfte Class.getResource oder Class.getResourceAsStream geeignet sein.
Es ist ja nicht gesagt, daß die Klasse aus einem normalen Verzeichnis geladen wurde - sie kann ja irgendwoher aus dem CLASSPATH kommen, also auch aus einem Jar-File - demzufolge muß auch dieser gesamte CLASSPATH durchsucht werden ...
cu,
Andreas
Hallo,
das da oben ist der zugegeben schlechte Versuch eine angemessenen Titel für das Problem zu finden. Die Sache ist folgende: Ich habe einige Java-Klassen die auf verschiedene XML-Konfigurationsdateien zugreifen sollen. In meiner Entwicklungsumgebung (und in der späteren Anwendungsumgebung) sollen diese relativ zur Klasse im Unterordner config liegen.
Eine interessante Problematik, vor der übrigens nicht nur Java-Programme
stehen, sondern eigentlich jedes Programm, das in irgend einer Form
auf Dateien in seinem "eigenen Verzeichnis" zugreifen muß.
Das Problem ist, daß dieses "eigene Verzeichnis" eigentlich nur
theoretisch existiert. Wie du ja selbst schon festgestellt hast, ist
das aktuelle Arbeitsverzeichnis nicht zwingend auch das Verzeichnis,
in dem sich die ausführbare Programmdatei befindet.
Unter Windows läßt sich beispielweise für jede Verknüpfung festlegen,
in welchem Verzeichnis die dort referenzierte Datei ausgeführt werden
soll. (Siehe das Textfeld "Ausführen in:" bei den Verknüpfungs-
Eigenschaften.)
Prinzipiell bestehen wohl 4 Möglichkeiten, mit dieser Problematik
umzugehen:
1.) Man geht davon aus, daß das Arbeitsverzeichnis stets auch das
Verzeichnis ist, in dem sich die Programmdatei befindet. Alle Datei-
Operationen führt man daher relativ zum aktuellen (Arbeits-)Verzeichnis
durch.
Wie wir festgestellt haben, muß das Arbeitsverzeichnis nicht mit dem
Programm-Verzeichnis übereinstimmen, somit handelt es sich hierbei
um die schlechteste Vorgehensweise.
2.) Man legt die Konfigurationsdateien an eine zentrale Stelle im
System ab, die unabhängig vom Programm-Verzeichnis immer absolut
erreichbar ist. Im Falle von Linux wäre dies bspw. das Home-Verzeichnis
des aktuellen Benutzers. Im Falle von Windows wohl soetwas wie
"c:\Dokumente und Einstellungen\Ich".
Damit ist sichergestellt, daß die Daten immer auffindbar sind.
Nachteil: Bei einer Konfigurationsdatei mag sowas noch funktionieren.
Wenn allerdings mehrere oder größere Dateien zugreifbar sein sollen,
ist ein Umkopieren in das Home-Verzeichnis wohl kaum wirklich hilfreich.
3.) Ablage _einer_ Konfigurationsdatei an der genannten zentralen
Stelle. In dieser Konfigurationsdatei ist u.a. angegeben, wo sich das
Programmverzeichnis und damit die anderen Dateien, die für die
Programmausführung benötigt werden, befinden.
Unter Windows könnte zur Ablage auch die Registry verwendet werden.
Nachteil: Ein einfaches Verschieben der Programmdateien ist nach der
Installation nicht mehr möglich, da die Pfade "fest" in der Konfigurations-
Datei abgelegt sind. (Natürlich könnte man die entsprechenden
Einträge in der Konfigurations-Datei oder der Registry einfach
von Hand korrigieren.)
Diese 3. Möglichkeit wird übrigens von der meisten Windows-Programmen
realisiert. Üblicherweise legen Windows-Programme ihre gesamte
Konfiguration in der Registry ab, und verweisen von dort auf das
Programmverzeichnis. Da auf die Registry global zugegriffen werden
kann, kann das Programm stets ermitteln, wo es installiert ist.
(Ein Nachteil der Registry von Windows ist sicherlich, daß diese bei
einer Neuinstallation von Windows üblicherweise nicht gesichert wird
bzw. werden kann, und daher alle Programme neu installiert und
konfiguriert werden müssen.)
Seit JDK 1.4 bietet Java zur globalen Ablage von Konfigurationsdaten
übrigens das Package java.util.prefs, und im speziellen die Klasse
java.util.prefs.Preferences an.
Aus dem JavaDoc der Klasse "Preferences":
"A node in a hierarchical collection of preference data. This class
allows applications to store and retrieve user and system preference
and configuration data. This data is stored persistently in an
implementation-dependent backing store. Typical implementations
include flat files, OS-specific registries, directory servers and SQL
databases. The user of this class needn't be concerned with details
of the backing store."
Es lassen sich also plattformunabhängig hierarchisch Konfigurations-
Daten ablegen. Zur Speicherung wird ein plattformspezifische Ablageort
gewählt. Unter Windows ist dies meines Wissens die Registry. Da die
Ablage aber für den Programmierer völlig transparent erfolgt, hat
das eigentlich gar nicht zu interessieren.
(Ich selbst habe ich die Preferences-Klasse noch nie verwendet, so daß
ich keine weitere Aussage bzgl. des Für und Wider treffen kann.)
Es gibt noch eine weitere Möglichkeit zum Auffinden von Dateien des
Programms:
4.) Java bietet die Möglichkeit Dateien, die sich irgendwo im aktuellen
Classpath befinden, aufzufinden. Da sich die Programmdatei des
Hauptprogramms üblicherweise im Classpath befindet -- auch wenn dies
nicht immer der Fall sein muß -- lassen sich alle Dateien, die sich
im selben oder einem der untergeordneten Verzeichnisse zum
Hauptprogramm befinden, auf diese Weise auffinden und laden.
Java bietet die Methode java.lang.Class.getResource() an, die eine
Datei durch den ClassLoader, der die Klasse des Class-Objekts, auf das
die Methode aufgerufen wird, auffinden läßt.
Üblicherweise hat dieser ClassLoader mindestens das Verzeichnis, in
dem sich die genannte Hauptprogramm-Klasse befindet, in seinem
Classpath.
Beispiel:
class MyMainProgram
{
public MyMainProgram()
{
// Tu was...
// [...]
URL myConfigFile = getClass().getResource("/config/general.cfg");
// Lade die Daten aus "general.cfg".
// [...]
}
}
Was passiert hier?
Die Methode "getClass()", die in jedem Objekt existiert, liefert das
Class-Objekt der Klasse. (Genauer gesagt das Class-Objekt zur Klasse,
in deren Instanz wir uns gerade befinden.)
Diese Klasse sei das Hauptprogramm.
Der Klasse ist intern der ClassLoader zugeordnet, der die Klasse
geladen hat. Dieser ClassLoader hat den Pfad der Klasse in seinem
Classpath. Alle Aufrufe von "getResource()" auf das Class-Objekt werden an diesen ClassLoader gegegeben. Dieser wiederum versucht die
genannte Datei in seinem Classpath zu finden, und liefert im
Erfolgsfall ein URL-Objekt mit einer Referenz auf eben diese Datei
zurück.
Die Datei selbst muß dabei nicht einmal zwingend in einem Verzeichnis
liegen, das im Classpath ist. Wie im Beispiel zu sehen ist, reicht
es, daß sich das Verzeichnis "config" in einem Verzeichnis befindet,
das sich im Classpath befindet.
Um wieder auf das Ausgangsproblem zurückzukommen:
Die Konfigurationsdateien lassen sich über die Methode "getResource()"
finden und dann laden, sofern sich die Klasse des Hauptprogramms im
Classpath befindet. (Das ist üblicherweise nur dann nicht der Fall,
wenn die Klasse z.B. über einen eigenen ClassLoader oder den
URLClassLoader "manuell" anhand eines Dateinamens nachgeladen wurde.)
Nachteil: Wenn sich im aktuellen Classpath sehr viele Verzeichnisse
(oder auch JAR-Dateien) befinden, ist es durchaus möglich, daß sich
in mehreren dieser Verzeichnisse bspw. eine Datei "general.cfg"
befindet -- evtl. sogar in Kombination mit dem Verzeichnis "config",
somit also "/config/general.cfg" mehrmals im Classpath vorkommt.
Mir ist nicht bekannt, auf welche Weise Java (bzw. der ClassLoader)
diesen Konflikt auflöst. Ich vermute, daß einfach die Datei genommen
wird, die in dem Verzeichnis liegt, das weiter vorne im Classpath
steht. (Man kann damit allgemein sagen, daß das Verhalten bei
Doppeldeutigkeiten des Dateinamens undefiniert ist, da man den
Classpath evtl. selbst gar nicht kontrollieren kann.)
Umgehen kann man dieses Problem sicherlich durch die Vergabe von
eindeutigen Verzeichnis- oder Dateinamen, z.B. durch Voranstellen des
Package-Namens ("/de-slyh-mycoolapp-config/general.cfg") oder
dergleichen.
Natürlich läßt sich die letztbeschriebene Möglichkeit des Findens von
Dateien auf dem Classpath mit der Ablage des Programmverzeichnisses
in einer Konfigurationsdatei kombinieren. Hierzu legt man die
Konfigurationsdatei (mit einem möglichst eindeutigen Namen) in dasselbe
Verzeichnis ab, in dem sich auch die Programmdatei des Hauptprogramms
befindet, so daß sie vom ClassLoader gefunden werden kann. In die
Konfigurationsdatei speichert man den Programmpfad ab.
Im Hauptprogramm ruft man dann einfach beim Programmstart die Methode
"getResource()" des ClassLoaders auf, läßt sich die Konfigurations-
Datei ermitteln, und liest aus dieser den Programmpfad aus.
Alle nachfolgenden Dateioperationen läßt man dann nicht mehr über
denn ClassLoader abwickeln, sondern verwendet den Absolutpfad aus
der Konfigurations-Datei.
Grund: Da der ClassLoader beim Aufruf von "getResource()" immer den
gesamten Classpath nach der gewünschten Datei absuchen muß, wäre bei
vielen zu suchenden Dateien die Performanz ganz sicher nicht die beste...
Gruß
Slyh
Hallo Slyh,
wie auch immer hilfreich es sich herausstellt, VIELEN DANK für dieses extrem ausführliche Posting. Die Sache mit getResource führe ich mir mal zu Gemüte. Wegen der Performance: Da kann man ja wie du schon geschrieben hast sicherlich anfangen zu tricksen, eine Datei suchen, von dieser Datei dann den Ort der restlichen ableiten, das passt schon irgendwie.
Das Problem rührt daher, dass wir im Moment eine leicht heikle Sache machen: Wir schreiben ein Testframework für "beliebige" Java-Anwendungen. Mittels Bytecode-Injizierung werden einzelne Aufrufe unserer Software in ein fertiges Programm gestreut und die Frage ist nun wie finden unsere Klassen ihre Umgebung wieder, das user.dir wird ja irgendwas von der eigentlich ausgeführten Anwendung sein.
Aber ich denke das wird sich schon irgendwie finden lassen, vielen Dank nochmal!
MfG
Rouven
Hi,
wie auch immer hilfreich es sich herausstellt, VIELEN DANK für dieses extrem ausführliche Posting. Die Sache mit getResource führe ich mir mal zu Gemüte. Wegen der Performance: Da kann man ja wie du schon geschrieben hast sicherlich anfangen zu tricksen, eine Datei suchen, von dieser Datei dann den Ort der restlichen ableiten, das passt schon irgendwie.
Aber wie schon gesagt darauf achten, daß es sich nicht (mehr) um eine Datei im Filesystem handeln muß - wenn das ganze als Jar im CLASSPATH liegt. Das ist bei Java ja durchaus üblich.
cu,
Andreas
Hallo,
ja, das Problem wird aber wohl nicht bestehen, es geht hier explizit um einzelne xml-Dateien die dort von Hand abgelegt werden.
MfG
Rouven
Hallo,
Aber wie schon gesagt darauf achten, daß es sich nicht (mehr) um eine Datei im Filesystem handeln muß - wenn das ganze als Jar im CLASSPATH liegt. Das ist bei Java ja durchaus üblich.
Ja, das stimmt prinzipiell schon, da dann (evtl.) nur die JAR-Datei im
Classpath steht, nicht aber das Verzeichnis, in dem sich die JAR-Datei
befindet.
Andererseits hat man in einem solchen Fall dann bei Konfigurations-
dateien ohnehin nur die Möglichkeit, die Datei an einer zentralen
Stelle abzulegen, da sich die JAR-Datei nachträglich nicht mehr
(sinnvoll) ändern läßt.
Natürlich läßt sich (etwas komplizierter) aber auch der Pfad der
JAR-Datei ermitteln, in der sich eine Ressource befindet.
Man muß letztendlich nur überprüfen, ob es sich beim Protokoll der
zurückgegebenen URL um "jar" handelt. In diesem Fall schneidet man die
ersten 4 Zeichen ("jar:") und alles ab dem Ausrufezeichen ("!") der
URL ab, und erhält damit die URL zur JAR-Datei.
Eine JAR-URL sieht z.B. so aus:
jar:file:/c:/mein/pfad/meinJar.jar!/pfad/dateiImJar.cfg
Daraus wird:
file:/c:/mein/pfad/meinJar.jar
Damit hat man jetzt zum einen den Pfad zur JAR-Datei, kann zum anderen
aber auch einzelne Dateien selektiv aus der JAR-Datei laden, indem
man den Pfad der gewünschten Datei nach dem Ausrufezeichen wieder
anhängt.
(Natürlich ist dann -- wie gesagt -- nur lesender Zugriff sinnvoll.)
Gruß
Slyh