Problem mit relativen Pfadangaben nach dem build

Netzgeist

Mitglied
Hallo nochmal,

ich arbeite seit einige Zeit an meinem ersten größerem Java-Programm. Innerhalb der GUI werden einige Dinge aus externen Dateien geladen und in externe Dateien geschrieben. Ich benutze hier ganz normale .txt Files weil die Daten einfach strukturiert sind. Das ganze mache ich für den Benutzer anpassbar was den Lese-/Speicherort betrifft indem ich eine settings.txt angelegt habe. Meine GUI schreibt dort hinein und liesst von dort relative Pfade.

Innerhalb meiner IDE war das nie ein Problem. Wenn ich das Programm gestartet hab war ./Ergebnisse gleich dem Unterordner /Ergebnisse meines Projektverzeichnisses. Nun stelle ich aber fest, dass wenn ich das Programm als .jar builde und ausführe, der relative Pfad sich scheinbar auf das Verzeichnis der java.exe bezieht und nicht des Ordners der dann die .jar beinhaltet.

Kann ich/ Wie kann ich relative Pfade so benutzen das sie nach dem build nicht vom Java Stammverzeichnis aus gesehen werden?
 
Hallo nrg,

könntest du vlt etwas weiter ausholen? Innerhalb meines Dateisystemes bietet sich (unter anderem) folgender Baum:
Code:
/NetBeansProjects/MyProject/MyProject/manifest.mf
/NetBeansProjects/MyProject/MyProject/settings.txt
/NetBeansProjects/MyProject/MyProject/dist/Aufruf.jar
/NetBeansProjects/MyProject/MyProject/src/myproject/Aufruf.java


manifest.mf:
Code:
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

Zu erwähnen ist hier warscheinlich netbeans als meine IDE. Google sagt mir, das eine Zeile wie
Classpath: Myproject.Aufruf.jar
richtig wär. Hilft mir im Ergebnis aber nicht, im Gegenteil. Statt nurnoch Settings.txt zu suchen findet java.exe die Mainklasse nichtmehr.
 
mach deine jar. öffne sie mit rar und öffne die manifest.mf. diese dann wie folgt anpassen:

Code:
Manifest-Version: 1.0
Main-Class: de.deinPackage.Main
Class-Path: .

die Leerzeile am Ende nicht vergessen. Jar schließen und Änderungen von winrar übernehmen lassen

edit: damit fügst du den Pfad, von dem die jar ausgeführt wird, dem Class-Path hinzu
 
Zuletzt bearbeitet:
In der jar sieht die manifest dann abers aus:

Aufruf:
Code:
C:\Program Files (x86)\Java\jre6\bin>java.exe -jar C:\Users\Ich\Desktop\Myproject\Myproject.jar
Exception in thread "main" java.lang.NoClassDefFoundError: org/jdesktop/beansbinding/Property
Caused by: java.lang.ClassNotFoundException: org.jdesktop.beansbinding.Property
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
Could not find the main class: myproject.MainGUI. Program will exit.

C:\Program Files (x86)\Java\jre6\bin>

Manifest.mf:
Code:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 16.3-b01 (Sun Microsystems Inc.)
Main-Class: myproject.MainGUI
Class-Path: lib/toplink-essentials.jar lib/toplink-essentials-agent.ja
 r lib/AbsoluteLayout.jar lib/beansbinding-1.2.1.jar .
X-COMMENT: Main-Class will be added automatically by build

in der rar gibts:
/META-INF/MANIFEST.MF
/Myproject/<<die ganzen *.class files etc
 
Die Frage ist, wie die settings.txt geöffnet werden soll. Wenn du über so was ähnliches wie
Java:
new File("./settings.txt");
verwendest, bringt das Anpassen des Classpath nix 🙂

Ich vermute, dass du die .jar über einen Doppelklick über eine Verknüpfung startest. Dann ist das aktuelle Verzeichnis das, wo die Verknüpfung liegt. Versuche es mal über die Kommandozeile: Start->ausführen->cmd, dann mittels cd zum Verzeichnis mit der .jar wechseln und
Code:
java -jar Aufruf.jar
eingeben und mit ENTER ausführen.
 
Noch mal kurz:
statt
Code:
C:\Program Files (x86)\Java\jre6\bin>java.exe -jar C:\Users\Ich\Desktop\Myproject\Myproject.jar
versuche mal
Code:
C:\Users\Ich\Desktop\Myproject>java -jar Myproject.jar

EDIT: und lass den Classpath im Manifest wie er am Anfang war
 
ziemlich genau so sieht mein code aus, madboy.
den weg über die kommandozeile gehe ich allerdings bereits. liegt daran das sich java hier bei der installation nicht als umgebungsvariable eingetragen hat. In welchem dir ich mich befinde während ich das dann so aufrufe spielt aber scheinbar keine Rolle. Gibt es denn eine alternative möglichkeit externe Daten ohne absolute Pfade zu benutzen? Mein Programm soll ja auch für andere Menschen benutzbar sein und ich will jenen nicht Vorschreiben auf welcher Platte und in welchem Ordner das Programm dann zu liegen hat.
 
Oh mann, langsam verzweifel ich. Mindestens 1 Buch + alle möglichen google ergebnisse zeigen mir
new File("foo.txt"); als legitime Möglichkeit, eine Datei zum bearbeiten nachzuladen. Nun google ich Classloader+inputstream und bekomm 100Zeilen lange Beispiele um ne Klasse zu laden. Will ich denn wirklich ne Klasse laden? Ich will doch nur textdateien auch lesen/schreiben wenn ich nicht gerade in ner IDE arbeite ;(
 
poste doch mal, wie du sie jetzt liest. ClassLoader holt dir mit 2 Zeilen einen InputStream. Dann halte ich es immer noch für gut explizit eine NPE zu catchen. Also wären wir bei 5 zeilen 🙂

mal davon abgesehn, dass ich textdateien als datenhaltung ohnehin themaverfehlt finde. nimm lieber properties
 
die user bearbeiten ihre textdateien mit dem programm, damit zu arbeiten ist also unumgänglich.

Settings.java: Schnipsel:
Java:
private String settingsFile = "./settings.txt";
private String profileDir;
...
public String getSettings(int settingindex) {
        try {
            FileReader fileReader = new FileReader(settingsFile);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            List<String> lines = new ArrayList<String>();
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                lines.add(line);
            }
            bufferedReader.close();
            return lines.get(settingindex);
        } catch (IOException ex) {
            return ("Settings-Datei nicht gefunden! \n" +
                    "Gesucht wurde in:\n" +
                    System.getProperty("user.dir") + "/" +settingsFile);
        }
    }
...

MainGUI.java: 2 Schnipsel um den gebrauch zu verdeutlichen:
Java:
public class MainGUI extends javax.swing.JFrame {
...
private Settings progressionSettings = new Settings();
...
private void jListProfileValueChanged(javax.swing.event.ListSelectionEvent evt) {                                          
        profil.setProfildatei(progressionSettings.getProfileDir()
                + "/" + jListProfile.getSelectedValue().toString());
        profil.leseProfildatei();
        jLabelInfoSitename.setText(profil.getHostname());
...

edit: Ich muss morgen früh raus und werde deshalb jetzt schlafen gehen. Ich lese eure Antwort morgen mit viel Elan und Rückmeldung, versprochen 🙂
 
Zuletzt bearbeitet:
Java:
private String settingsFile = "./settings.txt";
...
            FileReader fileReader = new FileReader(settingsFile);
...
            return ("Settings-Datei nicht gefunden! \n" +
                    "Gesucht wurde in:\n" +
                    System.getProperty("user.dir") + "/" +settingsFile);
...

???:L
Warum gibst du was anderes aus, als das was gelesen werden soll? Ändere es mal so ab:

Java:
private String settingsFile = "./settings.txt";
...
            FileReader fileReader = new FileReader(settingsFile);
...
            return ("Settings-Datei nicht gefunden! \n" +
                    "Gesucht wurde in:\n" +
                    + new File(settingsFile).getAbsolutePath());
...
 
probiers mal so:
Java:
	private String settingsFile = "settings.txt";

	public String getSettings(int settingindex) throws IOException, URISyntaxException {
		try {
			BufferedReader bufferedReader = new BufferedReader(new FileReader(new File(ClassLoader.getSystemClassLoader().getResource(settingsFile).toURI())));
			List<String> lines = new ArrayList<String>();
			String line = null;
			while ((line = bufferedReader.readLine()) != null) {
				lines.add(line);
			}
			bufferedReader.close();
			return lines.get(settingindex);
		} catch (NullPointerException npe) {
			return ("Datei konnte nicht gefunden werden!");
		}
	}

wenn du so aber Zeile für Zeile ausliest bzw auf Benutzereingabe o.ä. eine bestimmte Zeile liest, nehme ich meine Aussage zurück. das ist dann mehr als themaverfehlend....

Nimm eine Propertiesdatei, lese diese beim Start und synce diese beim Beenden. Für permanenten Austausch nimmst du eine Datenbank.

edit: bzw die IOException/URISyntaxException noch lokal behandeln, wenn du das so willst. Hab ich nur für einen kurzen Test werfen lassen
 
Zuletzt bearbeitet:
So, hallo zurück 🙂

Ein Problem ist immer schwerer zu erklären, je mehr Code zwischen ihm steckt. Ich habe nun ein sehr kleines Beispiel geschrieben um exakter zu verdeutlichen was mir Sorgen bereitet:

Der Filetree im Projektverzeichnis:
1.PNG

Es fällt der Ordner Zusatzdateien auf. Dies ist halt ein Ordner, der bei Auslieferung des Programm dabei sein soll. Das Programm verarbeitet sich ändernde Daten die dort liegen und liegen werden. Der Ordner muss ein normale Ordner sein auf den Anwenden zugreifen können. Wenn man mein fertiges Programm bekommt und entpackt, sollte es im Ordner so aussehen:
dist.PNG


Wenn ich das Projekt innerhalb der IDE starte sieht alles ok aus:
ok.PNG


Wenn ich jedoch das Projekt in der Kommandozeile aufrufe:
Code:
C:\Program Files (x86)\Java\jre6\bin>java.exe -jar "C:\Users\Ich\Documents\NetBeansProjects\Simplifiziert\dist\Simplifiziert.jar"
Exception in thread "main" java.lang.NullPointerException
        at javax.swing.JList$1.getSize(Unknown Source)
        at javax.swing.JList.getPreferredScrollableViewportSize(Unknown Source)
        at javax.swing.ViewportLayout.preferredLayoutSize(Unknown Source)
        at java.awt.Container.preferredSize(Unknown Source)
...etc.

Ein Blick auf den src Ordner:
3.PNG


Ein Blick in den "Zusatzdateien" Ordner:
4.PNG


Nun der eigendliche Code:

VerwaltetDateien.java:
Java:
package simplifiziert;

import java.io.File;

public class VerwaltetDateien{

   private File dateien = new File("Zusatzdateien");

   public String[] listeDateien() {
       return dateien.list();
   }
}

Frontend.java:
Java:
package simplifiziert;

public class Frontend extends javax.swing.JFrame {

    private VerwaltetDateien verwaltetDateien = new VerwaltetDateien();

    public Frontend() {
        initComponents();
    }
   
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList(verwaltetDateien.listeDateien());

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jScrollPane1.setViewportView(jList1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 238, Short.MAX_VALUE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>
   
    // Variables declaration - do not modify
    private javax.swing.JList jList1;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration

}

Main.java:
Java:
package simplifiziert;

public class Main {

    public static void main(String[] args) {
        new Frontend().setVisible(true);
    }

}
 
Zuletzt bearbeitet:
versuche ihm schon die ganze zeit beizubringen, dass seine datenhaltung (mal ganz abgesehn vom problem) alles andere als gut gewählt ist und er es einfach mit Properties und ClassLoader machen soll. Außerdem frag ich mich, ob er sich mein Codebeispiel angeschaut hat. Versteh mich nicht falsch, aber was willst du eigentlich mehr (den Link von maki hatte ich gestern noch gesucht aber leider nicht mehr gefunden :/)? Soll ich mich via Teamviewer auf deinen PC schalten und den copy&paste für dich machen? wäre allerdings auch nicht das erste mal 😀
 
Hallo,

für meine Anwendung _muss_ ich leider Textdateien aus nem Ordner listen, daran komme ich nicht vorbei. Klar ist diese Datenhaltung fürs Arsch. Klar ist aber auch das diese Daten so gestellt werden und ich damit leben muss. Ich kann die nicht in die jar stecken, weder als properties noch als textdateien, sie werden seperat gestellt in nem ordner dessen speicherort ./ordner relativ zum .jar speicherort ist.

@nrg: ich habe dein Beispiel gelesen und versucht, umzusetzen und zur verarbeitung der Dateien in JList den classloader benutzt:
Code:
new File(ClassLoader.getSystemClassLoader().getResource("testdatei.istleer").toURI())))
leider mit selbem Ergebnis. Liegt warscheinlich daran das testdatei.istleer nicht in der jar steckt/stecken soll sondern seperat zur jar geliefert wird.

@maki:
Wenn ich deinen Link verstanden habe, werden hier die Files die benutzt werden in die jar gepackt und über Classpath angesprochen. Das hilft mir leider nicht.

Ich halte es im Moment für eine probate Lösung, eine Art "1st-run-dialog" zu schreiben, der einmal nach allen absoluten Pfaden fragt und sie dann in ne properties schreibt. Was sagt ihr dazu? Alternativ wär ne Möglichkeit den absoluten Pfad der jar dynamisch in ner variablen zu haben schon genug.
 
leider mit selbem Ergebnis. Liegt warscheinlich daran das testdatei.istleer nicht in der jar steckt/stecken soll sondern seperat zur jar geliefert wird.

also das funktioniert, wenn deine settings.txt mit der jar datei im gleichen verzeichnis liegt.

z.b.:
c:\programme\deinProgramm\deineJar.jar
c:\programme\deinProgramm\settings.txt

voraussetzung ist die richtige Manifest.mf (siehe thread etwas weiter oben - Class-Path . )

Wenn du es dann z.b. in:
c:\programme\deinProgramm\whatever\settings.txt
legen willst musst du halt die uri dementsprechend erweitern. Ich habe es immer mit ClassLoader und Properties gemacht (s. link maki). Keine ahnung, ob das mit der uri in deinem Fall die "best-practise" ist aber funktionieren tut es auf jeden fall!
 
update: settings liegt derweil in ner xml vor.
die externen daten die einzulesen sind können mittlerweile eingelesen werden. durch zufall fand ich heraus, das
Java:
System.getProperty("user.dir");
mir das jeweils aktuelle verzeichnis - auch einer später laufenden jar-Datei - als String zurückgibt. _Das_ war wonach ich bat, ich wills zumindest hier nochmal dokumentieren um meinen Thread zukünftigen Fragen zur Antwort zu archivieren.

dazu fällt mir spontan ein zitat ein:
Alle sagten: Das geht nicht. Dann kam einer, der wusste das nicht und hat´s gemacht.

lg
 
Die Property war mir schon auch bekannt aber ich war vor einem halben Jahr vor dem gleichen Problem gestanden und da hat irgendwas nicht ganz damit funktioniert, wie es soll. Finde dein zitat allerdings etwas dreist dafür, dass wir/ich dir schon seit 10 posts eine Lösung geschrieben haben und du nur zu blöd (sry aber leider ist es so) warst es umzusetzen... Aber naja. Wenn es dein Problem löst, sind wir ja froh...
 

Zurück
Oben