User's Guide for MulTEX - The Multi Tier Exception Handling Framework

Christoph Knabe

TFH Berlin
- University of Applied Sciences -

2005-04-26


Table of Contents

Introduce Central Exception Reporting
Reporting in Command Line Applications
Reporting in Swing Applications
Reporting in JSP Applications
Reporting in background threads
How to write method bodies in the API layers (TODO)

The main goal of this user's guide is to tell, how to quickly introduce robust and diagnostic exception handling and reporting into a Java software system.

These hints are easier to follow, if you are writing a software system from scratch, but can be applied onto existing software, as well.

You should firstly introduce central exception reporting, secondly assure exception propagation to the highest possible level, thirdly provide all exceptions with diagnostic parameters, and lastly provide natural message texts for all execptions.

Introduce Central Exception Reporting

When you are using MulTEx, the main paradigm for error handling and reporting are Java exceptions. Any misfunction will be passed to the caller of a method by the standard Java means, an exception. This will be propagated to the upmost level, where it still can be caught, in order to report it.

Depending on the type of user interface, there are different places, where you can catch all propagated exceptions. Sometimes it is not simple, to find this place in the UI documentation. Sometimes you will not be able to find it, and must use a workaround in order to assure central exception reporting.

Reporting in Command Line Applications

In a Java application, invoked as a command line, you should report, both the exception messages, and its stack trace, as the command line application typically does not have a button for requesting the details of the stack trace. The destination for the exception reporting usually will be System.err.

If you specify the main method as

public static void main(final String[] i_args) throws Exception {...}
, you will already have a centralized exception reporting, but the Java Runtime system will report only the stack trace without giving nice textual error messages.

In order to get the textual message chain, and the stack trace, you must write your main method as follows:

	  public static void main(final String[] i_args) {
	    ... //check and report argument violations
	    try{
	      _doTheWork(i_args);
	    }catch(final Exception e){
	      multex.Msg.printReport(e);
	    }
	  }
	
This will report to System.err the exception chain messages using for localization the resource bundle MsgText in the actual working directory, followed by the stack trace. If you want to use another resource bundle for localization, then use the variant
printReport(StringBuffer, Throwable, ResourceBundle)
, instead, and print out the filled StringBuffer, afterwards.

Reporting in Swing Applications

In a Java Swing application, you should report all exceptions by a variant of the static multex.Swing.report() methods. These report firstly a window with the messages of the exception causal chain. Then pressing on the button Details will expand the dialog and show the stack trace of the exception chain, too.

Usually the variant of method multex.Swing.report() with an owner component should be used, as it will block further input to the owner component or its parent window, until the exception message dialog has been closed by the user.

Last-resort exception reporting

In order to report all uncaught exceptions in a Swing application, which occur during the execution of an UI-event-triggered action, it is sufficient to install one central exception handler using the undocumented system property sun.awt.exception.handler, see more in a JGuru discussion.

This undocumented behaviour is wrapped by a MulTEx service. You can implement the interface multex.AwtExceptionHandler, and install this class by the method multex.Awt.setAwtExceptionHandlerClass(...). Then any exception propagating up to the AWT/Swing event-dispatch thread will be reported by the handler's method handle(Throwable). In this method you should call a multex.Swing.report() method.

In order to block input to the GUI application while reporting an exception, it is necessary to locate the application's frame and use it in the central exception handler's call to report(...) as the ownerHook.

Centralized exception reporting using javax.swing.AbstractAction

If an application has several frames, and it is not possible to determine automatically, which UI component must be blocked during exception reporting, then there is a more work-intensive way to central exception reporting.

You should write your own class, e.g. UiAction as a subclass of javax.swing.AbstractAction:

          public class UiAction extends javax.swing.AbstractAction {
             final void actionPerformed(ActionEvent ev){
               try{actionPerformedWithThrows(ev);
               }catch(Exception ex){multex.Swing.report(ev.getSource(), ex);
               }
            }
          }
	      
This class gets the UI component causing the event, uses its parent chain to get the owner frame, and will block it during exception reporting. When using UiAction instead of javax.swing.AbstractAction you must extend UiAction redefining the method actionPerformedWithThrows instead of actionPerformed.

Note: Not yet tested by me personally, but used in a project known to me.

Reporting in JSP Applications

In a Java Server Pages application, you have several places, where you should report exceptions. These include firstly a JSP error page, which will be called by any unhandled exception. Secondly each JSP page with an input form should contain an error message, if something went wrong during execution of its action. Thirdly, depending on the UI framework used, there is the possibility to report an exception near to the form field, which caused it.

Last-resort exception reporting using an error page

In order to report all uncaught exceptions in a JSP/servlet application, which occur during the execution of an UI-event-triggered action, it is sufficient to install one central error page. See section “2.4.2 Request Time Processing Errors” of the Sun Java Server Pages Specification about this.

In Tomcat you can do this in the deployment descriptor web.xml by an “error-page” directive. E.g.:


          <error-page>
              <exception-type>java.lang.Throwable</exception-type>
              <location>/system/errorPage.jsp</location>
          </error-page>

        

This means, that any exception, including Throwable, will be reported by forwarding to the JSP page /system/errorPage.jsp. The error page itself must be marked as such by setting the page directive’s isErrorPage attribute to true, e.g.:

        <%@page contentType="text/html" isErrorPage="true" %>
        

In such an error page you should report not only the message texts of the exception chain, but also its stack trace and diagnostically useful attributes of the request, session, and application.

TODO: Give examples.

Centralized exception reporting with Struts using org.apache.struts.action.Action

As for Struts I did not find a possibility to set a central exception handler, you must use the approach with subclassing the Struts Action class.

You should write your own class, e.g. UiAction as a subclass of org.apache.struts.action.Action:

          public class UiAction extends org.apache.struts.action.Action {
            public ActionForward execute(
                final ActionMapping mapping,
                final ActionForm form,
                final HttpServletRequest request,
                final HttpServletResponse response
            ){
               try{return executeWithThrows(mapping, form, request, response);
               }catch(Exception ex){... //report(ex)
               }
            }
          }
	      

This class's execute method will be invoked by Struts. It calls itself the method executeWithThrows of its subclass, catching all Exception-s of it. This will report the caught exception by storing it in the session attribute “EXCEPTION” and its message chain by Struts' saveErrors(...), and then forwarding to either the JSP input page, if specified in the struts-config.xml, or to the errorPage.jsp as last-resort.

Note: The report action in the catch-block is quite complex, so you should look at the example running on the webserver http://fb6.tfh-berlin.de/, which can be obtained from the author, in the moment at Examples for Central Exception Reporting.

Form Field related Exception Reporting with Struts

One student group even implemented centralized exception reporting for form field related exceptions. In Struts it is possible to saveErrors oder saveMessages indicating the name of the offending field of the form working on. This will place the message in the appropriately marked position in the form.

In the central exception handler for Struts you can specifically handle exceptions, which are related to a form field. E.g. a FieldvalueExc could port the identifier of the related field, so that UiAction could put the exception message into the good position.

Another idea is to find the place in Struts, where any exception occurring in a setXxx() method of a form bean can be caught and reported into the form near to the field xxx.

Centralized exception reporting with Java Server Faces

Here is described the solution of the student project TriplePlay in SS 2005.

Der JavaServer Faces Lebenszyklus (siehe http://java.sun.com/j2ee/1.4/docs/tutorial/doc/images/jsfIntro-lifecycle.gif) wird gesteuert von einer Implementation der Schnitstelle Lifecycle (im Paket javax.faces.lifecycle, Refenrenzimplementation von sun unter com.sun.faces.lifecycle.LifecycleImpl).

Beim Start der Webapplikation wird eine LifecycleFactory (ebenfalls im Paket javax.faces.lifecycle, Refenrenzimplementation von sun unter com.sun.faces.lifecycle.LifecycleFactoryImpl) aufgefordert, eine Lifecycle zu liefern. Normalerweise wird die Standardimplementierung genommen. Man hat aber die Möglichkeit, die LifecycleFactory auszutauschen uns somit seine eigene Lifecycle zurück zu liefern.

Werden wir konkret: Hier die zwei Klassen, die wir geschrieben haben. Man hat die Möglichkeit, die von der Referenzimplementation gelieferte Klasse zu spezialisieren, oder die Schnittstelle komplett selbst zu implementieren. Wir wählten die erste Möglichkeit.

package myPackage; //MyLifecycleFactoryImpl.java

public class MyLifecycleFactoryImpl extends  
com.sun.faces.lifecycle.LifecycleFactoryImpl {
  
import javax.faces.FacesException;
import javax.faces.lifecycle.Lifecycle;

public class MyLifecycleFactoryImpl extends  
com.sun.faces.lifecycle.LifecycleFactoryImpl {
  public Lifecycle getLifecycle(final String i_lifecycleId) throws FacesException {
    return new MyLifecycleImpl();
  }
}

/////////////////////////////////////////////////////////////////////
package myPackage; //MyLifecycleImpl.java 

import javax.faces.FacesException;
import javax.faces.context.FacesContext;

public class MyLifecycleImpl extends com.sun.faces.lifecycle.LifecycleImpl  
{
  public void execute(final FacesContext io_facesContext) {
    try {			
      super.execute(io_facesContext);			
    }
    catch(final FacesException e) {
      // handleExceptions here
    }
  }
}
	      

Nun muss man JavaServer Faces noch mitteilen, dass man seine eigene LifecycleFactory-Implementation statt der Standardimplementation verwenden will. Das geschieht über die faces-config.xml (eine Konfigurationsdatei ähnlich wie struts-config.xml)


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE faces-config PUBLIC
           "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
           "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">

<faces-config>

       ...

	<factory>
		<lifecycle-factory>myPackage.MyLifecycleFactoryImpl</lifecycle-factory>
	</factory>

       ...

</faces-config>

        

Damit wird unsere Implementation der LifecycleFactory verwendet, die widerum in der Methode getLifecycle (s.o.) unsere eigene Lifecycle-Implementation zurückliefert.

Falls noch Fragen sind, werde ich gerne versuchen, diese zu beantworten. Ansonsten können Sie gerne meine email-adresse zu diesem kleinen Howto angeben, damit sich künftige Studenten im Fall der Fälle auch an mich wenden können.

Siamak Haschemi, mail: siamak.h (bei) gmx.de

Reporting in background threads

If the run() method of a thread terminates due to an uncaught exception, then by default, this thread will die and the stack trace of this exception will be printed to System.err.

This default behaviour is not satisfactory. At first, we would like to see the exception message chain, too. And secondly, we would really like to see it in the application's user interface, instead of in a may-be suppressed console output.

Last-resort Exception Reporting

In order to report all uncaught exceptions of all background threads, you must place all the threads in your own ThreadGroup. For this ThreadGroup you redefine the method

void uncaughtException(Thread t, Throwable e)
. In this method you can report the exception chain in the format, and to the destination, you want. You should try to report to the user interface (UI), if the thread was created or started by an UI event, e.g. if it implements an action, which would last too long for the user to await its completion.

Exception Reporting in long-living threads

But for a long-living thread, usually serving commands or alike, and not triggered by a user action. we cannot tolerate to stop the thread. Thus such a server thread should catch in its command loop in the run() method any exception and report it to a logging destination.

Also here it would be useful to inform the user, if any exists, that a required service failed, and where is the appropriate logfile.

How to write method bodies in the API layers (TODO)

Until it will be described here, it is quite well described in german language in the technical paper about MulTEx.