jsf-forum.de jsf-forum.de jsf-forum.de
jsf-forum.de    
jsf-forum.de

Exception Handling

Kennen sie die Situation, wenn sie auf eine Webseite gehen und dort plötzlich ein StackTrace zu sehen ist? Das wirkt professionell, nicht wahr? Natürlich ist dies der Worst Case, wenn dem Anwender im Fehlerfalle ein Stacktrace angezeigt wird. So etwas darf nicht passieren. Aber wie kann man dies verhindern? Man könnte sicherlich eine globale Fehlerseite in der web.xml angeben, die zumindest die Anzeige eines StackTraces verhindert. Aber vielleicht möchten sie mehr tun: die Betriebsüberwachung benachrichtigen, Loggen, und dem Anwender eine sinnvolle Meldung anzeigen ("Bitte versuchen sie es zu einem späteren Zeitpunkt noch einmal." ;-) ).

In JSF 2.0 wurde daher ein Schwerpunkt auf ein funktionierendes Excpetion Handling gelegt. Das war in der Vergangenheit nicht ganz einfach, da viele Fehler zu oft gewrappt wurden und nirgendwo richtig eingegriffen werden konnte. Mit JSF 2.0 kann jetzt ein eigener ExceptionHandler registriert werden, in dem dann eigener Code für eine spezielle Fehlerbehandlung untergebracht ist.

Am konkreten Beispiel:

    <h:commandButton value="Make Error" action="#{simpleController.doSomethingSilly}" />

Hier ist zunächst ein trivialer CommandButton zu sehen. Mein erzwungener Fehler passiert in der Aktionsmethode.

@ManagedBean
public class SimpleController {
   
   public String doSomethingSilly() {
      String s = null;
      // Aua !
      s.length();
      return null;
   }
}
Wenn dieser Code ausgeführt wird, erscheint eine Fehlerseite inklusive einem Stacktrace. Für unser Beispiel möchte ich es jetzt wie folgt behandeln: Es soll einfach auf die Startseite weiternavigiert werden und dem Benutzer eine Meldung angezeigt werden.

Um dies zu realisieren, muss zunächst eine ExceptionHandler-Factory in JSF bekanntgemacht werden.
<factory>
   <exception-handler-factory>de.jsfforum.training.ui.error.MyExceptionHandlerFactory</exception-handler-factory>
</factory>

Nach dem bekannten JSF Decorator Pattern (Verhalten wird nicht ersetzt, sondern ergänzt), kommt eine neue Factory dazu.

package de.jsfforum.training.ui.error;

import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;

public class MyExceptionHandlerFactory extends ExceptionHandlerFactory {

   private ExceptionHandlerFactory parent;

   public MyExceptionHandlerFactory( ExceptionHandlerFactory parent ) {
      this.parent = parent;
   }

   @Override
   public ExceptionHandler getExceptionHandler() {
      ExceptionHandler result = parent.getExceptionHandler();
      result = new MyExceptionHandler(result);
      return result;
   }

}
Die Factory ist recht unspektakulär, wird lediglich ein eigener ExceptionHandler erzeugt. Der Handler ist nun das Herzstück der ganzen Angelegenheit:

package de.jsfforum.training.ui.error;

import java.util.Iterator;

import javax.faces.FacesException;
import javax.faces.application.FacesMessage;
import javax.faces.application.NavigationHandler;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;

public class MyExceptionHandler extends ExceptionHandlerWrapper {

   private ExceptionHandler wrapped;

   public MyExceptionHandler(ExceptionHandler wrapped) {
      this.wrapped = wrapped;
   }

   @Override
   public ExceptionHandler getWrapped() {
      return this.wrapped;
   }

   @Override
   public void handle() throws FacesException {
      
      Iterator<ExceptionQueuedEvent> itr = getUnhandledExceptionQueuedEvents().iterator();
      while ( itr.hasNext() ) {
  
         ExceptionQueuedEvent event = itr.next();
         ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
         Throwable thr = context.getException();

         if ( thr instanceof FacesException ) {
            FacesContext fc = FacesContext.getCurrentInstance();
            NavigationHandler nav = fc.getApplication().getNavigationHandler();
            try {
               fc.addMessage( null, new FacesMessage("Lieber Anwender, leider ist bei uns etwas schief gegangen. :-") );
               nav.handleNavigation(fc, null, "startseite" );
               fc.renderResponse();
            } finally {
               itr.remove();
            }
          }
      }
      getWrapped().handle();
   }
}

Es werden zunächst alle Exceptions betrachtet, die in der Queue hängen. Wenn ein "passender" dabei ist, wird eine neue Message erzeugt und auf die Startseite verwiesen. An dieser Stelle könnte natürlich eine beliebige projektspezifische Fehlerverarbeitung erfolgen.

Fazit: Mit einem eigenen Exception Handler hat es der Entwickler zukünftig selber in der Hand, vernünftig auf Exceptions zu reagieren.

Zurück zur Übersichtsseite

jsf-forum.de