# Grundlegende Fragen zum Exception-Handling



## Lakai (6. Jan 2010)

Hallo java folks!

Ich sitze hier gerade an einem mittleren Webprojekt als Übung für die Uni. Es handelt sich dabei um ein Webportal. Ich bin gerade dabei das Exception-Handling einzufügen. 

*Generelles*
Unbehandelte Exceptions, werden generell durch folgenden Eintrag in der web.xml auf ein Servlet gemapt:

```
<servlet>
    <description>Handles all exceptions.</description>
    <servlet-name>ExceptionHandler</servlet-name>
    <servlet-class>de.controller.utilities.ExceptionHandler</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>ExceptionHandler</servlet-name>
    <url-pattern>/error</url-pattern>
  </servlet-mapping>
  <servlet>
  <error-page>
    <exception-type>java.lang.Throwable</exception-type>
    <location>/error</location>
  </error-page>
  <error-page>
    <error-code>404</error-code>
    <location>/error/error404.jsp</location>
  </error-page>
[... weitere error codes ...]
```

Das Servlet, dass die Exceptions behandelt, extrahiert ein paar Informationen über die Exception aus dem request und leitet diese dann auf eine Jsp weiter.

Soweit so gut, funktioniert alles. Jetzt muss ich allerdings noch etwas weiter ausholen um meine Frage zu erläutern. 

Http-Anfragen werden in diesem Projekt entweder auf ein Frontend-Servlet oder ein Backend-Servlet gemapt. Diese leiten die Anfragen dann an jeweils eine Factory, die an Hand des Querystrings die passende Action-Klasse erzeugt. Das Servlet führt dann auf dieser Klasse eine doAction-Methode aus, welche dann die eigentlich Ablaufsteuerung enthält. Die Eltern-Action kümmert sich um generelle Sachen wie beispielsweise die Autentifizierung, so dass sich in den Action-Klassen lediglich die folgenden zwei Methoden unterscheiden.
[Java]    @Override
    protected abstract String executeCommand(HttpServletRequest request,
	    HttpServletResponse response, String queryString, String tld)
	    throws DaoException;

    @Override
    protected abstract void setContent(HttpServletRequest request,
	    String queryString, String tld) throws DaoException;[/Java]

Diese beiden werfen nun auftretende DaoExceptions, die während dem Zugriff auf die Datenbank auftreten können, weiter an die doAction()-Methode. Dort wird die Exception dann letztendlich gefangen und bisher wird die Request URL gesäubert, so dass einfach nur die entsprechende JSP-Seite ohne ein etwaiges Kommando (im Querystring aufgerufen wird).

[Java]    @Override
    public String doAction(HttpServletRequest request,
	    HttpServletResponse response) {
	try {
	    if (request.getCharacterEncoding() == null) {
		request.setCharacterEncoding("UTF-8");
	    }
	} catch (UnsupportedEncodingException e) {
	    logger.error(e.getMessage());
	}
	logger.debug("Encoding: " + request.getCharacterEncoding() + ".");
	String userName = (String) request.getSession()
		.getAttribute("userName");
	logger.debug("Executing " + this.getClass() + ".");

	if (!checkCredentials(userName)) {
	    return forwardToStatusPage(request, response, tld, new StatusBean(
		    "hdg_authError", "txt_authError"));
	}
	this.userName = userName;
	try {
	    String forward = executeCommand(request, response, queryString, tld);
	    if (forward != null) {
		return forward;
	    }
	    preparePage(request, response);
	} catch (DaoException e) {
	    logger.error(e.getMessage());
	    queryString = QueryStringFormatter
		    .getQueryStringForAction(QueryStringFormatter
			    .getAction(queryString));
	}
	return FrontendActionFactory.JSP_INDEX;
    }
[/Java]

Jetzt bin ich am überlegen wie ich das mit den Exceptions mache. *Sollte ich nicht vielleicht die auftretende DaoException bis hoch zum ExceptionHandler-Servlet werfen?* Also quasi "public String doAction(HttpServletRequest request,
	    HttpServletResponse response) throws DaoException"...


Die executeCommand() überprüft den QueryString auf einen command-Teil. Dies ist ein String der in der executeCommand extrahiert wird und an Hand dessen dann bestimmte Aktionen ausgeführt werden. Falls kein command-Teil übergeben wurde, wird null zurückgegeben und die preparePage()-Methode wird ausgeführt. Bisher ist es so, dass wenn ein gültiger Command-Teil übergeben wird, aber fehlerhafte Parameter zu diesem Kommando (beispielsweise eine Id), gibt diese auch einfach null zurück. Siehe nachfolgender Code:

[Java]    @Override
    protected String executeCommand(HttpServletRequest request,
	    HttpServletResponse response, String queryString, String tld)
	    throws DaoException {
	logger.debug("Executing commands in " + this.getClass());
	String command = QueryStringFormatter.getCommand(queryString);
	if (command == null || command.isEmpty()) {
	    logger.debug("No command was given. [" + queryString + "]");
	    return null;
	} else if (command.equals("showItem") || command.equals("hideItem")) {
	    return hideShow(request, response, queryString, command, tld);
	} else if (command.equals("sort")) {
	    logger.debug("Sorting the content items.");
	    return sort(request, response, queryString, tld);
	} else if (command.equals("changeOrderType")) {
	    logger.debug("Changing the order type.");
	    return changeOrderType(request, response, queryString, tld);
	} else {
	    logger.debug("Invalid command was given. [" + queryString + "]");
	    return null;
	}
    }[/Java]
Ich bin nun am überlegen ob ich im letzten else-Teil statt dem "return null" eine MalformedSyntaxException (eigene Exception) werfe, die dann bis rauf zum Servlet geworfen wird.

Lange Rede - kurzer Sinn:
Eigene Exceptions werfen - gut oder böse? (Hier kommt es mir vor allem auf guten Stil an, Konventionen und so)
Exceptions so schnell wie möglich abfangen oder auch mal weiterwerfen?
Den Benutzer mit Fehlerseiten beunruhigen oder durch interne Säuberung des QueryString nicht darüber informieren, dass jetzt etwas nicht funktioniert hat, wie er eigentlich wollte. 
"Exception-Throws" so speziell wie möglich definieren oder nicht? (Ich denke hierbei an bsp. das FrontendServlet: doPost(...) throws ServletException, IoException, DaoException, AuthorizationException, MalformedSyntaxException VS. doPost(...) throws Exception)

Wie macht ihr das in eure Projekten? Habt ihr vielleicht auch Links zu Seiten die sich etwas mit Exceptionhandling auf Servlets/Jsps befassen?

Bin für jede Anregung dankbar!

Grüße


----------



## HLX (6. Jan 2010)

Lakai hat gesagt.:


> Eigene Exceptions werfen - gut oder böse? (Hier kommt es mir vor allem auf guten Stil an, Konventionen und so)


Selbstverständlich kannst du eigene Exceptions werfen. Die Exception sollte dabei eine sprechende Bezeichnung haben.


Lakai hat gesagt.:


> Exceptions so schnell wie möglich abfangen oder auch mal weiterwerfen?


Ich würde Exceptions nicht so schnell wie möglich abfangen. Du würdest dir damit die Flexibilität nehmen, später (z.B. in der GUI) darauf individuell zu reagieren. 


Lakai hat gesagt.:


> Den Benutzer mit Fehlerseiten beunruhigen oder durch interne Säuberung des QueryString nicht darüber informieren, dass jetzt etwas nicht funktioniert hat, wie er eigentlich wollte.


Ich finde intere Säuberung furchtbar. Ich rege mich als Anwender immer darüber auf, wenn ich keine vernünftige Information über einen aufgetretenen Fehler erhalte. Was soll der Anwender seinem Support/Administrator sagen, wenn er (überspitzt formuliert) lediglich die Fehlermeldung "Es ist ein Fehler aufgetreten!" erhält?


Lakai hat gesagt.:


> "Exception-Throws" so speziell wie möglich definieren oder nicht? (Ich denke hierbei an bsp. das
> FrontendServlet: doPost(...) throws ServletException, IoException, DaoException, AuthorizationException, MalformedSyntaxException VS. doPost(...) throws Exception)


Exception-Throws würde ich übersichtshalber spätestens bei der 4. Exception zusammenfassen


----------



## Lakai (7. Jan 2010)

Vielen Dank, HLX!

Dann werde ich meine Überlegungen wohl in die Tat umsetzen!..

Außer jmd., der das hier noch liest ist komplett anderer Meinung...


----------



## jule37 (10. Jan 2010)

generell hat sich folgendes bewährt:

niemals exceptions umwandeln. das kann im schlimmsten fall dazu führen, dass man den ort, wo die exception zuerst auftrat niemals findet, wenn das projekt erstmal groß ist. außerdem, wenn z.b. eine IoException auftritt will und sollte man auch eine IoException fangen. sonst muss man jedes mal schauen, was da eigentlich los ist.

eigene exceptions nur als fachliche exceptions, die von der fachlogik ausgelöst werden

exceptions immer sofort fangen, wenn man den fehler behandeln und wieder "gutmachen" kann, ansonsten gar nicht fangen! generell behandelt die fachlogik so gut wie nie exceptions, sondern fast immer nur die ui (sonderfälle vorbehalten). wenn ein geschäftsprozess fehlschlägt, schlägt er halt fehl und die ui muss dem benutzer sagen "sorry, geht nicht, weil: darum...".

dem benutzer gegenüber die fehlerquelle zu verschleiern oder nicht ist eine designentscheidung und hängt von der zielgruppe ab. im web z.b. würde ich nur allgemeine fehlermeldungen geben ("dieser dienst ist nichtz verfügbar" o.ä.) und exakte fehlermeldungen mit stacktrace nur ins log für den admin / entwickler.

edit:

nochwas:
exception throws in der methodensignatur zusammenzufassen ist ganz ganz schlechter stil. der sinn von checked exceptions ist es, dass der compiler überprüfen kann, ob alle nötigen catchblöcke vorhanden sind. fasst man diese zusamen profitiert man nicht mehr von diesem automatismus und läuft gefahr, bestimmte throws zu vergessen zu behandeln. und wenn das projekt erstmal schön groß und unübersichtlich ist, sitzt man zwei tage order so und sucht obwohl man einfach nur die throws ordentlich hätte auflisten müssen (5 minuten vs 2 tage). 

fazit: wenn checked exceptions, dann ordentlich auflisten. es ist etwas mühsam, aber 100 mal sauberer und nachvollziehbarer. denk dran, dass man immer so coden soll, dass andere ohne eine einführung von dir damit arbeiten können.

wenn du die throws nicht auflisten willst, dann wirf RuntimeExceptions (unchecked)


----------



## Lakai (15. Jan 2010)

okay vielen vielen dank euch beiden!

in meinem nächsten projekt werde ich die exceptions wohl auch so schnell wie möglich abfangen. und dann eben ein null oder ein false returnen. ist im grunde schon besser, weil die funktionen die auf diese werte zugreifen wollten merken ja das sie keinen gültigen wert erhalten und können dementsprechend reagieren. und alle anderen funktionen laufen dann wenigstens sauber weiter. so wird mir zumindest außen herum meine seite sauber aufgebaut (navigation, benutzermenü, templates, ...) und über den nichtvorhandenen seiteninhalt (der berechnet werden sollte, als die exception auftrat) kann ich dann immer noch relativ speziell und flexibel berichten, je nachdem in wie weit ich das überhaupt will (-> zielgruppe).

das projekt hier hat nur leider schon ein paar üble denkfehler im entwurf... aber es ist ne sehr sehr gute übung!


----------



## Landei (15. Jan 2010)

Bezüglich der Umwandlung von Exceptions kann ich Jule nicht ganz zustimmen. Angenommen du bekommst eine FileNotFoundException, wenn du dein properties-File nicht lesen kannst. Der Kern des Problems ist nicht "Datei nicht gefunden" sondern "Konnte Properties nicht laden". Ob die Properties auf Platte, in einer Datenbank oder sonstwo gespeichert werden, spielt hier aus Anwendungssicht keine große Rolle, und deshalb ist es sinnvoll, die "physische" FileNotFoundException in eine "logische" Exception umzuwandeln.

Checked Exceptions sind ein zweischneidiges Schwert, und viele Programmiersprachen haben sich dagegen entschieden. Das Hauptproblem ist Vererbung. Angenommen du schreibst ein Interface zum Laden von Properties. Welche Exceptions soll man jetzt für die Load-Methode angeben? FileNotFoundException? SQLException? RemoteException? Denn das hängt ja eben von der Implementierung ab.

Und natürlich führt der Zwang, auch triviale Exceptions behandeln zu müssen, zu unübersichtlichem Code. Insbesondere die fehlende Möglichkeit, in einem catch-Zweig mehrere Exceptiontypen abfangen zu können, bläht den Code unnötig auf, oder führt zu absolut kontraproduktiven Techniken wie catch (Throwable t), das einfach alle Exceptions frisst.


----------



## jule37 (15. Jan 2010)

wie immer gibt es hier kein einziges richtig und falsch

das beispiel, das du genannt hast, spricht außerdem wieder gegen umwandlung der exception. wird dein config file nicht gefunden soll auch die FileNotFound exception fliegen. die nachricht, die du dem user gibst, kann doch trotzdem eine andere sein... willst du dem etwa deinen stacktrace zeigen? im log sollte stehen "FileNotFoundException: config.file not found" dann ist die fehlerursache absolut klar. außerdem weiss man nie, welche exceptions alle auftreten können. man denkt es zwar oft, aber das ist nicht korrekt. grad in web projekten. was ist, wenn eine völlig unerwartete exception fliegt und du verpackst sie in eine andere? viel spaß beim finden des fehlers, vor allem deinen usern dann. stell dir vor tomcat würde im catalina.out nur verpackte exceptipons zeigen. niemals die problemursache verschleiern!

checked exceptions sind wirklich eine medaille mit zwei seiten, aber wenn man sie diszipliniert behandelt sind sie sehr gut beherrschbar, gerade was fachliche exceptions angeht. das problem mit der vererbung von interfaces, was du angesprochen hast ist allerdings völlig richtig, betrifft aber auch nicht die fachlogik, sondern andere schichten.

und an lakai: nein eben nicht null oder irgendwas liefern. du redest dort grad von deiner fachlogik und die sollte fehlschlagen, wenn eine exception fliegt. immer in use cases denken. vor allen sollten in der fachschicht keine exceptions gefangen werden. behandeln fachlicher exceptions ist zu 99% ui sache.

technische exceptions kommen meist aus der db oder ui schicht, wobei schon eher db und die sollte man hochreichen und loggen, weil dann ein fehler an der umgebung, nicht im programm vorliegt und der admin ran muss. allerdings sollte man den end-usern nicht immer alle technischen ursachen präsentieren.

ich schätze mal es ist philosophiesache, aber für mich hat sich dieses prinzip bewährt und wirkt sich positiv auf das erreichen loser kopplung aus.


----------



## Landei (16. Jan 2010)

jule37 hat gesagt.:


> wie immer gibt es hier kein einziges richtig und falsch
> 
> das beispiel, das du genannt hast, spricht außerdem wieder gegen umwandlung der exception. wird dein config file nicht gefunden soll auch die FileNotFound exception fliegen. die nachricht, die du dem user gibst, kann doch trotzdem eine andere sein... willst du dem etwa deinen stacktrace zeigen? im log sollte stehen "FileNotFoundException: config.file not found" dann ist die fehlerursache absolut klar. außerdem weiss man nie, welche exceptions alle auftreten können. man denkt es zwar oft, aber das ist nicht korrekt. grad in web projekten. was ist, wenn eine völlig unerwartete exception fliegt und du verpackst sie in eine andere? viel spaß beim finden des fehlers, vor allem deinen usern dann. stell dir vor tomcat würde im catalina.out nur verpackte exceptipons zeigen. niemals die problemursache verschleiern!



Was spricht dagegen, das Ding zu fangen und in new PropertyLoadException(fileNotFoundEx) zu verpacken? Code, der das Problem "logisch" angeht (etwa um eine Nutzermeldung zu zeigen), hat nur einge Sicht der Dinge (bei mehreren Varianten, um eine Property zu landen), "technischer Code" (wie etwa zum Loggen) kommt immer noch an die Original-Exception ran. Großer Vorteil ist, dass man selber entscheiden kann, ob die eigene PropertyLoadException checked oder unchecked ist, und wo man sie in der Hierarchie einordnen will (z.B. könnte man eine allgemeine PropertyException haben, von der sie erbt).


----------



## jule37 (16. Jan 2010)

ok, das mit dem verpacken könnte man machen, solange man die ursprüngliche exception in irgendeiner from weitergibt, ja warum nicht. dann wäre ja die fehlerursache nicht verschleiert. es hätte wohl keinen nachteil, allerdings sehe ich auch keinen vorteil so richtig, da ja wie gesagt jede exc im kontext eines use cases fliegt und sich dadurch eh eine spezielle behandlung und meldung ergibt.

das ist halt auch ein sehr philosophisches thema, habe da zusammengerechnet schon mehrere tage meines lebens drüber diskutiert. es gibt auch leute die auf multex schwören (ich nicht)...

und die kontreverse zwischen checked und unchecked exc ist ja auch nix neues. in c# z.b. haben sie checked exc komplett rausgenommen, was mich dort auch nicht stört. das ist auch ein bischen gewohnheitssache.

vor allem in web applikationen schreibe ich oft eh generalisierte error handler, die eine fehlerseite und einen logeintrag generieren, auf ui ebene. aber das ist auch ein bischen was anderes, weil dort nur der vorgang stirbt und nicht der server. der user kann dann einfach auf zurück oder auf startseite klicken und andere werte versuchen, wohingegen eine desktop applikation komplett crashen könnte.


----------

