MichiLee: Wie am besten Fehlermeldungen handlen

Hallo,
ich schreibe derzeit eine Java Applikation und frage mich, da überall, in allen Methoden und in allen Schichten (View, Model, Controller, Datenabstraktionslayer, Datenbankconnection usw.) Fehler passieren können.

Ich logge alles mit log4j, so dass ich naher, im Fehlerfall ich in der Log nachschauen kann.

Ich frage mich nun, wie ich aber bei einem Fehler bestimmte Fehlertexte auch an den User weitergeben kann. Ich kann den Fehler von der untersten (Connection) leider nicht bis in die View immer weiterreichen, da die Methoden immer verschiedene Typen zurückgeben.

Wie könnte ich das am besten machen?

Könnte ich eine Klasse mit statischen Klassenvariablen wie zum Beispiel errorCode und errorText (String wenn nur der letzte Fehler gespeichert oder Vector wenn mehrere) schreiben.

In allen Methoden kann ich nun, falls etwas eintrifft, was ich nich will (Ein Objekt Null oder eine Else- oder Catchzweig erreicht die statische ErrorKlasse aufrufen und einen errorCode setzen und das Programm weiter ablaufen kann und später, zum Beispiel im Controller oder woanders von der statischen ErrorKlasse den errorText und errorCode prüfen?

1. Wäre das zum Beispiel ein denkbarer einsatz von einer statischen Klasse, bzw. Klassenvariablen, die dann für eine Klasse, bzw. einen Benutzer gelten?

2. Eigentlich müsste ich für die statischen Klassenvariablen ja keine Setter und Getter Methoden schreiben, da ich ja über Klassenname.VARIABLE überall darauf zugreifen könnte. (Geht bei mir leider aber nicht, wenn ich von einer anderen Klasse die Klassenvariable aufrufen will)

Grüße

  1. Ich frage mich nun, wie ich aber bei einem Fehler bestimmte Fehlertexte auch an den User weitergeben kann. Ich kann den Fehler von der untersten (Connection) leider nicht bis in die View immer weiterreichen, da die Methoden immer verschiedene Typen zurückgeben.

    Dazu gibts Exceptions. Die werden da im Aufrufbaum abgefangen, wo du das ausdrücklich haben willst.

    Könnte ich eine Klasse mit statischen Klassenvariablen wie zum Beispiel errorCode und errorText (String wenn nur der letzte Fehler gespeichert oder Vector wenn mehrere) schreiben.

    Könntest du schon auch. Aber das ist doch umständlich zu handhaben.
    Exceptions sind genau dazu da, was du vor hast.

    1. Hi Encode,
      danke für die schnelle Antwort.

      Ich frage mich nun, wie ich aber bei einem Fehler bestimmte Fehlertexte auch an den User weitergeben kann. Ich kann den Fehler von der untersten (Connection) leider nicht bis in die View immer weiterreichen, da die Methoden immer verschiedene Typen zurückgeben.
      Dazu gibts Exceptions. Die werden da im Aufrufbaum abgefangen, wo du das ausdrücklich haben willst.

      Ja, an Exceptions habe ich auch gedacht. Dann müsste ich überall, wo etwas passieren kann try und catch blöcke machen, was ich eh gemacht habe. Ich könnte auch eine eigene Exceptionsklasse schreiben, was dann von Exception angeleitet ist. Auch kein Thema.

      Ich frage mich aber, wenn ich im Baum ganz unten bin, Beispiel:

      1. Der User klickt
      2. Der Controller wird aufgerufen
      3. Das UserModel wird aufgerufen
      4. Es wird eine Query erstellt
      5. Diese Query wird der Klasse für die Datenbankverbindung übergeben.

      Innerhalb der Reihenfolge werden verschiedene Objekttypen untereinander weitergegeben oder zurückgegeben.

      Nun frage ich mich, wenn ich in der Ebene 5 eine Exception auswerfe, dass der Datenbanktreiber nicht gefunden wurde, dann kann es die Ebene 4 abfangen, falls in Ebene 5 eine Exception passiert. Die Exceptionmeldung muss aber Standarisiert dann bis zum Controller zurück, der dann die Meldung dem User geben kann. (Ich schaue mir mal an, wie ich das mit den Exceptions noch geschickt einbauen könnte)

      Könnte ich eine Klasse mit statischen Klassenvariablen wie zum Beispiel errorCode und errorText (String wenn nur der letzte Fehler gespeichert oder Vector wenn mehrere) schreiben.
      Könntest du schon auch. Aber das ist doch umständlich zu handhaben.
      Exceptions sind genau dazu da, was du vor hast.

      Also prinzipiell wäre es mit statischen Klassenvariablen auch möglich?
      Wenn man eine Klassenvariable "public static int errorCode=null" hätte dann könnte ich doch von überall aus in der Applikation durch Klassenname.errorCode=1 ja einen neuen Wert zuweisen können, wo dann später jede Klasse diese Klassenvariable abrufen oder einen neuen errorCode überschreiben könnte?

      Dazu brauche ich ja keine sSetter und Getter, dennoch kann ich irgendwie von einer anderen Klasse nicht auf eine statische Klassenvariable zugreifen, Netbeans zeigt mit nach dem Punkt Klassenname. nicht die Klassenvariablen an.

      Grüße

      1. Nun frage ich mich, wenn ich in der Ebene 5 eine Exception auswerfe, dass der Datenbanktreiber nicht gefunden wurde, dann kann es die Ebene 4 abfangen, falls in Ebene 5 eine Exception passiert. Die Exceptionmeldung muss aber Standarisiert dann bis zum Controller zurück, der dann die Meldung dem User geben kann. (Ich schaue mir mal an, wie ich das mit den Exceptions noch geschickt einbauen könnte)

        Und was fragst du dich da? Was du schreibst ist wirklich so.
        Was meint du mit standartisiert? Es muss halt die Meldung ankommen, die steckt aber ja in der Exception. Viel standartisierter gehts doch kaum mehr?

        Wenn man eine Klassenvariable "public static int errorCode=null" hätte dann könnte ich doch von überall aus in der Applikation durch Klassenname.errorCode=1 ja einen neuen Wert zuweisen können, wo dann später jede Klasse diese Klassenvariable abrufenn

        Klar, so gehts auch. Wenn du das wirklich willst.

        oder einen neuen errorCode überschreiben könnte?

        Klar, du kannst dir einen gesetzten Code selbstverständlich auch unbesehen mit einem neuen überschreiben. Das ist sinnvoll, wenn man zuviel Zeit hat und lieber einen Folgefehler gemeldet haben will, statt der eigentlichen Ursache *g*

        Mit Netbeans kenn ich mich übrigens nicht aus.

        1. Und was fragst du dich da? Was du schreibst ist wirklich so.
          Was meint du mit standartisiert? Es muss halt die Meldung ankommen, die steckt aber ja in der Exception. Viel standartisierter gehts doch kaum mehr?

          Oki, ich versuche das nun umzusetzen gleich in der nächsten Stunde.

          Das war ja in etwa der Ablauf:
          1. Der User klickt
          2. Der Controller wird aufgerufen. "ControllerKlasse"
          3. Das UserModel wird aufgerufen "ModelKlasse". Dort wird eine QueryKlasse erstellt.
          4. Die "QueryKlasse" erstellt wiederum eine Query, wo ein Fehler passieren kann und nutzt die "DataSourceKlasse", im ihr die Query zu übergeben/auszuführen.
          5. Diese Query wird der Klasse für die Datenbankverbindung übergeben. "DatasourceKlasse"

          1. In dem Interface, wo Methoden für die DataSourceKlasse vorgeschrieben werden, habe ich die Methoden mit throws DatasourceException erweitert. Das heißt die DatasourceKlasse, die dieses Interface implementiert, müssen in den Methoden DatasourceException werfen, falls dort im Try-Block etwas passiert.

          2. Die Queryklasse kann dann auch Exceptions werfen, zum Beispiel eine QueryException und fängt selber praktisch von der "DatasourceKlasse" die DatasourceException ab.

          Ich teste jetzt einmal, ob ich bei der ModelKlasse die erwünschte Meldung erhalte. Dort kann ich ja QueryException anfangen, aber dort könnte es ja sein, dass ein Exception weiter drunter in der Datasource passiert ist.

          Ich guck mal, ob ich es verstanden habe, bzw. wie ich ds umsetze ;)

          Grüße

      2. Moin!

        1. Der User klickt
        2. Der Controller wird aufgerufen
        3. Das UserModel wird aufgerufen
        4. Es wird eine Query erstellt
        5. Diese Query wird der Klasse für die Datenbankverbindung übergeben.

        Innerhalb der Reihenfolge werden verschiedene Objekttypen untereinander weitergegeben oder zurückgegeben.

        Nun frage ich mich, wenn ich in der Ebene 5 eine Exception auswerfe, dass der Datenbanktreiber nicht gefunden wurde, dann kann es die Ebene 4 abfangen, falls in Ebene 5 eine Exception passiert. Die Exceptionmeldung muss aber Standarisiert dann bis zum Controller zurück, der dann die Meldung dem User geben kann. (Ich schaue mir mal an, wie ich das mit den Exceptions noch geschickt einbauen könnte)

        Nein, WENN die Exception aus der Datenbankklasse gefangen wird, darf sie nicht ganz bis zur obersten Ebene gelangen. Aber du darfst natürlich eine neue Exception werfen, die das Problem in einem anderen Licht betrachtet, und die dann vielleicht nach ganz oben wandert.

        Sollte sie wiederum gefangen werden, wäre eine neue, für diesen neuen Kontext geeignete Exception zu werfen.

        Könnte ich eine Klasse mit statischen Klassenvariablen wie zum Beispiel errorCode und errorText (String wenn nur der letzte Fehler gespeichert oder Vector wenn mehrere) schreiben.
        Könntest du schon auch. Aber das ist doch umständlich zu handhaben.
        Exceptions sind genau dazu da, was du vor hast.

        Also prinzipiell wäre es mit statischen Klassenvariablen auch möglich?

        Exceptions sind exakt das, was du brauchst. Vor allem, da sie genau das tun, was man im extremen Fehlerfall tun will: Die Ausführung an der aktuellen Stelle abbrechen und soweit zurückspringen, dass irgendein sich zuständig fühlender Catch-Codeteil die Verarbeitung übernimmt, andernfalls aber halt die Exception bis zum User vordringt.

        Wenn eine Datenbank nicht antwortet, und der User von der DB Daten wollte, kann man das Problem halt "nackt" lassen, oder in nette Worte verpacken. An der Tatsache, dass die DB nicht antwortet, ist aber nix zu ändern.

        Üblicherweise baut man sich zwecks feiner granulierter Catch-Routinen einen eigenen Baum von Exceptions, die alle von der Standard-Exception erben und ihrerseits das eventuell auftretende Problem feiner beschreiben - für den Fall, dass eine Unterscheidung des Problems grundsätzlich möglich und hilfreich ist.

        Sprich: Zuallererst brauchst du mal "DeineException" als Erben von der allgemeinen "Exception", damit du alle deine selbstgebauten Exceptions unterscheiden kannst von Exceptions anderer Komponenten.

        Und davon erben dann eventuell benötigte feiner aufgeteilte Exceptions, die dein Code in speziellen Situationen werfen könnte.

        Und dein Frontcontroller fängt dann als letzte Ebene alle Exceptions, die bei der Ausführung auftreten könnten, und kann dann unterscheiden zwischen deinen eigenen Exceptions (die vielleicht nicht so böse sind, und deshalb eine nette Fehlermeldung anzeigen, in deinem eigenen Design), und allen anderen Exceptions, die er deshalb mit Serverstatus 500, nur einer Standardmeldung sowie einem geschriebenen Logfile beantwortet.

        Wenn man eine Klassenvariable "public static int errorCode=null" hätte dann könnte ich doch von überall aus in der Applikation durch Klassenname.errorCode=1 ja einen neuen Wert zuweisen können, wo dann später jede Klasse diese Klassenvariable abrufen oder einen neuen errorCode überschreiben könnte?

        Warum erfindest du etwas neu, was es in vernünftiger Form schon gibt? :)

        - Sven Rautenberg

        1. Hi Sven,

          Nun frage ich mich, wenn ich in der Ebene 5 eine Exception auswerfe, dass der Datenbanktreiber nicht gefunden wurde, dann kann es die Ebene 4 abfangen, falls in Ebene 5 eine Exception passiert. Die Exceptionmeldung muss aber Standarisiert dann bis zum Controller zurück, der dann die Meldung dem User geben kann. (Ich schaue mir mal an, wie ich das mit den Exceptions noch geschickt einbauen könnte)

          vielen dank, hast das weiter unten auch sehr schön geschrieben, dass man nicht alles nach oben weiterreichen soll. wie du auch erwähnt hast, könnte man manche sachen (Wenn die datenbank nicht erreichbar ist) schon etwas weiter höher reichen, da man das problem ja meißt nicht durch abfangen und andere methoden beheben kann.

          Ich habe dem hotti schon geantwortet, dass ich eine eigene klasse von exceptions abgeleitet habe und die dann in der untersten klasse werfen kann.

          In der mittleren Klasse kann ich nun diese eigene Exception abfangen und evtl. sogar eigene neue Exceptions werfen. Das heißt, die oberste Klasse kann entweder von der mittleren Klasse die Exception prüfen, oder auch das von der untersten, was die mittlere praktisch weitergleitet hat nach oben :-) (okey, etwas umständlich beschrieben)

          Ich frage mich grad nur noch nur, ob ich Zugriff auf die Message habe, wenn ich weiter oben die Exception abfange oder weiterreiche, welches ich unten etwa so geworfen hatte:

          throw new myException("Mein Text");

          Grüße

          1. Moin!

            Ich frage mich grad nur noch nur, ob ich Zugriff auf die Message habe, wenn ich weiter oben die Exception abfange oder weiterreiche, welches ich unten etwa so geworfen hatte:

            throw new myException("Mein Text");

            Da, wo du die Exception fängst, steht dir deren volles Programm an Eigenschaften und Methoden zur Verfügung.

            Und wenn du eine neue Exception werfen willst, lässt sich deren Message auch aus Teilen der alten Message zusammensetzen, sofern dir das sinnvoll erscheint.

            Du kannst dir natürlich auch deine eigene Exception so erweitern, dass du in der Exception die relevanten Daten der gefangenen Exception nochmal kopiert weiterreichst. Allerdings wäre das ein unguter Verstoß gegen das Prinzip der "Separation of Concern". Deine Model-Klasse interessiert es wenig, dass deiner Querybuildingklasse der Datenbanktreiber verloren gegangen ist und der Query deshalb scheiterte. Ebenso interessiert es den Controller nicht, welches Datenbankproblem nun genau zum Scheitern des Model-Ladens geführt hat. Das Model-Object konnte nicht in den Speicher befördert werden - das ist das Entscheidende. Nicht alle Models kommen aus einer Datenbank, deshalb interessiert der Datenbankfehler nur die Entwickler zum Bugfixing.

            - Sven Rautenberg

            1. Hi,

              Du kannst dir natürlich auch deine eigene Exception so erweitern, dass du in der Exception die relevanten Daten der gefangenen Exception nochmal kopiert weiterreichst. Allerdings wäre das ein unguter Verstoß gegen das Prinzip der "Separation of Concern". Deine Model-Klasse interessiert es wenig, dass deiner Querybuildingklasse der Datenbanktreiber verloren gegangen ist und der Query deshalb scheiterte. Ebenso interessiert es den Controller nicht, welches Datenbankproblem nun genau zum Scheitern des Model-Ladens geführt hat. Das Model-Object konnte nicht in den Speicher befördert werden - das ist das Entscheidende. Nicht alle Models kommen aus einer Datenbank, deshalb interessiert der Datenbankfehler nur die Entwickler zum Bugfixing.

              Das ist interessant und hört sich sehr sinnvoll an (Es könnte ja später anstatt einer Datenbank ein XML-File sein und man sollte Models und Views ja unabhängig programmieren können). Wie sollte dann die Model-Klasse hantieren, wenn es sie wenig interessiert, dass der Datenbanktreiber verlorren gegangen ist? D.h., er kriegt die Exception dass der Datenbanktreiber verloren gegangen ist, was macht er dann am besten danach? Was teilt er dem Controller dann mit, wie wird es grob gehandhabt, der Fehler besteht ja.

              Noch eine Kleinigkeit noch, bzw. des Verständnises, wenn man Exceptions umher wirft :-)

              Die Datasourceklasse wirft eine DatasourceException

              try {
              Baue Verbindung zur Datenbank
              } catch (Exception e) {
              throw new DataSourceException ("Meine Message");

              Die Querybuilder hat nun in etwa zwei solche Methoden:

              public Resultdata QueryBuildAndRun (String query, boolean b) throws DatasourceException {

              try {
              Die Query wird durch aufruf der Datasourceklasse ausgeführt, wo Fehler auftreten können
              } catch (Exception e) {
              Hier kann ich vermutlich mit e.getMessage() die Message erhalten, obwohl ich nicht nach DatasourceException prüfe, sondern nach Exception

              return Resultdata
              }

              Nun komme ich zu meiner eigentlichen Frage

              Ich habe nun eine überladene Klasse, die die Methode QueryBuildAndRun aufführt, aber kein boolean-Wert erhält:

              public Resultdata QueryBuildAndRun (String a) throws DataSourceException {

              Diese Methode ruft folgende Methode von oben auf:
              QueryBuildAndRun (a, false);
              }

              Muss ich nun in der unteren Klassen eigentlich die Methode QueryBuildAndRun (a, false); auch in ein Try-Catch-Block rein, da die oberen Methode,die aufgerufen wird, ja einen Fehler werfen kann?

              Grüße

  2. Bitte konkreter und kompakter...

    mfg xxxMaster

    1. Bitte konkreter und kompakter...

      Hi,
      ich habe es im anderen Beitrag versucht etwas konkreter zu beschreiben. Falls es nicth gelungen ist, versuche ich es gerne noch etwas präziser, bzw. kompakter.

      grüße

  3. hi,

    Ich frage mich nun, wie ich aber bei einem Fehler bestimmte Fehlertexte auch an den User weitergeben kann. Ich kann den Fehler von der untersten (Connection) leider nicht bis in die View immer weiterreichen, da die Methoden immer verschiedene Typen zurückgeben.

    Das willst Doch auch nicht wirklich, oder? Java ist schon pragmatisch genug, so dass bestimmte Programmcodes nur dann übersetzt werden, wenn Du eine fundierte Exceptions Behandlung codest, aber dem (End)Benutzer würde ich nicht jeden Fehler-Code-Fetzen um die Ohren hauen. Beschränke Dich da auf das Wesentliche und Sinnvolle. Letzeres heißt in der Regel: Es funktioniert oder es funktioniert nicht.

    Hotti

    1. Hi hotti,

      vielen Dank, ich setze grad alles mit Exceptions um.

      Ich habe eine DataSourceException welches von Exception abgeleitet ist.

      public DataSourceException(String msg) {
              super(msg);
          }

      Ich kann nun von woanders im Try-Catch-Block speziell auf DatasourceException abfangen, bzw. allgemein auf Exception.

      Meine frage nun,
      1.wenn ich nur auf Exception ex im Catch-Block prüfe, würde er ja die DatasourceException ja auch abfangen ne, da es von Exception abgeleitet ist?

      2. Wie kann ich auf die message "msg" zugreifen, wenn ich das später anfangen? In der Variable e (Exception e) ist doch mehr drin?

      Grüße