Tag ‘Java’

Anti-Patterns: Werfen und Fangen von java.lang.Exception

February 2nd, 2010

Immer wieder entdeckt man als Java-Entwickler Code-Abschnitte in dem ein “Exception Handling” verbaut ist. Das ist erfreulich und auch gut so. Exception Handling erlaubt es auf Fehler die zur Laufzeit der Software auftreten zu reagieren. Manchmal entdeckt man dabei jedoch Code-Stücke wie:

1
2
3
if (BEDINGUNG) {
    throw new Exception("An error occurred.");
}

oder

1
2
3
4
5
try {
    // some code
} catch (Exception e) {
    // exception handling
}

Diese Art mit der java.lang.Exception herumzuspringen birgt einige Nachteile und sogar Risiken. Warum Nachteile enstehen und welchen Risiken man sich aussetzt wenn man sein Exception Handling an der Basisklasse aller Exceptions ausrichtet, sei hier deshalb kurz aufgezeigt:

Lesbarkeit

Die Lesbarkeit des Codes leidet unter dem Werfen von java.lang.Exception.

1
throw new CorruptFileException();

sagt deutlich mehr aus als ein einfaches

1
throw new Exception();

Ich selbst vergleiche das Werfen von java.lang.Exception gerne damit jemandem versehentlich auf den Fuss zu treten. Das ist ungefähr so, als träte man jemanden versehentlich auf den Fuss und bekommt als Antwort einen Schlag ins Gesicht. Es geht einfach ein großer Teil der Information der Exception verloren.

Angst und Unsicherheit

Bei catch-Blöcken, die java.lang.Exception fangen, wird mir immer mulmig im Bauch. Es entsteht der Eindruck als wüsste der Entwickler nicht, welche Fehler in diesem Teil seiner Software auftreten können.

1
2
3
4
5
try {
    // some code
} catch (Exception e) {
    // exception handling
}

Um diese Unsicherheit zu beseitigen, wird fälschlicherweise oft auf java.lang.Exception gecatcht. Das ist schlecht. Besser ist es die Exception einfach “nach oben” durchkrachen zu lassen oder nur die Exceptions zu fangen, die der zugehörige Source-Code auch werfen kann. Ein catch auf java.lang.Exception ist im Zweifelsfall immer die schlechteste Wahl, da hier auch alle Exceptions gefangen werden, die an dieser Stelle des Source-Code besser nicht gefangen werden sollten. Noch schlimmer wird es wenn auf Throwable gecatcht wird.

1
2
3
4
5
try {
    // some code
} catch (Throwable t) {
    // exception handling
}

An dieser Stelle werden nicht nur alle Exceptions sondern auch alle Errors gefangen. Das hat dann mit Genauigkeit gar nichts mehr zu tun.

Schlechtere Wartbarkeit

Ein Entwickler, der für diese Software später vielleicht mal die Pflege und/oder Änderungen übernimmt und mit der Software nicht vertraut ist, der wird sich unter Umständen tot suchen, wenn das Logging nicht Klasse und Zeile des Sources mitloggt und einen catch wie in dem folgenden Beispiel macht.

1
2
3
4
try {
} catch (Exception e) {
    LOG.error(e);
}

Dieser Fehler kann insbesondere dann kritisch werden, wenn eine Software das für das Logging das Log4J PatternLayout verwendet, das gerade beim Loggen von Klasse und Zeile sehr viel Performance frisst und deshalb für das Logfile manchmal maximal noch Datum, Uhrzeit und Log-Level ins Pattern konfiguriert wird.

Verlust des Informationsgehalts

Nicht nur die Message einer Exception sondern auch der Name einer Exception kann auch einen Informationsgehalt haben und den Code leserlicher machen. So ist

1
2
3
4
5
try {
    // some code
} catch (PrinterUnreachableException e) {
    // exception handling
}

mit deutlich mehr Information bestückt als

1
2
3
4
5
try {
    // some code
} catch (Exception e) {
    // exception handling
}

RuntimeExceptions werden verschluckt

Entwickler sind auch nur Menschen und Menschen passieren Fehler. Jeder Entwickler hat schon mal einen Fehler gemacht, der zum Beispiel eine NullPointerException oder eine ähnliche RuntimeException ausgelöst hat.

1
2
3
4
5
6
7
try {
    // some code
    Person person = null;
    String = person.getName();
} catch (Exception e) {
    LOG.error("An error occurred.");
}

Diese NullPointerException wird einfach verschlammt. Sie weißt auf einen Programmierfehler hin aber verschwindet weil sie unsachgemäß verschlammt wird. Der Entwickler, der diesen Source nun debuggen soll, hat keinen sinnvollen Anhaltspunkt was schief gelaufen sein könnte.

Erst fangen und dann doch lieber weiter werfen

1
2
3
4
5
6
7
8
public void foo() throws Exception {
    try {
        // some code
    } catch (Exception e) {
        LOG.error(e);
        throw e;
    }
}

Dieser Fall ist auch mit einem negativen Beigeschmack versehen. Auch wenn er auf den ersten Blick vielleicht völlig korrekt erscheinen mag. Die gefangene Exception wird ins Logfile geschrieben und dann weitergeworfen. Was wird wohl der Source außerhalb Methode foo machen? Richtig! Er wird die Exception irgendwo erneut fangen und mit großer Wahrscheinlichkeit noch mal im Logfile versenken. Die Exception wird 2 Mal im Logfile versenkt obwohl sie nur ein mal aufgetreten ist. Ugly hoch drei!

Vertuschen

Leere catch-Blöcke sind genauso gefährlich wie das Fangen von java.lang.Exception. Doch noch viel schlimmer wird das Ganze, wenn eine java.lang.Exception gefangen wird und dann zusätzlich auch noch der catch-Block leer bleibt.

1
2
3
try {
    // some code
} catch (Exception e) {}

Dieser Fall ist der Supergau. Der Fehler wird weder behandelt noch wird er jemals auffindbar. Er wird vertuscht und “das große Suchen” beginnt. Eine weitere Form des Vertuschens ist das Loggen der Exception-Message innerhalb catch-Blocks auf java.lang.Exception.

1
2
3
4
5
try {
    // some code
} catch (Exception e) {
    LOG.error(e.getMessage());
}

Diese Art der Vertuschung wird manchmal damit begründet, dass das Logfile nicht zu lang werden soll und/oder der Logfile Inhalt nicht mehr schön aussieht. Wobei wir dann natürlich bei der Frage wären, ob wir das Logfile überhaupt noch brauchen. Ein Logfile das “hübsch” aussieht, das ist genauso hilfreich bei einer eventuellen Fehlersuche als wenn man die Software ohne Logfile im Blindflug betreibt. Noch eine weitere Form des Vertuschens ist das einfache returnen von null im Fehlerfall.

1
2
3
4
5
try {
    // some code
} catch (Exception e) {
    return null;
}

Diese Möglichkeit kann zwar in einigen Fällen sinnvoll erscheinen aber weißt auf alles andere als einen guten Entwicklungstil hin. Und gerade bei umfangreicheren Methoden ist die Lösung pauschal schon falsch.

Wie man jetzt sicher einfach versteht, ist das direkte arbeiten mit java.lang.Exception eher so der “Holzfäller-Style” der Programmierung. Deshalb sollte man sich neben “Fail fast! Fail early! Fail loud!” auch mit den für den jeweiligen Source-Bereich passenden Exceptions arbeiten.

In diesem Sinne…
Lasst es krachen! Aber richtig!

Für weiter Interessierte empfehle ich:
http://today.java.net/pub/a/today/2006/04/06/exception-handling-antipatterns.html

1 Kommentar »

Volle Fahrt vorraus

November 3rd, 2008

Gerade rausche ich mit dem InterCity Richtung München. WJAX 2008 heißt mein Ziel. Zum Glück habe ich hier im Zug einen Platz mit Steckdose erwischt. Jetzt kann ich gemütlich vor mich hin fahren und während dessen mit meinem Netbook in der Dokumentation des Django Frameworks rumstöbern. Ok, man könnte jetzt behaupten: WJAX und Django passen überhaupt nicht zusammen. Das ist im Grunde auch richtig, aber es schadet nie mehrgleisig zu fahren. Noch bin ich der Meinung: Nicht die ganze Welt ist Java. Mal schauen ob das nach der Konferenz auch noch so ist.


Mein Netbook während der Anreise

Kommentar schreiben

Wordpress aus Java

October 8th, 2008

WTF? Bye Bye PHP? Es gibt jetzt eine Wordpress-Variante aus Java auf Basis des Spring MVC Frameworks. KLicht gut, oder? Das Ding heißt nWordPress und wurde von numiton.org released. Numiton hat sich auf die Fahne geschrieben Open Source Projekte, die PHP entwickelt wurden, auf Java umzustellen. Komische Idee. Aber sie bringt auch Vorteile mit, denn schließlich werden die “Gefahren von PHP” so ganz gut umgangen. Ich glaube heute abend schaue ich mir mal den Source von dem Ding an. Wenn das sauber umgesetzt ist, dann kann man damit sicher ne Menge Spaß haben. Ich brauche sowieso einen neuen Blog: Den “PhilDevBlog”. :D

1 Kommentar »

Mehrere Java-Versionen auf einem Ubuntu-System

July 25th, 2008

Auf meinem Ubuntu-Desktop-Rechner habe ich aktuell 4 unterschiedliche Java-Versionen im Einsatz. Da ich nicht alle Java-Versionen gleichzeitig benutzen will/kann und für unterschiedliche Tests eine jeweils andere Java-Version benutzen möchte, brauche ich einen Schalter. Und diesen Schalter habe ich jetzt gefunden:

sudo update-alternatives --config java

Besser und leichter kann man seine Java-Version einfach garr nicht wechseln. Super nice!

Kommentar schreiben