Programmeinstellungen für Anwendung??

MiMa

Top Contributor
Nun möchte ich für mein JavaFX Programm Einstellungen speichern. Nicht nur nur Position, Größe und der letzten geöffneten Dateien. Ich möchte auch Verzeichnispfade (File) speichern, sowie boolsche Werte für aktivierte und deaktivierte Funktionen also nicht nur Strings und Integer Werte.
Angefangen über Registry, ini, XML und Preferences habe ich Informationen gefunden.
Leider ist mir nicht ganz klar wie Einstellungen überhaupt funktionieren?
Mir kommt es vor als wenn die Informationen in einer Externen datei gespeichert werden und bei Bedarf von Werten aus dieser Datei geladen werden?
Oder macht man eine eigene Klasse mit zu speichernden informationen, erstellt ein Objekt und macht es durch weiterreichen verfügbar?
Auf keinen Fall kommen ini und Regsitry in Frage, da ich gerne Platformübergreifend bleiben möchte.
Über eine Erleuchtung würde ich mich freuen.
Danke
Mi
 

MiMa

Top Contributor
Danke, habe ich gemacht.
Mir erschließt sich nicht ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind? Ich habe keine Idee, wie ich mit properties vorgehen soll?
Die Werte sollen nicht nur mit der GUI Synchonisiert werden, die Einstellungen zum Programm auflistet und auch dort geändert werden können. Diese Werte sollte im gesamten Programm zur Verfügung stehen.
Properties in JavaFX kenne ich als eingepackte Datentypen in Objekte, die mit der GUI synchronisiert werden.
 

thet1983

Top Contributor
ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind?
je nachdem...
Ich habe keine Idee, wie ich mit properties vorgehen soll?
vor dem start laden?

entweder die ließt die datei immer wenn du etwas benötigst oder vor dem start alles in ein object speichern und dieses durch die application reichen...beim beenden kannst du es ja dann speichern
 

MiMa

Top Contributor
Danke für die Info.
Das durchreichen hört sich ziemlich aufwendig an, da man auch berücksichtigen muss wenn sich das Objekt in einer Methode verändert wieder zurück zu geben.
Beim ändern in die Datei zuschreiben und bei bedarf von einem Ort die Werte zu laden, das hört sich ziemlich unkompliziert an. Jedoch habe ich keine Erfahrungen, ob sich die Leistung des Programms durch ständige Zugriffe auf das Dateisystem beeinträchtigt wird?
 

MiMa

Top Contributor
Hi,
ich weiß es ist schon etwas länger her, muss aber immer noch am dem Problem mit den Programmeinstellungen arbeiten.
Lange habe ich gebraucht mit JavaFX eine GUI auf FXML Basis zu erstellen und auch funktionales mit dem MVC Modell erlernen.
War für mich nicht einfach, habe lange im Netz und in Büchern verbracht.
Die Aktuelle Situation ist, das ich im Modell StringProperties verwendet habe.
Das Speichern mit Serialize speichern möchte.
Die Datei wird zwar geschrieben, gibt aber eine Fehlermeldung.

Mein log
Code:
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
INFO  kontroller.Einstellungen - einstellungenLaden wurde aufgerufen
INFO  kontroller.Einstellungen - Alle Werte im Fenster Einstellungen werden gelöscht
INFO  kontroller.Einstellungen - Alle Werte für Einstellungen werden von Festplatte geladen
INFO  modelle.Einstellungen - lese EinstellungenHD wurde aufgerufen
INFO  kontroller.Einstellungen - Neu geladene Werte für Einstellungen werden im Fenster aktualisiert
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  dms.DMS - Programm-Ende wird ausgeführt stop()-Methode

Methode Speichern auf HD
Java:
public void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream dateiAusgabeStream = null;
        try {
            dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf");
        } finally {
            try {
                dateiAusgabeStream.close();
            } catch (IOException e) {
            }
        }
    } // schreibeEinstellungenHD

Methode Laden von HD
Java:
public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        // prg.schreibePfadQuelle("Das ist ein neuer Quellpfad");
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }

        return programmEinstellungen;
    } // leseEinstellungenHD

NachSuchenQuelle.JPG
Button Speichern
Java:
@FXML
    private void einstellungenSpeichern(ActionEvent event) {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern

NachLadenButton.JPG
Button Laden
Java:
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblBildverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
        lblQuellverzeichnis.setText(einstellungen.holePfadQuelle().toString());
        tfDBBenutzername.setText(einstellungen.holeDbBenutzername().toString());
    } // einstellungenLaden

1. Fehlermeldung "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"
Obwohl die Datei erstellt wird, wird die catch IOException e ausgeführt?

2. Im Fenster Einstellungen "StringProperty"
Im Fenster wird StringProperty inklusive Value und den Vert in Eckigen Klammern ausgegeben.
Kannman im Fenster auch nur den Wert anzeigen lassen? "B:\01 Quelle"

3. Button "Laden"
Damit ich auch wirklich merke das neue Einstellungen im Fenster label dargestellt werden habe ich alle
Werte in den Labels zurückgesetzt lblQuellverzeichnis.setText("");
Dann lade ich die Einstellungen aus der Datei und es wird dann die StringProperty dargestellt

Schein zu funktionieren, bin mir aber nicht so ganz sicher, ob das wirklich so ist.

Ich würde mich freuen wenn mal jemand darüber schauen würde um den Punkt 1 und 2 zu klären.

Danke
Mi
 
K

kneitzel

Gast
Schau Dir doch bitte die Methoden von Deiner log Instanz an!

Diese haben normalerweise auf Methoden, denen Du am Ende einfach die Exception (bzw. ein Throwable, wovon Exception erbt) übergeben kannst.

Bitte merke Dir eine Sache: Entwickler, die in einem catch die Exception komplett ignorieren, landen auf dem Scheiterhaufen. :)

Also im Ernst: So Exceptions solltest Du wirklich nie ignorieren. Es mag evtl. ganz seltene Ausnahmen geben, bei denen das ok sein kann, aber selbst da ist Tracing meist noch sinnvoll....

Und sobald du das, was ich als erster Angesprochen habe, gemacht hast, bekommst Du auch deutlich mehr Informationen als nur ein "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"!

Und noch ein kleiner Hinweis:
Und dieses close im finally Block: Schau Dir doch einmal das "try with resources" an. Dann entfällt dies komplett und Java kümmert sich darum.
 
K

kneitzel

Gast
Zu 2. hatte ich noch nichts geschrieben: Hier habe ich Probleme, Dich genau zu verstehen. Du setzt ja den Text über setText(...) und Du rufst bei holePfadQuelle() einfach .toString() auf. (Und bei dem anderen Wert natürlich auch!)

Ich vermute einmal, dass das Resultat mit den eckigen Klammern von der toString Implementation der Klasse, von der die Instanz, die da zurück gegeben wird, kommt. Und wenn das ein key / value Paar ist, dann kannst Du bestimmt auch den Wert abfragen. Also nicht einfach .toString() darauf aufrufen.
 

MiMa

Top Contributor
Mit Log Instanz meinst du das Logger Objekt?
Meinst Du das man IOException an den Logger übergibt anstatt es in einer Catch Anweisung zu definieren?

Eine catch Exception zu ignorieren ist nicht meine Absicht und habe dies unter 1 beschrieben. Da ich in Büchern die Vorgehensweise erlernt habe die in etwa so aussieht frage ich mich wo das problem liegen könnte. Sehr viele Zeilen sind fsa ja nicht bei denen man etwas falsch machen könnte?
Und das die Datei dann plötzlich da ist sagt mir dass das writeObject etwas geschrieben hat.

Der Funktionskopf und finally habe ich geändert in
Java:
public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
...

} finally {
                dateiAusgabeStream.close();
        }
    } // schreibeEinstellungenHD

Zu 2. StringProperties
Damit hatte ich zuvor noch nie gearbeitet und dachte, das es wie Strings behandelt wird die sich halt Serialisieren lassen.
Werde mir die StringProperty noch mal genauer ansehen.
Eigentlich ist der Inhalt nur ein String "Ein Verzeichnispfad" und wird durch holePfadQuelle() aus dem objekt geholt und mit toString als String für das Label deklariert.

Anscheinend habe ich das Serializieren und Laden nicht richtig verstanden, da ich beim Einfügen eines zweiten Stringwertes gesehen habe das diese nicht richtig den labels zugeordnet werden. Die Inhalte sind vertauscht. Ich könnte mir vorstellen das ich das schreiben und laden bezüglich des Datenobjektes nicht wirklich korrekt gemacht habe.
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Ja, ich meinte Deine LOG Instanz. Ich weiß nicht, welches Logging Framework Du verwendest, aber Du könntest einmal schauen, ob der Code so funktionieren würde (im Forum-Editor geschrieben, daher evtl. Tippfehler vorhanden!)
Java:
        // 1. Try with ressource
        try (dateiAusgabeStream = new FileOutputStream(dateiEinstellungen)) {
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.

Man kann dann auch die Deklaration mit in das try ziehen und da der ObjectOutputStream auch AutoCloseable implementiert, würde ich dann am Ende zu sowas tendieren:
Java:
        // 0. Die Deklaration von dateiAusgabeStream vor dem try entfällt!
        // 1. Try with ressource
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
             ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.

Die Kommentare mit dem 0. / 1. / 2. und 3. sind nur für Dein Verständnis. Im Code würde ich die nicht setzen.
 

Robat

Top Contributor
Properties sind dazu da, an eine andere Property gebunden zu werden um automatisch aktualisiert zu werden.
So kannst du die TextProperty deines Textfeldes an die StringProperty deines Objektes binden.
Java:
lblQuellverzeichnis.textProperty() .bind(einstellungen.holePfadQuelle());
 

MiMa

Top Contributor
@Robat
Danke nochmal für die Aufklärung. Ist schon länger her, aber ja genau deshalb sollte ich mich damals mit Properties beschäftigen, da ich ja mit einer GUI Arbeiten wollte. Bezüglich GUI Programmierung war echt nicht viel Literatur zu finden nur die übliche für Anfänger, bei denen scheinbar alle abschreiben. Jedenfalls kommt es mir so vor, da die Inhalte alle gleich sind.
Hat man bedarf darüber hinaus, sieht es schlecht aus.
Habe den SetText durch das oben vorgeschlagene Binding ersetzt und bekomme das gewünschte Ergebnis.
Danke freue mich dafür und werde das Kapitel nochmal etwas vertiefen.
GuiBinding.JPG

@kneizel
Danke für den Vorschlag, ich werde das mal ausprobieren. Ich nutze Log4j2.

Mein Ziel war es ein Objekt mit dem Namen "einstellungen" zu erstellen. In dieses Objekt wollte ich alles was an Programmeinstellungen benötigt wird hinein speichern. Dieses Objekt sollte zu beginn des Programmes init() geladen werden. Mit dem aufrufen des Fensters "Einstellungen sollten alle Inhalte dieses Objektes verändert, aktualisiert oder gelöscht werden können. Der Laden Button habe ich aktuell nur zur Kontrolle eingebaut um die Speicherung zu testen. Beim Drücken von Speichern sollen alle Inhalte des Objektes auf die Festplatte gespeichert und das Fenster Einstellungen geschlossen werden.
Klingt nicht gerade kompliziert, aber für mich nicht gerade einfach.
 
Zuletzt bearbeitet:

MiMa

Top Contributor
Deinen Vorschlag für die Log Instanz habe ich implementiert und siehe da, es kommen jede Menge informationen
Code:
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185) ~[?:?]
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553) ~[?:?]
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510) ~[?:?]
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433) ~[?:?]
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179) ~[?:?]
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) ~[?:?]
    at modelle.Einstellungen.schreibeEinstellungenHD(Einstellungen.java:161) ~[DMS.jar:1.0]
    at kontroller.Einstellungen.einstellungenSpeichern(Einstellungen.java:139) ~[DMS.jar:1.0]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76) ~[javafx.base:?]
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275) ~[javafx.base:?]
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670) ~[javafx.fxml:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Node.fireEvent(Node.java:8865) ~[javafx.graphics:?]
    at javafx.scene.control.Button.fire(Button.java:200) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274) ~[javafx.controls:?]
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3876) ~[javafx.graphics:?]
    at javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) ~[javafx.graphics:?]
    at javafx.scene.Scene.processMouseEvent(Scene.java:1874) ~[javafx.graphics:?]
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) ~[javafx.graphics:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.notifyMouse(View.java:942) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175) ~[javafx.graphics:?]
    at java.lang.Thread.run(Thread.java:844) [?:?]
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start - Programm-Ende wird ausgeführt stop()-Methode

Da ich mir nicht vorstellen kann das es an der Schreibe Methode liegt, denke ich das es an meinem Datenodell liegt?

Hier sind zwei Methoden für die Knöpfe Quellverzeichnis und Zielverzeichnis.
Java:
@FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis
Wenn ich statt des Bindings setText verwende wird es richtig in das Fenster geschrieben, arbeite ich mit dem Binding, ist es falsch.
So wie im Bild einstellungen. Versteh ich nicht, wenn ich zwei verschiedene Labels mit Binding anpreche, wie kann denn dann der gleiche inhalt bei beiden Labels gleich sein obwohl das Quellverzeichnis schon korrekt dargestellt wurde?
einstellungen.JPG
Daher gehe ich davon aus, das mein Modell fehlerhaft sein könnte?
Die Klasse für das Modell ist im package modelle
Java:
public class Einstellungen implements Serializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    // Deklarationen Standardeinstellungen
    String dateiNameEinstellungen = "Programmeinstellungen.ser";
    String pfadEinstellungen = "B:\\00 Einstellungen\\";
    File dateiEinstellungen = new File(pfadEinstellungen + dateiNameEinstellungen);

    private final StringProperty pfadQuelle = new SimpleStringProperty();
    private final StringProperty pfadZiel = new SimpleStringProperty();
    private final StringProperty pfadOCR = new SimpleStringProperty();
    private final StringProperty pfadOhneDokumentArt = new SimpleStringProperty();
    private final StringProperty pfadLoeschen = new SimpleStringProperty();
    private final StringProperty pfadGeschuetzt = new SimpleStringProperty();
    private final StringProperty pfadBild = new SimpleStringProperty();

    private final StringProperty dbServeradresse = new SimpleStringProperty();
    private final StringProperty dbDatenbankname = new SimpleStringProperty();
    private final StringProperty dbBenutzername = new SimpleStringProperty();
    private final StringProperty dbPasswort = new SimpleStringProperty();
    private final StringProperty dnbToken = new SimpleStringProperty();

    // Konstruktor ohne Parameter
    public Einstellungen() {

    } // Konstruktor ohne Parameter

    // Methoden
    public StringProperty holePfadQuelle() {
        return pfadQuelle;
    }

    public void schreibePfadQuelle(String quellVerzeichnis) {
        pfadQuelle.set(quellVerzeichnis);
    }

    public StringProperty pfadQuelleProperty() {
        return pfadQuelle;

    }

    public StringProperty holePfadZiel() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle.set(zielVerzeichnis);
    }

    public StringProperty holePfadOCR() {
        return pfadOCR;
    }

    public void schreibePfadOCR(String ocrVerzeichnis) {
        pfadQuelle.set(ocrVerzeichnis);
    }

    public StringProperty holePfadOhneDokumentArt() {
        return pfadOhneDokumentArt;
    }

    public void schreibePfadOhneDokumentArt(String ohneDokumentartVerzeichnis) {
        pfadQuelle.set(ohneDokumentartVerzeichnis);
    }

    public StringProperty holePfadLoeschen() {
        return pfadLoeschen;
    }

    public void schreibePfadLoeschen(String loeschenVerzeichnis) {
        pfadQuelle.set(loeschenVerzeichnis);
    }

     public void schreibePfadGeschuetzt(String geschuetztVerzeichnis) {
        pfadQuelle.set(geschuetztVerzeichnis);
    }

    public StringProperty holePfadBild() {
        return pfadBild;
    }

    public void schreibePfadBild(String bildVerzeichnis) {
        pfadQuelle.set(bildVerzeichnis);
    }

    public StringProperty holeDbServeradresse() {
        return dbServeradresse;
    }

    public void schreibeDbServeradresse(String serveradresse) {
        pfadQuelle.set(serveradresse);
    }

    public StringProperty holeDbDatenbankname() {
        return dbDatenbankname;
    }

    public void schreibeDbDatenbankname(String datenbankname) {
        pfadQuelle.set(datenbankname);
    }

    public StringProperty holeDbBenutzername() {
        return dbBenutzername;
    }

    public void schreibeDbBenutzername(String benutzername) {
        pfadQuelle.set(benutzername);
    }

    public StringProperty holeDbPasswort() {
        return dbPasswort;
    }

    public void schreibeDbPasswort(String passwort) {
        pfadQuelle.set(passwort);
    }

    public StringProperty holeDnbToken() {
        return dnbToken;
    }

    public void schreibeDnbToken(String token) {
        pfadQuelle.set(token);
    }

    public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }

    } // schreibeEinstellungenHD

    public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        return programmEinstellungen;
    } // leseEinstellungenHD
} // Einstellungen

Der Kontroller ist im package kontroller
Java:
public class Einstellungen implements Initializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    modelle.Einstellungen einstellungen = new modelle.Einstellungen();

    @FXML
    private TextField tfDBServeradresse;
    @FXML
    private TextField tfDBDatenbankname;
    @FXML
    private TextField tfDBBenutzername;
    @FXML
    private TextField tfDBPasswort;
    @FXML
    private TextField tfTokenDNB;
    @FXML
    private Label lblQuellverzeichnis;
    @FXML
    private Label lblZielverzeichnis;
    @FXML
    private Label lblOCRVerzeichnis;
    @FXML
    private Label lblOhneDokumentartVerzeichnis;
    @FXML
    private Label lblLoeschverzeichnis;
    @FXML
    private Label lblGeschuetztesVerzeichnis;
    @FXML
    private Label lblBildverzeichnis;

    /**
     * Initialisierung der Kontroller Klasse.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");     
    } // initialize

    @FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis

    @FXML
    private void sucheOCRVerzeichnis(ActionEvent event) {
        LOG.info("sucheOCRVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das OCR Verzeichnis");
        einstellungen.schreibePfadOCR(verzeichnis);
        lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadOCR());
    } // sucheOCRVerzeichnis

    @FXML
    private void sucheBildverzeichnis(ActionEvent event) {
        LOG.info("sucheBildverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Bildverzeichnis");
        einstellungen.schreibePfadBild(verzeichnis);
        lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
        LOG.info("Selektiertes Bildverzeichnis " + einstellungen.holePfadBild());
    } // sucheBildverzeichnis

    @FXML
    private void sucheOhneDokumentartVerzeichnis(ActionEvent event) {
        LOG.info("sucheOhneDokumentartVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Verzeichnis ohne Dokumentart");
        einstellungen.schreibePfadOhneDokumentArt(verzeichnis);
        lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
        LOG.info("Selektiertes Verzeichnis ohne Dokumentart " + einstellungen.holePfadOhneDokumentArt());
    } // sucheOhneDokumentartVerzeichnis

    @FXML
    private void sucheLoeschverzeichnis(ActionEvent event) {
        LOG.info("sucheLoeschverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Löschverzeichnis");
        einstellungen.schreibePfadLoeschen(verzeichnis);
        lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
        LOG.info("Selektiertes Löschverzeichnis " + einstellungen.holePfadLoeschen());
    } // sucheLoeschverzeichnis

    @FXML
    private void sucheGeschuetztesVerzeichnis(ActionEvent event) {
        LOG.info("sucheGeschuetztesVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Geschützte Verzeichnis");
        einstellungen.schreibePfadGeschuetzt(verzeichnis);
        lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
        LOG.info("Selektiertes Geschützte Verzeichnis " + einstellungen.holePfadGeschuetzt());
    } // sucheGeschuetztesVerzeichnis

    @FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
    

    @FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblBildverzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
          lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
          lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
          lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
          lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
          lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
          lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
          lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
                  
    } // einstellungenLaden

} // Einstellungen
 
K

kneitzel

Gast
Also das Problem ist in Deinem Log diese Stelle:
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty

Du kannst mit ObjectOutputStream nur Objekte schreiben, die Serializable sind. Und das ist SimpleStringProperty nicht.

Aber Du implementierst Serializable ja bei Einstellungen implementierst, wäre eine Möglichkeit, dass Du die Serialisierung selbst vornimmst:
a) alle Properties markierst du als transient
b) Du liest und schreibst die Werte selbst in den entsprechenden Funktionen:
Java:
private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();
    s.writeLong(idProperty().longValue());
    s.writeUTF(aStringProperty().getValueSafe()); // getValueSafe damit kein null rein kommen kann.
    // ...
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    idProperty.set(s.readLong());
    aStringProperty.set(s.readUTF());
    // Gleiche Reihenfolge wie bei writeObject()
    // ...
}
 

looparda

Top Contributor
Ich würde das ganze aufspalten, sodass deine Einstellungen keine Properties haben. Die Properties wandern in ein ViewModel EinstellungenViewModel, das aus einem Model konstruiert werden kann. Im Controller verbindest du nur noch den View mit dem ViewModel
Java:
tfDBServeradresse.textProperty().bind(einstellungenVm.tfDBServeradresseProperty());
Die Einstellungen sind somit einfach zu serialisieren und die Zuständigkeiten besser geklärt. Ich hoffe das war verständlich genug skizziert.
 

MiMa

Top Contributor
Hi, wie meinst du das mit dem aufteilen?
Ich habe ein Model, einen Kontroller und eine View! Ich habe ewig gebraucht das zu begreifen und jetzt versuche ich das in der Praxis. Im Kontroller "EinstellungenK" wird wir ein Objekt erzeugt welches durch den Parameterlosen Konstruktor ein Einstellungen Objekt erzeugt, mit den Methoden und Properties aus der Klasse "EinstellungenM". Somit habe doch doch schon mal eine Verbindung zum Modell durch den Kontroller hergestellt. War doch richtig?
Und im Kontroller und dem Binding wird das Modell dann zum Fenster gebunden?!?!
Wenn ich jetzt etwas in die Properties stecke, sollte es dann doch im Fenster sichtbar werden? War doch so?
Eine wirklich gute Literatur hierzu habe ich nie wirklich gefunden die das an einem Beispiel in JavaFX zeigt.

Es scheint so das ich etwas mit den Methoden "schreibePfadQuelle", "schreibePfadZiel" zwar hineinschreibe aber es nicht wirklich drin ist oder so. Zumindest habe ich das Gefühl, dass das objekt nicht so reagiert wie ich es erwartet hätte.
 

mrBrown

Super-Moderator
Mitarbeiter
Dein Model hat das Problem, dass es JavaFX-Properties benutzt, und damit kaum von der View unabhängig ist. Besser ist’s, das Model möglichst einfach zu halten.

Um das Model trotzdem an die View binden zu können, führt man ein ViewModel ein, dieses enthält die Properties (an die sich die View binden kann) und kennt das Model, holt sich also den aktuellen Zustand daher und schreib Änderungen dort hin.
 

looparda

Top Contributor
Ein Minimalbeispiel, um es darzustellen.
Die Einstellungen (Modell) als POJO ohne Abhängigkeiten zu JavaFX. Reines Java Standardlib.
Java:
// POJO
public class Einstellungen {

    private String tfDBServeradresse;

    public Einstellungen setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse = tfDBServeradresse;
        return this;
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse;
    }

    public static Einstellungen defaultSettings() {
        return new Einstellungen()
                .setTfDBServeradresse("localhost");
    }
}
Das ViewModel als Mapper zwischen Modell und View. Hier kommt jede Logik rein, die zur Darstellung benötigt wird. z.B. die Logik, ob ein Feld disabled sein soll oder nicht (wird wiederum als Property nach außen sichtbar, um die View daran zu binden). Das ViewModel darf keine Referenzen auf die View haben.
Java:
public class EinstellungenViewModel {
    private final StringProperty tfDBServeradresse = new SimpleStringProperty(this, "tfDBServeradresse");

    public EinstellungenViewModel(Einstellungen einstellungen) {
        tfDBServeradresse.set(einstellungen.getTfDBServeradresse());
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse.get();
    }

    public StringProperty tfDBServeradresseProperty() {
        return tfDBServeradresse;
    }

    public void setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse.set(tfDBServeradresse);
    }

}
Der View-Controller macht das Binding der Observables der View und des ViewModel. Streng genommen kommt hier kein bisschen Logik rein, sondern nur das Herstellen der Bindings.
Java:
public class EinstellungenView implements Initializable {

    @FXML
    private TextField tfDBServeradresse;

    private final EinstellungenViewModel viewModel;

    public EinstellungenView() {
        // Load from file in real world example
        Einstellungen einstellungen = Einstellungen.defaultSettings();
        // Construct view model
        this.viewModel = new EinstellungenViewModel(einstellungen);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        tfDBServeradresse.textProperty().bind(viewModel.tfDBServeradresseProperty());
    }
}
Ein Startercode, um die GUI zu erzeugen.
Java:
public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("EinstellungenView.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.sizeToScene();
        primaryStage.show();
    }
}
Das sehr simple FXML
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="EinstellungenView"
            prefHeight="400.0" prefWidth="600.0">

    <TextField fx:id="tfDBServeradresse"/>

</AnchorPane>
Ich hoffe dir wird ersichtlich, wie deine Klasse Einstellungen sauberer wird und die Serialisieren wieder einfach.
 

MiMa

Top Contributor
Vielen Dank für die Ausführliche Darstellung.
Wenn ich das richtig verstanden habe, sollte ich mein Modell genau so gestalten, wie ich es bei meinen Konsolenprogrammen ohne GUI in normalen Java Code gemacht habe. Dann eine weitere Modell Klasse (ViewModell) die im Prinzip die gleichen Inhalte der zu speichernden Werte hat wie das Modell, aber als Properties weil diese an das View gebunden werden können. Im Kontroller sind dann nur Methoden enthalten die zum View gehören wie Buttons, Labels usw.
Methoden zur Verarbeitung von Werten kommen weiterhin in das Modell!
Das Serialisieren der Werte wird dann nur mit dem Modell durchgeführt, also dort implements Serializable?
Habe dann mal unter ViewModel im Netz nachgesehen und da tauchte dann das Konzept des MVVM auf.
Ich werde mein Programm nach den o.a. Informationen abändern und hoffe, das es klappt :)

Eine Frage hätte ich noch zur Organisation der Pakete.
Sollte man diese nach Fenster gruppieren, was zum Fenster Einstellungen gehört?
Also Paket Einstellungen und darunter dann das Modell, ViewModell, Kontroller, FXML, stil.css usw.

Oder wie im Bild nach Gruppen wie Fenster, Kontroller, Modelle, Stiele.css, FXMLs?
packages.JPG
Vielen Dank für die tolle Hilfe, ich melde mich zurück wenn es es funktioniert, oder wenn es auch nicht funktioniert :)
Mi
 

looparda

Top Contributor
Methoden zur Verarbeitung von Werten kommen weiterhin in das Modell!
Der View ruft Methoden aus dem ViewModel auf, wenn Aktionen auftreten wie Button gedrückt. Das ViewModel entscheidet was passieren soll, wenn die Methode aufgerufen wurde. Beispielsweise führst du im ViewModel eine Validierung durch, setzt eine Fehlerproperty, die dafür sorgt, dass im View der Fehler sichtbar wird oder rufst letztlich eine Methode aus dem Modell auf.
Das Serialisieren der Werte wird dann nur mit dem Modell durchgeführt, also dort implements Serializable?
Ja
Habe dann mal unter ViewModel im Netz nachgesehen und da tauchte dann das Konzept des MVVM auf
Ja, es handelt sich im groben um das Konzept. Da ich nicht alle Nuancen kenne und damit sicher nicht einhalte bezeichne ich mein Beispiel aber nicht als MVVM Beispiel.
Wie du die Klassen zusammenpackst ist Geschmackssache. Kommt auch drauf an, ob du ein Master-Detail UI hast, ein Form auslagerst, welches du in View, Update und Edit einbindest oder nur über Button navigierst und jedes mal ein neues Fenster öffnest. Für sowas kleines reicht vermutlich die Struktur
in src
proj.Application.java
proj.model.Einstellungen.java
proj.ui.einstellungen.EinstellungenView.java
proj.ui.einstellungen.EinstellungenViewModel.java
und in resources
proj.ui.einstellungen.EinstellungenView.fxml
proj.ui.einstellungen.style.css
 

MiMa

Top Contributor
Das minimale Beispiel mit der Serveradresse habe ich jetzt mal in mein Projekt mit Dateien übertragen.
Es funktioniert zwar soweit, aber die Abwicklung hat sich mir noch nicht so ganz erschlossen.
Ich Dokumentiere zwar sehr viel, das ist aber erstmal ein Hilfmittel für mich um es besser zu verstehen.

Auszug der Klasse Einstellungen (Java-Standardlib)
Java:
private String pfadQuelle;
private String pfadZiel;

// Methode für das einsetzen von Standard Werten
    public static Einstellungen standardWerte() {
        String heimVerzeichnis = System.getProperty("user.home");
        // return new Einstellungen().schreibePfadQuelle(homeVerzeichnis+"\\Dateien\\01 Quelle");
        Einstellungen standardWerte = new Einstellungen();
        standardWerte.schreibePfadQuelle(heimVerzeichnis+"\\Dateien\\01 Quelle");
        standardWerte.schreibePfadZiel(heimVerzeichnis+"\\Dateien\\02 Ziel");    
        return standardWerte;
    } // Methode für das einsetzen von Standard Werten

public String holePfadQuelle() {
        return pfadQuelle;
    }
   
    public Einstellungen schreibePfadQuelle(final String quellVerzeichnis) {
        pfadQuelle = quellVerzeichnis;
        return this;
    }
   
    public String holePfadZiel() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle = zielVerzeichnis;
    }
Jedoch habe ich Schwierigkeiten zu verstehen warum bei der schreibePfadQuelle der Datentp Einstellungen gewählt wurde?
Es wird doch nur den Pfad der einen String enthält zurück gegeben!
Normalerweise definiere ich so wie bei schreibePfadZiel eine Methode die nichts zurückgibt mit "void" bei einer Set-Methode?

Auszug der Klasse EinstellungenFM (ViewModell)
Java:
private final StringProperty pfadQuelle = new SimpleStringProperty();
private final StringProperty pfadZiel = new SimpleStringProperty();

// Erstellt das Einstellungen Fenster Modell
    public EinstellungenFM(Einstellungen einstellungen){
        pfadQuelle.set(einstellungen.holePfadQuelle());
        pfadZiel.set(einstellungen.holePfadZiel());
    }

public String holePfadQuelle() {
        return pfadQuelle.get();
    }
   
    public StringProperty pfadQuelleProperty() {
        return pfadQuelle;
    }
   
    public void schreibePfadQuelle(final String pfadQuelle) {
        this.pfadQuelle.set(pfadQuelle);
    }
   
    public StringProperty holePfadZiel() {
        return pfadZiel;
    }
    public StringProperty pfadZielProperty() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle.set(zielVerzeichnis);
    }

Bei EinstellungenFM wird ein Kosntruktor aufgerufen der einen Parameter einstellungen enthält.
Ich denke mal hier wird ein Objekt Erzeugtder aus dem Bauplan von Einstellungen der Standardlib erzeugt wird und direkt Werte zugewiesen bekommt. Zu diesem Zeitpunkt ist mir nicht bekannt, ob sich bereit Werte darin enthalten? Ich denke nicht, da die Methode standardWerte() noch nicht aufgerufen wurde?
Ich verstehe aber schon das im ViewModell alles enthalten sein soll was für den Fensteraufbau und deren verhalten notwendig ist.

Auszug der Klasse EinstellungenFK (ViewController)
Java:
@FXML
private Label lblQuellverzeichnis;
@FXML
private Label lblZielverzeichnis;

private EinstellungenFM fensterModell;

// Konstruktor um das Fenstermodell zu erzeugen
    public EinstellungenFK() {
        // Laedt die Standardwerte aus dem Modell
        Einstellungen einstellungen = Einstellungen.standardWerte();
        // Erstellt das Modell des Fenstermodells
        this.fensterModell = new EinstellungenFM(einstellungen);
    } // Konstruktor um das Fenstermodell zu erzeugen

public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
    } // initialize

@FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        fensterModell.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
        LOG.info("Selektiertes Quellverzeichnis " + fensterModell.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        fensterModell.schreibePfadZiel(verzeichnis);
        lblZielverzeichnis.textProperty().bind(fensterModell.pfadZielProperty());
        LOG.info("Selektiertes Zielverzeichnis " + fensterModell.pfadZielProperty());
    } // sucheZielverzeichnis

Der Fenster Kontroller soll also nur die Daten vom Modell zum Fenster verbinden.
Also wird EinstellungenFM mit der variable fensterModell definiert, welche dann im Konstruktor irgendwie gefüllt werden?
Der Konstruktor so wie er da ist verstehe ich nicht so ganz. Ich weiß das dort durch die Methode die Pfade gesetzt werden.
Es wird ein Objekt einstellungen erzeugt und dort die die Werte übergeben.
Aber einstellungen wird doch nach dem Bauplan erzeugt. Wird in diesem Objekt tatsächlich Quellverzeichnis und Zielverzeichnis in den entsprechenden Platzhalter erzeugt und übergeben so wie es in der Vorgabe definiert wurde?

Wenn ich mein Programm mit beiden Daten (Quellverzeichnis und Zielverzeichnis) ausführe dann erhalte ich im Textfeld des Fensters nur den letzten also Zielverzeichnis.
Im label Quellverzeichnis wird der Pfad des Zielverzeichnisses angezeigt und im label Zielverzeichnis nichts.
 

looparda

Top Contributor
Jedoch habe ich Schwierigkeiten zu verstehen warum bei der schreibePfadQuelle der Datentp Einstellungen gewählt wurde?
Es wird doch nur den Pfad der einen String enthält zurück gegeben!
Normalerweise definiere ich so wie bei schreibePfadZiel eine Methode die nichts zurückgibt mit "void" bei einer Set-Methode?
Ich habe hier bloß "fluent-Setter" benutzt (z.B. hier erklärt).
Zu diesem Zeitpunkt ist mir nicht bekannt, ob sich bereit Werte darin enthalten? Ich denke nicht, da die Methode standardWerte() noch nicht aufgerufen wurde?
Du gibst doch in den Konstruktor des ViewModels ein Einstellungs-Objekt rein, welches im Kontroller erzeugt wurde:
public EinstellungenFK() { // Laedt die Standardwerte aus dem Modell Einstellungen einstellungen = Einstellungen.standardWerte(); // Erstellt das Modell des Fenstermodells this.fensterModell = new EinstellungenFM(einstellungen); } // Konstruktor um das Fenstermodell zu erzeugen

standardWerte wurde zu dem Zeitpunkt aufgerufen, und das Einstellungs-Objekt auf die Werte gesetzt, die in standardWerte gesetzt werden. Das natürlich nur für dieses minimale Beispiel. Später liest du die Einstellungen aus einer Datei oder Properties (java.util).
Aber einstellungen wird doch nach dem Bauplan erzeugt. Wird in diesem Objekt tatsächlich Quellverzeichnis und Zielverzeichnis in den entsprechenden Platzhalter erzeugt und übergeben so wie es in der Vorgabe definiert wurde?
Auch hier: Es wird das gesetzt, was in standardWerte definiert ist.

Wenn ich mein Programm mit beiden Daten (Quellverzeichnis und Zielverzeichnis) ausführe dann erhalte ich im Textfeld des Fensters nur den letzten also Zielverzeichnis.
Du hast im Konstruktor nur eins von zwei Textfeldern gebunden und bindest in den Action-Handlern erneut Properties.
Die Bindings erledigst du ausschließlich im Konstruktor.
In den Action-Handlern setzt du die neuen Verzeichnispfade im ViewModel über die Setter ( schreibePfadZiel usw. ) und der View aktualisiert sich aufgrund der Bindings selbstständig.
 

MiMa

Top Contributor
Ich hab es mir jetzt noch mal angeschaut und bin ein bisschen durcheinander.
Vielleicht hat jemand Zeit in form einer Nachhilfestunde mir das an meinem Code über Teamviever zu erklären.
Ich bin auch gerne bereit die Stunde zu bezahlen, bitte einfach eine PN senden.
 

MiMa

Top Contributor
Mittlerweile habe ich es geschafft alle Werte in der Methode standardWerte() zu definieren und in das ViewModel zu transferieren und werden auch dort im Fenster angezeigt. Im Fenster kann ich durch meine Button Methoden die Verzeichnisse ändern und durch Logeinträge konnte ich kontrollieren, das die neuen Pfade auch tatsächlich im VielModell enthalten sind.

Ich weiß jetzt wie es in etwa funktionieren sollte, aber es herrscht bei mir immer noch in den ein oder anderen Punkten Unklarheiten.
Wobei im MVC auf ein Muster mit Model, View und Controller beschrieben wird, kann es für JavaFX anwendungen so nicht verwendet werden. Zumindest habe ich das jetzt so interpretiert, da die Properties nicht Model platziert werden können, wenn es auch um Persistente Speicherung der Modell-Daten geht?!?
Beim MVVM basiert der Entwurf auf ein Model, VieModel und der View. Hier im Forum habe ich jetzt gelernt zu diesem Entwurf noch einen ViewContoller hinzu zu nehmen der nur die Bindung übernimmt. Das macht es etwas übersichtlicher. So wie ich es auf der Wikipedia Seite gelesen habe, aber nicht nötig, da die Bindung, die Steuerung und Geschäftslogik im ViewController sein darf.
Mittlerweile habe ich aus dem Netz, aus Büchern und Tutorials unterschiedliche Informationen. Ich tue mich ein bisschen schwer damit um zu gehen, weil ich noch keine Erfahrung in GUI Programmierung gemacht habe und erst mit dieser Beispielapplikation damit angefangen habe.

Ich weiß jetzt, daß die View (Fenster) durch die Property Bindung im Controller mit dem ViewModel verbunden ist und jede Eingabe im Textfield automatisch aktualisiert wird. Jedoch ist mir nicht klar wie dann die Aktualisierung mit dem Modell der Standardlib in der ich implements Serializable verwendet habe?

Schließlich möchte ich die gesamten Daten im Einstellungen Fenster gerne auf Festplatte permanent speichern.
Um den Ablauf besser zu verstehen, habe ich mir einen kleine Hilfe erstellt.
Einstellungen.png
Ich habe Schwierigkeiten zu verstehen, wie ich die Daten Serialisieren kann, da ich nicht verstehe, wie die Daten aus dem ViewModel in mit dem Modell abgeglichen werden.
 
Zuletzt bearbeitet:

looparda

Top Contributor
Wobei im MVC auf ein Muster mit Model, View und Controller beschrieben wird, kann es für JavaFX anwendungen so nicht verwendet werden. Zumindest habe ich das jetzt so interpretiert, da die Properties nicht Model platziert werden können, wenn es auch um Persistente Speicherung der Modell-Daten geht?!?
Doch, kann man schon machen, aber gibt halt Wildwuchs, in meinen Augen. Ich rate davon ab und bin für eine Trennung. MVVM bekommt diese Trennung in meinen Augen am leichtesten hin.

So wie ich es auf der Wikipedia Seite gelesen habe, aber nicht nötig, da die Bindung, die Steuerung und Geschäftslogik im ViewController sein darf.
Also alles in den ViewController? Das ist ja Quatsch. Das ViewModel enthält die Logik für die Darstellung und delegiert an das Model für die Geschäftslogik. Der einzige Job des (View)Controllers ist es die in MVVM zentrale Rolle des Binders einzunehmen und die Action Handler zu definieren, die wiederum nur an das ViewModel delegieren. Der Binder stellt die Bindings zwischen View und ViewModel her. Dank der Properties in JavaFX hat der Binder nicht viel zutun als eine Property des View mit der zugehörigen Property des ViewModels zu verbinden. Hätten wir keine Properties, würden wir im Binder vermutlich das Oberserver-Pattern nutzen und manuell Änderungen durch Listener mappen. Ich will mir gar nicht vorstellen, wie hässlich das im Vergleich wäre.
Ein View ist erstmal kein Fenster. Du kannst z.B. mehrere Instanzen eines Views im gleichen Fenster anzeigen. FensterModell ist deshalb auch keine gute Übersetzung, fällt mir auf.
Jedoch ist mir nicht klar wie dann die Aktualisierung mit dem Modell der Standardlib in der ich implements Serializable verwendet habe?
Du definierst in deiner View eine Action (z.B. Klick auf Button 'Speichern'), dazu definierst du einen Action-Handler im Controller. Der Controller delegiert die Action an das ViewModel. Das ViewModel validiert nun die Anfrage in Kooperation mit dem Modell. Beispielsweise wenn auf Speichern geklickt wurde sollen doch bitte alle Felder ausgefüllt sein. Falls ok: Speichern, falls nicht ok: nicht Speichern. Je nach Validierungsergebnis werden
  1. die Werte des ViewModels in das Modell übertragen
  2. die Geschäftslogik des Models aufgerufen (speichern) bzw. wird das Modell an einen für die Persistierung zuständigen Service weitergegeben
  3. Properties aktualisiert (z.B. eine Fehler-Property oder "Alles OK und erfolgreich gespeichert")
Deine Grafik, kann ich leider nicht ganz deuten. Die Pfeile sind nicht definiert und ein Pfeil ist orange(?). Manche Pfeile gehen in eine Richtung und manche in zwei.
Ich habe Schwierigkeiten zu verstehen, wie ich die Daten Serialisieren kann, da ich nicht verstehe, wie die Daten aus dem ViewModel in mit dem Modell abgeglichen werden.
Das habe ich nun beantwortet. Ab da musst du nur noch das Schreiben und Lesen in ein File anstoßen.
 

MiMa

Top Contributor
Mit der Grafik habe ich mir einen überblick verschafft, was passiert nachdem das JavaFX Programm ausgeführt wird.
Wie gesagt, ist JavaFX und die GUI Programmierung für mich neu.
Linien mit Doppelpfeilen sollen veranschaulichen, das die Daten hin und zurück gehen.
Der Roten Linien war das wo ich nicht wie es geregelt wird damit das Model die Daten vom VielModell aktualisiert.
Du hast mir gesagt, des es im Speicher Button passiert. Also Dort muss ich alle Variablen vom Modell Viel auf das Model übertragen und dann das Modell Speichern.

Ich werde den Button Code posten wenn ich es fertig habe.
 

MiMa

Top Contributor
Der Code für den Button speichern, indem ich einen Variableninhalt von einem Modell in das andere Kopiere und einen Logeintrag mit dem Wert erzeuge und dann das Modell auf Festplatte Speichern.

Java:
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei = null;
        try {
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD


Java:
@FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibePfadQuelle(fensterModell.holePfadQuelle());
        LOG.info("Modell Einstellungen Pfad Quelle : " + einstellungen.holePfadQuelle());    
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern

Der Code gibt in der Netbeans iDE keine Fehler an, erzeugt aber beim ausführen folgende Fehler in der Konsole.
Die Meldung ist ziemlich umfangreich, wenn man bedenkt, das ich nur eine Variablen von einem Model ins andere Modell kopiere und einen LOG ausgeben wollte.
Der Logeintrag wurde nicht erzeugt.
FXML Loader hat doch was mit dem Fenster zu tun?
Das Fenster wurde ordnungsgemäß erstellt und dargestellt, mit dem Speichern Button wird weder einen neues Fenster erzeugt oder dargestellt.

Code:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8865)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
    at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 47 more
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFK.einstellungenSpeichern(EinstellungenFK.java:165)
    ... 58 more
 

looparda

Top Contributor
Du bist im zweiten Code offensichtlich im Controller, hier solltest du die Einstellungen nicht anfassen. Alles geht über das ViewModel. Lies hierzu erneut:
Du definierst in deiner View eine Action (z.B. Klick auf Button 'Speichern'), dazu definierst du einen Action-Handler im Controller. Der Controller delegiert die Action an das ViewModel. Das ViewModel validiert nun die Anfrage in Kooperation mit dem Modell. Beispielsweise wenn auf Speichern geklickt wurde sollen doch bitte alle Felder ausgefüllt sein. Falls ok: Speichern, falls nicht ok: nicht Speichern. Je nach Validierungsergebnis werden
  1. die Werte des ViewModels in das Modell übertragen
  2. die Geschäftslogik des Models aufgerufen (speichern) bzw. wird das Modell an einen für die Persistierung zuständigen Service weitergegeben
  3. Properties aktualisiert (z.B. eine Fehler-Property oder "Alles OK und erfolgreich gespeichert")

Im Action Handler einstellungenSpeichern setzt du immer noch Bindings, die eigentlich nur im Konstruktor einmalig gesetzt werden sollen. Ich dachte das hatte ich im vorhergehenden Beiträgen schon bemängelt.

Eine NullPointerException deutet darauf hin, dass eins deiner Attribute null ist. Das kann leicht passieren, wenn man ein Feld anders als im FXML benennt, denn das Mapping passiert über den Namen. Ansonsten steht in der Exception explizit die Quelle des Übels ((EinstellungenFK.java:165)).
 

MiMa

Top Contributor
Aktuell habe ich noch ein Problem mit dem Binden der Textfelder im FensterKontroller.
Ich hatte bisher immer nur die Labels gebunden und das funktioniert auch soweit ganz gut.
Nun habe ich auch die TextFelder eingebunden (TextField) und ab dem Zeitpunkt kann ich keine Eingaben mehr im Fenster machen wenn das Programm läuft?

Auszug aus "EinstellungenFensterKontroller"
Java:
// Einstellungen Fenster Kontroller
@FXML
    private Label lblQuellverzeichnis;
    @FXML
    private Label lblZielverzeichnis;
    @FXML
    ...
    ...

    @FXML
    private TextField tfDBServeradresse;
    @FXML
    private TextField tfDBDatenbankname;
    @FXML
    private TextField tfDBBenutzername;
    @FXML
    private TextField tfDBPasswort;
    ...
    ...

    // Datenmodelle
    private EinstellungenFM fensterModell;
    private Einstellungen einstellungen;

    // Konstruktor um das Fenstermodell zu erzeugen
    public EinstellungenFK() {
        // Erzeugt ein Modell-Objekt Einstellungen mit Standardwerten
        this.einstellungen = Einstellungen.standardWerte();

        // Erzeugt ein Fenster-Modell aus dem Einstellungen Model-Objekt
        this.fensterModell = new EinstellungenFM(einstellungen);
    } // Konstruktor um das Fenstermodell zu erzeugen

    /**
     * Initialisierung der Kontroller Klasse.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");
        // Binden der Werte an das Fenster
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
        lblZielverzeichnis.textProperty().bind(fensterModell.pfadZielProperty());
        ...
        ...
        tfDBServeradresse.textProperty().bind(fensterModell.dbServeradresseProperty());
        tfDBDatenbankname.textProperty().bind(fensterModell.dbDatenbanknameProperty());
        tfDBBenutzername.textProperty().bind(fensterModell.dbBenutzernameProperty());
        tfDBPasswort.textProperty().bind(fensterModell.dbPasswortProperty());
   } // initialize

...
@FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        EinstellungenFM.einstellungenSpeichernHD(fensterModell, einstellungen);
    } // einstellungenSpeichern
...
} // Einstellungen Fenster Kontroller

Auszug aus "Einstellungen Fenster Modell"
Java:
// Einstellungen Fenster Modell
...
    private final StringProperty pfadEinstellungen = new SimpleStringProperty();
    private final StringProperty pfadQuelle = new SimpleStringProperty();
    ...
    // Erstellt das Fenster Modell Einstellungen
    public EinstellungenFM(Einstellungen einstellungen) {
        // Holt die Werte aus den Variablen in das Fenster Modell
        pfadEinstellungen.set(einstellungen.holePfadEinstellungen());
        pfadQuelle.set(einstellungen.holePfadQuelle());
        ...
        dbServeradresse.set(einstellungen.holeDbServeradresse());
        dbDatenbankname.set(einstellungen.holeDbDatenbankname());
        dbBenutzername.set(einstellungen.holeDbBenutzername());
        dbPasswort.set(einstellungen.holeDbPasswort());
    } // Konstruktor Fenster Modell
   
        public static void einstellungenSpeichernHD(EinstellungenFM fensterModell, Einstellungen einstellungen) {
        einstellungen.schreibePfadQuelle(fensterModell.holePfadQuelle());
        ...
        einstellungen.schreibeDbServeradresse(fensterModell.holeDbServeradresse());
        einstellungen.schreibeDbDatenbankname(fensterModell.holeDbDatenbankname());
        einstellungen.schreibeDbBenutzername(fensterModell.holeDbBenutzername());
        ...      
        LOG.info("Modell Einstellungen Pfad Quelle : " + einstellungen.holePfadQuelle());
        LOG.info("Modell Einstellungen Pfad Ziel : " + einstellungen.holePfadZiel());
        ...      
        LOG.info("Modell Einstellungen DB Serveradresse : " + einstellungen.holeDbServeradresse());
        LOG.info("Modell Einstellungen DB Datenbankname : " + einstellungen.holeDbDatenbankname());
        LOG.info("Modell Einstellungen DB Benutzername : " + einstellungen.holeDbBenutzername());
        ...
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichernHD
   
     public String holePfadEinstellungen() {
        return pfadEinstellungen.get();
    }
   
    public StringProperty pfadEinstellungenProperty() {
        return pfadEinstellungen;
    }
   
    public void schreibePfadEinstellungen(String pfadEinstellungen) {
        this.pfadEinstellungen.set(pfadEinstellungen);
    }
    ...
Auszug aus Einstellungen
Java:
// Einstellungen
public class Einstellungen implements Serializable {

    private String pfadEinstellungen;
    private String pfadQuelle;
    ...
    private String dbServeradresse;
    private String dbDatenbankname;
    private String dbBenutzername;
    private String dbPasswort;
    ...
    // Methode für das einsetzen von Standard Werten
    public static Einstellungen standardWerte() {
        String heimVerzeichnis = System.getProperty("user.home");
        Einstellungen standardWerte = new Einstellungen();
        standardWerte.schreibePfadQuelle(heimVerzeichnis+"\\Dateien\\01 Quelle");
        standardWerte.schreibePfadZiel(heimVerzeichnis+"\\Dateien\\02 Ziel");  
        ...
        return standardWerte;
    } // Methode für das einsetzen von Standard Werten
   
public String holePfadQuelle() {
        return pfadQuelle;
    }
   
    public Einstellungen schreibePfadQuelle(String quellVerzeichnis) {
        this.pfadQuelle = quellVerzeichnis;
        return this;
    }
...
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei = null;
        try {
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD
...
} // Einstellungen

Ich habe versucht die Aufgabe zu lösen wie looparda es beschrieben hat.
1. Eine Klasse Einstellungen erstellt mit allen benötigten Parameter im Standard Java
2. Ein Klasse EinstellungenFensterModell die im alle Parameter enthält wie in 1. nur das die Werte Getter und Setter in Properties definiert sind
3. Eine Klasse EinstellungenFensterKontroller der zum Binden des Fenster Modelles und dem Fenster dient.

Im FensterKontroller Konstruktor wird ein EinstellungenObjekt erstellt, welches die Standard Werte aus dem Konstruktor holt.
Weiter wird im Konstruktor ein FensterModell Objekt erstellt, welches auf dem EinstellungenObjekt Basiert.

In der Initialisierung des FensterKontrollers wird das Fenster mit dem FensterModell gebunden.
Wenn im Fenster auf Speichern geklickt wird, dann wird im FensterKontroller die Methode "EinstellungenSpeichern" ausgeführt, worin eine Methode ausgeführt wird die im FensterModell Klasse definiert wurde. Als Parameter werden die im FensterKontroller erzeugten Einstellungen Objekt und fensterModell Objekt übergeben.
In der Methode der FensterModell Klasse werden dann die Daten vom FensterModell in das Einstellungen Objekt übertragen, wobei dann noch die Methode "schreibeEinstellungenHD" ausgeführt wird mit dem Paramter des einstellungen objektes.
Die Methode ist definiert in der Klasse Einstellungen und schreibt dort das Objekt Einstellungen auf die Festplatte.

Aktuell funktioniert das noch nicht wirklich so, da erst mal das Problem des TextFields gelöst werden muss um keine leeren Datenobjekte zu haben.

Ich hoffe, das ich das ganze so richtig verstanden habe?
 

looparda

Top Contributor
kann ich keine Eingaben mehr im Fenster machen wenn das Programm läuft?
Das Binding ist unidirektional. Benutze .bindBidirectional, um Änderungen in beide Richtungen zu ermöglichen - sorry, das hatte ich falsch in meinem Snippet vorgegeben.

EinstellungenFM.einstellungenSpeichernHD(fensterModell, einstellungen);
Du kannst im ViewModell ganz normal eine Methode anbieten, denn im ViewModel hast du die benötigten Objekte einstellungen und den Zustand des ViewModell selbst - also keine Notwenigkeit static zu nutzen. viewmodel.einstellungenSpeichernHD();

Die Implementierung von einstellungenSpeichernHD wiederum gehört dort nicht rein sondern in das Model oder einen externen Service. Lies dazu erneut:
Der View ruft Methoden aus dem ViewModel auf, wenn Aktionen auftreten wie Button gedrückt. Das ViewModel entscheidet was passieren soll, wenn die Methode aufgerufen wurde. Beispielsweise führst du im ViewModel eine Validierung durch, setzt eine Fehlerproperty, die dafür sorgt, dass im View der Fehler sichtbar wird. [Dann mappst du das ViewModel ins Model] und rufst letztlich eine Methode aus dem Modell [oder einen Service zum Speichern] auf.

Ich habe die Implementierungen jetzt nicht genau gelesen sondern bin eher über den Datenfluss und die Struktur geflogen. Ansonsten sieht das jetzt schon viel besser aus!
 

MiMa

Top Contributor
Vielen Dank, das hat jetzt funktioniert.
Bei den Labels ging es Unidirektional weil ich direkt in das Modell geschrieben habe, was sich im Label aktualisiert hat.
Bei den Textfeldern hat Bidirektional gut funktioniert.
Auch die Datei "Einstellungen.ser" wurde ohne Fehlermeldungen in einem geänderten Pfad geschrieben.

Ich muss mich jetzt noch mal für die tolle Unterstützung bedanken, sowas als Beispiel habe ich nirgendwo finden können und bin froh das es jetzt funktioniert. Auch gelernt habe ich super viel davon und sehe das es nicht so ganz easy ist.

Morgen mache ich mich ran die Datei zu lesen.
Jetzt habe ich einen funktionierenden Einstellungen Dialog in dem Standardverzeichnisse enthalten sind, die ich sogar ändern kann und Eingaben durch Textfelder. Die Daten werden jetzt zwar auf Festplatte gespeichert, aber wie mache ich dann all die Daten im gesamten Programm verfügbar?
Gebe ich das Einstellungen Objekt jetzt Klasse für Klasse weiter?
Lade ich es von Festplatte wenn ich Daten davon Benötige?
 

looparda

Top Contributor
Die Daten werden jetzt zwar auf Festplatte gespeichert, aber wie mache ich dann all die Daten im gesamten Programm verfügbar?
Eine Möglichkeit ist es die Einstellungen bei Programmstart einzulesen und sicherzustellen, dass es im gesamten Verlauf nur ein einziges Objekt davon gibt (Umsetzung mit Singleton-Pattern), welches global überall zugreifbar ist. Das erzeugt jedoch eine hohe Kopplung zwischen der Einstellungen-Klasse und den Klassen, wo sie genutzt wird. Es verhindert gut testbaren Code zu schreiben. Ich bin mir unsicher, ob ich dir wirklich zum Singleton raten sollte.
So lange du an keine Grenzen stößt würde ich es verwenden. Spätestens wenn du dich ernsthaft mit Testen auseinandersetzt fällt es dir auf die Füße und du wirst nach Alternativen suchen.

Eine Alternative dazu wäre erstmal ein Interface für die Einstellungen einzuführen, um die Kopplung zu lösen (Dependency Inversion) und die Einstellungen (von einem Dependency Injection Framework erzeugen lassen oder selbst erzeugen) und in die abhängigen Klassen zu injecten (Dependency Injection).
Weiterhin würde man dann versuchen die Einstellungen in verschiedene Sichten aufzusplitten (z.B. Pfade, Datenbankeinstellungen, Sicherheitseinstellungen) (Interface segregation principle), um die Kopplung weiter zu verringern.
Hier wird es erst richtig spannend und da lerne selbst noch ständig dazu. Mich würden auch andere Alternative oder Meinungen zum Singleton interessieren.
 

MiMa

Top Contributor
Aktuell bin ich jetzt dabei die Programmeinstellungen zu laden.
Ich habe im Einstellungen Fenster einen Lade Button platziert der beim Drücken die Einstellungen von der Festplatte laden soll.
Da beim Aufruf des Einstellungen Fenster alles Standardwerte geladen werden kann ich beim drücken vom Lade Button sehen ob die Funktion korrekt arbeitet, denn dann müssten sich die Pfade ändern.
Der Lade Button wird später entfernt da ich dann beim Programmstart die Einstellungen automatisch von Festplatte laden möchte, wenn die Einstellungen Datei existiert.

Die aktuelle Fehlermeldung beim Laden
Code:
INFO  einstellungen.Einstellungen - Es trat ein Fehler beim laden der Einstellungen auf
java.lang.ClassCastException: einstellungen.Einstellungen cannot be cast to einstellungen.EinstellungenFM
    at einstellungen.Einstellungen.ladeEinstellungenHD(Einstellungen.java:207) ~[DMS.jar:1.0]
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:93) ~[DMS.jar:1.0]
    at einstellungen.EinstellungenFK.einstellungenLaden(EinstellungenFK.java:176) ~[DMS.jar:1.0]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76) ~[javafx.base:?]
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275) ~[javafx.base:?]
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670) ~[javafx.fxml:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Node.fireEvent(Node.java:8865) ~[javafx.graphics:?]
    at javafx.scene.control.Button.fire(Button.java:200) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274) ~[javafx.controls:?]
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3876) ~[javafx.graphics:?]
    at javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) ~[javafx.graphics:?]
    at javafx.scene.Scene.processMouseEvent(Scene.java:1874) ~[javafx.graphics:?]
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) ~[javafx.graphics:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.notifyMouse(View.java:942) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175) ~[javafx.graphics:?]
    at java.lang.Thread.run(Thread.java:844) [?:?]
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode

Methode im Einstellungen Fenster Kontroller
Java:
 @FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        fensterModell.einstellungenLadenHD(einstellungen);
    } // einstellungenLaden

Methode im Einstellungen Fenster Modell
Java:
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = null;
        Einstellungen.ladeEinstellungenHD(einstellungen);
        return fensterModell;
    } // einstellungenLadenHD

Methode in der Klasse Einstellungen
Java:
public static EinstellungenFM ladeEinstellungenHD(Einstellungen einstellungen) {
         InputStream eingabeStreamDatei = null;
         EinstellungenFM fensterModell = null;
         try {
             LOG.info("Einstellungen Pfad : " + einstellungen.holePfadEinstellungen());
             eingabeStreamDatei = new FileInputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
             ObjectInputStream eingabeStreamObjekt = new ObjectInputStream(eingabeStreamDatei);
             fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();
             LOG.info("Die Einstellungen wurden in das Modell geladen");
             LOG.info("Quellverzeichnis im Modell : " + fensterModell.holePfadQuelle());
             LOG.info("Verzeichnis Einstellungen im Modell : " + fensterModell.holePfadEinstellungen());
             return fensterModell;
         } catch (Exception e) {
             LOG.info("Es trat ein Fehler beim laden der Einstellungen auf" , e);
         }
     } // ladeEinstellungenHD

Es wurde schon mal angesprochen, dass ich meine Variablen bezüglich der Modelle nicht wirklich gut benannt habe, aber das wird sich ändern wenn ich das Prinzip und und die Funktionsweise besser verstanden habe. :)
 
K

kneitzel

Gast
Die Fehlermeldung sagt es doch schon:
java.lang.ClassCastException: einstellungen.Einstellungen cannot be cast to einstellungen.EinstellungenFM

Du versuchst eine Instanz vom Typ Einstellungen in einer Variablen vom Typ EinstellungenFM zu speichern und der Cast scheint nicht möglich.

Deine Klassen Einstellungen und EinstellungenFM kenne ich aber nicht ebenso weiss ich nicht, was du in deiner Datei wie gespeichert hast. Aber die Zeile mit der Fehlermeldung ("at einstellungen.Einstellungen.ladeEinstellungenHD(Einstellungen.java:207)")
scheint die folgende zu sein:
fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();
 

mrBrown

Super-Moderator
Mitarbeiter
Es wurde schon mal angesprochen, dass ich meine Variablen bezüglich der Modelle nicht wirklich gut benannt habe, aber das wird sich ändern wenn ich das Prinzip und und die Funktionsweise besser verstanden habe.
Andersrum ist’s einfacher :) Alles so gut wie möglich zu benennen macht das Verstehen deutlich einfacher...
 

MiMa

Top Contributor
Die Schreib Methode die zuvor Diskutiert wurde arbeitet ohne Fehler und schreibt ein Objekt auf die Festplatte.
Demnach müsste es doch ausreichen, wenn ich in der Lese Methode das Objekt erstelle und es dann mit
Code:
fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();
wieder ein zu lesen.
 

mrBrown

Super-Moderator
Mitarbeiter
Offensichtlich schreibst du ein Einstellungen-Objekt und versuchst es als EinstellungenFM zu lesen, das klappt natürlich nicht ;)

Halte EinstellungenFM gänzlich aus deinem Model raus, dann gibt es solche Fehler erst gar nicht ;)
 

MiMa

Top Contributor
Ach ja, das Stimmt.
Du hast recht, das schreiben der Objekt-Datei war ein Einstellungen Objekt.
Ich erinnere mich das die Methode writeObjekt nicht mit Properties umgehen kann.
Ich komme damit immer noch etwas durcheinander, mir fehlt halt die Übung.
Ich schreibe die Methoden neu und hoffe das es diesmal klappt :)
Danke
 
Zuletzt bearbeitet:

MiMa

Top Contributor
Die Laden Methode funktioniert soweit.
Code:
INFO  start.Start - Das Programm wird initialisiert init()-Methode
INFO  start.Start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  start.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  einstellungen.EinstellungenFK - Einstellungen werden initialisiert
INFO  einstellungen.EinstellungenFK - einstellungenLaden wurde aufgerufen
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\JApp\Einstellungen.ser
INFO  einstellungen.Einstellungen - Die Einstellungen wurden in das Modell geladen
INFO  einstellungen.Einstellungen - Quellverzeichnis im Modell : B:\01 Quelle
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode

Nachdem ich dann im Einstellungen Fenster Modell die werte des Einstellungen Modell in das Fenster Modell übertragen wollte, trat dann wieder ein Fehler in der Konsole auf. Im Log waren keine Fehler ausgegeben worden.

Methode Laden aus der Klasse Einstellungen
Java:
 public static Einstellungen leseEinstellungenHD(Einstellungen einstellungen) {
         InputStream eingabeStreamDatei = null;
         Einstellungen hdEinstellungen = new Einstellungen();
         String dateiEinstellungen = einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser";
         LOG.info("Pfad zum  laden von Einstellungen : " + dateiEinstellungen);
         try {
             eingabeStreamDatei = new FileInputStream(dateiEinstellungen);
             ObjectInputStream eingabeStreamObjekt = new ObjectInputStream(eingabeStreamDatei);
             hdEinstellungen = (Einstellungen) eingabeStreamObjekt.readObject();
           
             LOG.info("Quellverzeichnis von HD geladen : " + hdEinstellungen.holePfadQuelle());
         } catch (Exception e) {
             LOG.info("Es trat ein Fehler beim laden der Einstellungen auf" , e);
         }
         return hdEinstellungen;
     } // ladeEinstellungenHD

Methode aus Einstellungen Fenster Modell
Java:
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);
       
        // Werte in das Fenster-Modell übetragen
        fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
        fensterModell.schreibePfadZiel(einstellungenHD.holePfadZiel());
        fensterModell.schreibePfadOCR(einstellungenHD.holePfadOCR());
        fensterModell.schreibePfadOhneDokumentArt(einstellungenHD.holePfadOhneDokumentArt());
        fensterModell.schreibePfadLoeschen(einstellungenHD.holePfadLoeschen());
        fensterModell.schreibePfadGeschuetzt(einstellungenHD.holePfadGeschuetzt());
        fensterModell.schreibePfadBild(einstellungenHD.holePfadBild());
        fensterModell.schreibePfadEinstellungen(einstellungenHD.holePfadEinstellungen());
       
        fensterModell.schreibeDbServeradresse(einstellungenHD.holeDbServeradresse());
        fensterModell.schreibeDbDatenbankname(einstellungenHD.holeDbDatenbankname());
        fensterModell.schreibeDbBenutzername(einstellungenHD.holeDbBenutzername());
        fensterModell.schreibeDnbToken(einstellungenHD.holeDnbToken());
       
        return fensterModell;
    } // einstellungenLadenHD

Fehler in der Konsole
Code:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8865)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
    at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 47 more
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:96)
    at einstellungen.EinstellungenFK.einstellungenLaden(EinstellungenFK.java:176)
    ... 58 more
 
Zuletzt bearbeitet:

MiMa

Top Contributor
Ich wollte den obigen Post noch korrigieren aber die Zeit war abgelaufen.
Ich habe den Fehler habe ich selbst gefunden. So funktioniert es.
Java:
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = new EinstellungenFM(einstellungen);
//        EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);

        // Werte in das Fenster-Modell übetragen
        fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
        fensterModell.schreibePfadZiel(einstellungenHD.holePfadZiel());
        fensterModell.schreibePfadOCR(einstellungenHD.holePfadOCR());
        fensterModell.schreibePfadOhneDokumentArt(einstellungenHD.holePfadOhneDokumentArt());
        fensterModell.schreibePfadLoeschen(einstellungenHD.holePfadLoeschen());
        fensterModell.schreibePfadGeschuetzt(einstellungenHD.holePfadGeschuetzt());
        fensterModell.schreibePfadBild(einstellungenHD.holePfadBild());
        fensterModell.schreibePfadEinstellungen(einstellungenHD.holePfadEinstellungen());

        fensterModell.schreibeDbServeradresse(einstellungenHD.holeDbServeradresse());
        fensterModell.schreibeDbDatenbankname(einstellungenHD.holeDbDatenbankname());
        fensterModell.schreibeDbBenutzername(einstellungenHD.holeDbBenutzername());
        fensterModell.schreibeDnbToken(einstellungenHD.holeDnbToken());

        return fensterModell;
    } // einstellungenLadenHD

Anschliessend die neuen Daten dann im Fenster aktualisiert anzeigen lassen.
Methode im Fenster Kontroller
Java:
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
//        fensterModell.einstellungenLadenHD(einstellungen);
        EinstellungenFM fensterModellHD = new EinstellungenFM(einstellungen);
        fensterModellHD = fensterModell.einstellungenLadenHD(einstellungen);
        fensterModell.schreibePfadQuelle(fensterModellHD.holePfadQuelle());
} // einstellungenLaden

Ich hatte versucht einfach das fenstermodell zu überschreiben fensterModellHD = fensterModell.einstellungenLadenHD(einstellungen);
Das hatte soweit nicht funktioniert und daher wollte ich mal nachfragen ob das überhaupt möglich ist?

Als ich gerade so dabei bin die neuen Daten des FensterModelles zurück zu geben ist mir aufgefallen, das ich doch auch das Modell der Einstellungen zurückgeben sollte, damit die am entsprechenden Programmpunkt ebenfalls aktuell sind??
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Code:
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:96)

==> Was ist die Zeile 96 der Klasse EinstellungenFM (In einstellungenLadenHD Methode)?
 

MiMa

Top Contributor
In der Zeile 96 ist nichts.
Der null Pointer Exception kam weil ich fensterModell als null deklariert hatte und es dann nicht als Instanz erzeugt hatte.
Ich habe es oben Kommentiert wie es vorher war. Darüber habe ich die Instanz erzeugt, danach gab es keine Fehlermeldungen mehr.

Danke
 
K

kneitzel

Gast
In der Zeile 96 ist nichts.
Der null Pointer Exception kam weil ich fensterModell als null deklariert hatte und es dann nicht als Instanz erzeugt hatte.
Ich habe es oben Kommentiert wie es vorher war. Darüber habe ich die Instanz erzeugt, danach gab es keine Fehlermeldungen mehr.

Danke
Nach deiner Änderung war da nichts mehr. Als die Exception geworfen wurde, muss da etwas gewesen sein, dass eben die NPE ausgelöst hat. Nach Deiner Beschreibung dann wohl ein Zugriff auf eben dieses fensterModell, welches null war (So dass Deine Änderung war).

Also als Hinweis für die eigene Fehlersuche:
Bei dem StackTrace immer schauen, was die eigentliche Ursache ist. Wenn die Exception abgefangen und dann eine andere Exception geworfen wird, dann kommen manchmal Exceptions, die so erst einmal einem nicht weiter helfen. Daher immer schön bis zum Ende gehen und dann findet man schnell die Ursache.
 

mrBrown

Super-Moderator
Mitarbeiter
Nach deiner Änderung war da nichts mehr. Als die Exception geworfen wurde, muss da etwas gewesen sein, dass eben die NPE ausgelöst hat. Nach Deiner Beschreibung dann wohl ein Zugriff auf eben dieses fensterModell, welches null war (So dass Deine Änderung war).
Der Code ist auf der vorherigen Seite zu finden :)

der relevante Teil ist:

Java:
-->     EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);
      
        // Werte in das Fenster-Modell übetragen
-->     fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
 

MiMa

Top Contributor
Da aktuell das Grundgerüst nun steht und soweit auch alles soweit funktioniert überlege ich gerade an folgende Optionen um mit den Programmeinstellungen ab zu schließen.

1. Einen Standardpfad für Programmeinstellungen der sich nicht mehr ändert?
Einen Standardpfad für die Einstellungen im Benutzerverzeichnis C:\Benutzerordner\Benutzername\Applikation\Einstellungen.ser
Wenn das Programm keine Einstellungen hat werden alle Standardpfade auf das Benutzerverzeichnis gelegt.
Wenn man das Verzeichnis der Einstellungen verlegen sollte wie zB. B:\Einstellungen\Einstellungen.ser weiß das Programm nicht wo es die Einstellungen laden soll. Daher dache ich mir das es das einfachste wäre die Einstellungendatei immer im Benutzerverzeichnisse zu lagern und die anderen Verzeichnisse sind dann ja in der Datei definiert.

2. Laden der Einstellungen vor dem eigentlichen Start?
Ich habe mir Überlegt die Einstellungen in der init() zu laden, damit diese vor dem Programmstart alle verfügbar sind.
Und immer weiter reichen um die Inhalte verfügbar zu machen.

3. Keine Einstellungsdatei dann Fenster aufrufen?
Wenn keine Einstellungen Datei vorhanden sind, werden alle Pfade als Standard in das Benutzerverzeichnis gelegt. Sollte man es einfach so belassen oder das Fenster mit den Einstellungen öffnen um die Pfade zu definieren?
Das Einstellungen Fenster kann auch im Menü angewählt werden um die Pfade zu ändern.
 

MiMa

Top Contributor
Hi,
aktuell habe ich ein Problem das ich schon den ganzen Tag versuche zu lösen.
Das Laden der Einstellungen funktioniert für alle Werte.
Das Speichern funktioniert auch für alle Werte bis auf einen (Pfad Einstellungen).
Wenn ich diesen einen Wert eintrage und speichern drücke, dann wird die Datei überhaupt nicht geschrieben?
Fehlermeldungen erhalte ich keine, es werden auch keine Exceptiones geworfen. Es wird halt die Datei einfach nicht geschrieben.
Da ich den ganzen Tag damit verbracht habe den Kontroller und das Modell zu kontrollieren könnte die Fehlerquelle nur die Speichern Methode in Frage kommen, aber ich habe nichts gefunden.
Wäre super wenn sich das mal jemand anderes ansehen könnte
Der Problemwert ist: schreibePfadEinstellungen
Wenn ich aber die Standardwerte speichere, dann funktioniert das speichern für alle Werte problemlos?
Danke
Mi

Java:
@FXML
    private void einstellungenSchreiben(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        this.einstellungen.schreibePfadQuelle(this.einstellungenModell.getPfadQuelle());
        LOG.info("Schreibe Einstellung Pfad Quelle           : " + this.einstellungen.holePfadQuelle());
        this.einstellungen.schreibePfadZiel(einstellungenModell.getPfadZiel());
        LOG.info("Schreibe Einstellung Pfad Ziel             : " + this.einstellungen.holePfadZiel());
        this.einstellungen.schreibePfadOCR(einstellungenModell.getPfadOCR());
         LOG.info("Schreibe Einstellung Pfad OCR              : " + this.einstellungen.holePfadOCR());
        this.einstellungen.schreibePfadBild(einstellungenModell.getPfadBild());
        LOG.info("Schreibe Einstellung Pfad Bild             : " + this.einstellungen.holePfadBild());
        this.einstellungen.schreibePfadOhneDokumentArt(einstellungenModell.getPfadOhneDokumentArt());
        LOG.info("Schreibe Einstellung Pfad Ohne Dokumentart : " + this.einstellungen.holePfadOhneDokumentArt());
        this.einstellungen.schreibePfadLoeschen(einstellungenModell.getPfadLoeschen());
        LOG.info("Schreibe Einstellung Pfad Loeschen         : " + this.einstellungen.holePfadLoeschen());
        this.einstellungen.schreibePfadGeschuetzt(einstellungenModell.getPfadGeschuetzt());
        LOG.info("Schreibe Einstellung Pfad Geschuetzt       : " + this.einstellungen.holePfadGeschuetzt());
        this.einstellungen.schreibePfadEinstellungen(einstellungenModell.getPfadEinstellungen());
        LOG.info("Schreibe Einstellung Einstellungen         : " + this.einstellungen.holePfadEinstellungen());
     
        this.einstellungen.schreibeDbServeradresse(einstellungenModell.getDbServeradresse());
         LOG.info("Schreibe Einstellung Serveradresse         : " + this.einstellungen.holeDbServeradresse());
        this.einstellungen.schreibeDbDatenbankname(einstellungenModell.getDbDatenbankname());
        LOG.info("Schreibe Einstellung Datenbankname         : " + this.einstellungen.holeDbDatenbankname());
        this.einstellungen.schreibeDbBenutzername(einstellungenModell.getDbBenutzername());
        LOG.info("Schreibe Einstellung Benutzername          : " + this.einstellungen.holeDbBenutzername());
        this.einstellungen.schreibeDbPasswort(einstellungenModell.getDbPasswort());
        LOG.info("Schreibe Einstellung Passwort              : " + this.einstellungen.holeDbPasswort());
        this.einstellungen.schreibeDnbToken(einstellungenModell.getDnbToken());
        LOG.info("Schreibe Einstellung DNB Token             : " + this.einstellungen.holeDnbToken());
             
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSchreiben

Java:
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei;
        // Prüfen ob es das Einstellungen-Verzeichnis existiert
        File einstellungenVerzeichnis = new File(einstellungen.holePfadEinstellungen());
         if (einstellungenVerzeichnis.mkdirs()) {
             LOG.info("Das Einstellungen-Verzeichnis ist nicht vorhanden und wurde erstellt");
         }
       
        try {
            LOG.info("Einstellungen Pfad : " + einstellungen.holePfadEinstellungen());
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (IOException e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Bist du Dir sicher, dass Du info Logmeldungen mitbekommst? Ich vermute, dass eine Exception geworfen wird und dass Du die nur nicht mitbekommst. Sprich: Siehst Du die Logmeldung von wegen "Einstellungen Pfad" und so? (Und Fehler sollten auch nicht auf Level INFO sondern auf Level ERROR im Log geschrieben werden!)
 

MiMa

Top Contributor
Danke für den Hinweis der Logging Anwendung bei den Fehlermeldungen. Ich bin so darauf konzentriert, das MVC hin zu bekommen. Ich trau mich schon gar nicht zu sagen, das ich es schon viele Monate versuche das Konzept in eine funktionierende Version um zu setzen.
Habe das Projekt mal in Eclipse übertragen, für den Fall das Netbeans irgendetwas macht? Aber das gleichen Problem.
Der Wert Einstellungen wurde geändert. Beim speichern wird der richtige Wert übernommen, aber die Datei wird nicht gespeichert, obwohl das Log es als erfolgreich ausgibt?

Das Logfile sieht so aus
Code:
INFO  start.Start - Das Programm wird initialisiert init()-Methode
INFO  start.Start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  start.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  einstellungen.EinstellungenFK - Einstellungen werden initialisiert
INFO  einstellungen.EinstellungenFK - einstellungenLaden wurde aufgerufen
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser
INFO  einstellungen.Einstellungen - Die Einstellungen wurden von Festplatte geladen :
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Quelle : B:\01 Quelle
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Ziel : B:\02 Ziel
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad OCR : B:\03 OCR
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Bild : B:\04 Bild
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad ohne Dokumnentart : B:\05 ohne Dokumentart
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Loeschen : B:\06 Loeschen
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Geschuetzt : B:\07 geschuetzt
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Einstellungen : C:\Users\Michael\DMS\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Lade Einstellung Serveradresse : 192.168.178.10
INFO  einstellungen.EinstellungenFK - Lade Einstellung Datenbankname  : DMS
INFO  einstellungen.EinstellungenFK - Lade Einstellung Benutzername : michael
INFO  einstellungen.EinstellungenFK - Lade Einstellung Passwort : PW123
INFO  einstellungen.EinstellungenFK - Lade Einstellung DNB Token : DNB 123
INFO  einstellungen.EinstellungenFK - sucheEinstellungenVerzeichnis wurde aufgerufen
INFO  einstellungen.EinstellungenFK - Selektiertes Einstellungen Verzeichnis : B:\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Methode einstellungenSpeichern wurde aufgerufen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Quelle           : B:\01 Quelle
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Ziel             : B:\02 Ziel
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad OCR              : B:\03 OCR
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Bild             : B:\04 Bild
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Ohne Dokumentart : B:\05 ohne Dokumentart
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Loeschen         : B:\06 Loeschen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Geschuetzt       : B:\07 geschuetzt
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Einstellungen         : B:\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Serveradresse         : 192.168.178.10
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Datenbankname         : DMS
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Benutzername          : michael
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Passwort              : PW123
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung DNB Token             : DNB 123
INFO  einstellungen.Einstellungen - Einstellungen Pfad : B:\00 Einstellungen
INFO  einstellungen.Einstellungen - Einstellungen wurden auf Festplatte gespeichert
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Ok, meine erste Vermutung ist damit hinfällig. Du bekommst ja alle info Meldungen angezeigt und so wie es aussieht schreibt er die Datei auch.

Kann es daran liegen, dass Du an der falschen Stelle schaust?

Beim Laden sehe ich:
Code:
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Einstellungen : C:\Users\Michael\DMS\00 Einstellungen

Aber wenn es zum Speichern geht, dann ist der Pfad angegeben als:
Code:
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Einstellungen         : B:\00 Einstellungen

Das ist der Wert von:
LOG.info("Schreibe Einstellung Einstellungen : " + this.einstellungen.holePfadEinstellungen());

Und das ist dann ggf. der Wert, der auch hier verwendet wird:
ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");

Somit wäre der Pfad, der geschrieben wird: B:\00 Einstellungen\Einstellungen.ser
Da schaust Du auch? Oder schaust Du aus dem Pfad, der weiter oben im Log zu finden ist:
C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Juelin Für Java-Spezialisten AWT, Swing, JavaFX & SWT 4
H JTabel - RowFilter Daten für Berechnung filtern AWT, Swing, JavaFX & SWT 6
I JavaFX JavaFx-Anwendung für die Erstellung einer Windows-Anwendung? AWT, Swing, JavaFX & SWT 6
M Eigene Java Klasse für allgemeine Grafikelemente AWT, Swing, JavaFX & SWT 8
M Vokabelprogram - Schleife für Liste soll schrittweise durchlaufen werden AWT, Swing, JavaFX & SWT 3
tommybalbor JavaFx Anwendung klappt nicht für macOs Nutzern, wenn ich zwei dependecies bei maven hinzufüge AWT, Swing, JavaFX & SWT 6
I Libraries für AWT für andere Grafik-Frameworks tauglich machen AWT, Swing, JavaFX & SWT 6
R auto. Importanweisungen für javafx funktioniert in Eclipse nicht mehr AWT, Swing, JavaFX & SWT 4
komplettlost Vollbildmodus für MacOs Nutzer geht nicht AWT, Swing, JavaFX & SWT 13
D JavaFX Schadensberechnung für Kartenspiel AWT, Swing, JavaFX & SWT 1
P JTable Listener für die Änderung einzelner Zellen oder Rows AWT, Swing, JavaFX & SWT 2
Jose05 JavaFX: eigene FXML-Datei für einen Button AWT, Swing, JavaFX & SWT 3
L actionListener für Button AWT, Swing, JavaFX & SWT 97
izoards Textfeld für Zeit AWT, Swing, JavaFX & SWT 4
CptK Wie funktioniert contains() für Path2D.Double AWT, Swing, JavaFX & SWT 10
T Getter und Setter für eine Stage AWT, Swing, JavaFX & SWT 6
P Swing Programm hängt sich bei Buttondruck auf? (GUI für "Chatbot" erstellen) AWT, Swing, JavaFX & SWT 15
T Button für GUI programmieren AWT, Swing, JavaFX & SWT 1
Z Switch Case für Buttons AWT, Swing, JavaFX & SWT 8
M Hough-Transformation für Kreise und andere Formen AWT, Swing, JavaFX & SWT 3
kodela HTML-tags für JLabel AWT, Swing, JavaFX & SWT 9
E Keystroke für Ausschneiden läßt sich nicht ändern AWT, Swing, JavaFX & SWT 2
M Swing Cell Renderer für Zeilenumbruch in JTable AWT, Swing, JavaFX & SWT 0
MiMa Package Struktur für GUI Programmierung AWT, Swing, JavaFX & SWT 26
N JavaFX 1 Listener für mehrere ChoiceBoxen AWT, Swing, JavaFX & SWT 3
B eclipse für JavaFx setuppen AWT, Swing, JavaFX & SWT 4
K Swing Struktur für TreeTable rekursiv aufbauen AWT, Swing, JavaFX & SWT 17
A Swing JTextField an Button übergeben für Popup-Fenster funktioniert nicht AWT, Swing, JavaFX & SWT 3
H Ein Patten für das Gluon Mobile Framework AWT, Swing, JavaFX & SWT 7
J Gibt es einen Grund für 16x16 anstatt z.B. 15x15 Tiles ? AWT, Swing, JavaFX & SWT 10
F JFormattedTextField für kg und Währung AWT, Swing, JavaFX & SWT 6
V Swing für jedes Kästchen eine eigene Farbe AWT, Swing, JavaFX & SWT 2
F Wie bekomme ich den Wert der ComboBox in eine Variable gespeichert welche ich für meinen ActionListener nutzen kann? AWT, Swing, JavaFX & SWT 3
Soloeco JavaFX Dreifachklick für MenuButton erforderlich AWT, Swing, JavaFX & SWT 2
L JavaFX Lösungsvorschläge für dieses coole Control AWT, Swing, JavaFX & SWT 8
looparda Suche Lib für Visualisierung von Graphen AWT, Swing, JavaFX & SWT 12
G LayoutManager Beliebige Anzahl von Panels für LayoutManager AWT, Swing, JavaFX & SWT 3
L Ein Actionlistener für ein Textfeld, anstatt viele Actionlistener für ein Textfeld AWT, Swing, JavaFX & SWT 7
S Swing Finde Grund für NullPointerExeption nicht. AWT, Swing, JavaFX & SWT 2
W JavaFX (j)Unittests für GUI AWT, Swing, JavaFX & SWT 0
B JavaFX JavaFX TableView PropertyValueFactory für Werte aus HashMap AWT, Swing, JavaFX & SWT 2
SchmidiMC Swing Vorschläge für ein Design AWT, Swing, JavaFX & SWT 5
Z JavaFX Pane für wechselnde Sub-Panes mit Auto-Resize AWT, Swing, JavaFX & SWT 2
S 2D-Grafik affine Transformation für Text-Shape AWT, Swing, JavaFX & SWT 0
G Swing Variable Elemente für GroupLayout AWT, Swing, JavaFX & SWT 18
kodela Accalerator für einige Menüoptionen funktioniert nicht mehr AWT, Swing, JavaFX & SWT 3
P Swing Empfehlungen für einfaches Computerspiel AWT, Swing, JavaFX & SWT 4
L DragDropped für jede Node AWT, Swing, JavaFX & SWT 0
temi JavaFX Lösungsansatz für Umsetzung gesucht AWT, Swing, JavaFX & SWT 4
J Swing JavaProgramm für Verschlüssen für eine Datei AWT, Swing, JavaFX & SWT 19
D DatePicker für Java Swing AWT, Swing, JavaFX & SWT 2
heinz ketchup While-Schleife in einem Service für GUI AWT, Swing, JavaFX & SWT 22
L JavaFX Renderer für JavaFX AWT, Swing, JavaFX & SWT 2
MiMa GUI Controller für Border Pane als MVC Modell AWT, Swing, JavaFX & SWT 1
L Font für Dashboard AWT, Swing, JavaFX & SWT 3
F Swing JColorChooser für die JToggleButtons AWT, Swing, JavaFX & SWT 5
S JavaFX Optimierung für verschiedene Auflösungen AWT, Swing, JavaFX & SWT 12
L JavaFX Animation für Panel wechsel AWT, Swing, JavaFX & SWT 3
T Swing Drag and Drop für JComponents AWT, Swing, JavaFX & SWT 1
Kloso Swing Pseudocode für Strafurzeichnung AWT, Swing, JavaFX & SWT 4
F Konstruktor für "Vier Gewinnt" AWT, Swing, JavaFX & SWT 10
L JavaFX PdfViewer für JavaFX Anwendung AWT, Swing, JavaFX & SWT 6
R Swing Welche LayoutManager sind die richtigen für mich? AWT, Swing, JavaFX & SWT 11
L Event Handling Gui für Taschenrechner AWT, Swing, JavaFX & SWT 27
C Slider für Zeitauswahl AWT, Swing, JavaFX & SWT 3
M Limit für JFrame-Vergrößerung AWT, Swing, JavaFX & SWT 8
GreenTeaYT Button funktioniert nicht für Ein-und Auszahlungen? AWT, Swing, JavaFX & SWT 8
K Liniendicke für Line Chart dynamisch ändern AWT, Swing, JavaFX & SWT 0
K JButton nicht sichtbar machen für User 2 AWT, Swing, JavaFX & SWT 4
OnDemand Gui Themes für FX AWT, Swing, JavaFX & SWT 4
G DefaultListModel für JList AWT, Swing, JavaFX & SWT 2
P JavaFX Kalender mit Kacheln für Ereignisse AWT, Swing, JavaFX & SWT 4
S ActionListener für alle Buttons AWT, Swing, JavaFX & SWT 26
J Swing Neuen Command für "show"? AWT, Swing, JavaFX & SWT 2
sandaime Swing Thread für CMD auslesen AWT, Swing, JavaFX & SWT 16
H Swing JFileChooser für nicht existierendes Unterverzeichnis AWT, Swing, JavaFX & SWT 3
R Java FX - Fxml - relative Größenangaben für Breite und Höhe einer TextArea AWT, Swing, JavaFX & SWT 8
D GUI-Bau für ein Auswertungs-Tool AWT, Swing, JavaFX & SWT 11
L Swing CellRenderer für einzelne Zellen? AWT, Swing, JavaFX & SWT 5
RalleYTN Swing Menü für Texteditor (Rechtsklick) AWT, Swing, JavaFX & SWT 4
H Applet Flappy Bird für Noobs AWT, Swing, JavaFX & SWT 4
X JavaFX Tooltips für XYChart-Knoten werden nicht angezeigt! AWT, Swing, JavaFX & SWT 3
Thallius Swing Aufgabe für einen der gerne Tüftelt. AWT, Swing, JavaFX & SWT 4
A hilfe für flowlayout AWT, Swing, JavaFX & SWT 6
B EventHandler für durch Tastenkombination erzeugte Zeichen AWT, Swing, JavaFX & SWT 3
T Hintergrund für GUI AWT, Swing, JavaFX & SWT 1
L JavaFX TableView mit Attributs der Modellreferenzerierung für die Spalte AWT, Swing, JavaFX & SWT 3
C JavaFX Character Comparator für TableColumn AWT, Swing, JavaFX & SWT 0
KrokoDiehl JavaFX Gleiche Controller-Instanz für inludiertes FXML AWT, Swing, JavaFX & SWT 1
V Tastatur KeyListener für mehrere Buttons AWT, Swing, JavaFX & SWT 1
S Pfad für Speichervorgang auswählen AWT, Swing, JavaFX & SWT 11
M If für viele TextFields AWT, Swing, JavaFX & SWT 7
stylegangsta Eigene Klasse für JButton aus dem JFrame abrufen AWT, Swing, JavaFX & SWT 29
R JMenuItems für Touchscreen auseinanderziehen AWT, Swing, JavaFX & SWT 3
L LookAndFeel Eigenes Design für die Applikation AWT, Swing, JavaFX & SWT 4
G Grafikformat für AWT-Applet? AWT, Swing, JavaFX & SWT 0
J Java -8 Action Listener für mehrere Buttons AWT, Swing, JavaFX & SWT 9
Z JavaFX TableView cellValueFactory für Arrays AWT, Swing, JavaFX & SWT 2
T LayoutManager Methode, um Bildschirm(fenster) für Aktualisierungen zu blockieren bzw. freizugeben gesucht AWT, Swing, JavaFX & SWT 2
A Gui für Vokabeltrainer (ActionListener) AWT, Swing, JavaFX & SWT 14

Ähnliche Java Themen

Neue Themen


Oben