Tipps & Tricks - Groovy Web-Error-Handler

Vorwort

In Intrexx können eigene Errorhandler definiert werden. Damit können auftretende Fehler in Prozessen oder Velocity-Skripts genauer spezifiziert und aussagekräftigere Fehlermeldungen für Benutzer bereit gestellt werden.

Errorhandler-Datei

Die benutzerdefinierten Errorhandler befinden sich im Portalverzeichnis /internal/system/vm/html/errorhandler/custom/. Eine Errorhandler-Datei muss folgenden Konventionen genügen: Der Dateiname muss

  • mit handler_ beginnen

  • gefolgt von einer zweistelligen Zahl mit anschließendem Unterstrich

  • die Dateiendung .groovy besitzen

Im Ordner befindet sich bereits eine Beispieldatei mit dem Namen "handler_50_xxx.groovy.example". Der Titel zwischen den Zahlen inkl. Unterstrich und der Dateiendung kann beliebig gewählt werden. Errorhandler werden lexikographisch sortiert und in dieser Reihenfolge abgearbeitet. Die Abarbeitung endet mit dem ersten Handler, der einen Titel sowie optional eine Beschreibung definiert. Es ist also sicherzustellen, dass vor dem Setzen eines Titels für die Fehlermeldung im Browser überprüft wird, ob es sich um einen Fehler handelt, der gesondert behandelt werden soll. Die Beispieldatei handler_50_xxx.groovy.example zeigt eine beispielhafte Implementierung:

/*

	The variable g_exception contains the exception that indicates the error.

	Additional methods added to java.lang.Throwable:

	g_exception.getCauseOfType(SessionException)
	g_exception.getCauseOfType("de.uplanet.lucy.server.session.SessionException")

	g_exception.getCauseOfType(WorkflowException)
	g_exception.getCauseOfType("de.uplanet.lucy.server.workflow.WorkflowException")

	g_exception.isCausedBy(WorkflowException)
	g_exception.isCausedBy("de.uplanet.lucy.server.workflow.WorkflowException")

	g_exception.getRootCause()
	g_exception.isCausedByAccessControlException()
*/


// decide if we must handle the error or not
def bMyError = g_exception.isCausedBy(java.lang.Exception)

if (bMyError)
{
	switch (g_language)
	{
		case "de":
			title        = "Bitte Titel angeben"
			description  = "Bitte Beschreibung angeben. ${g_exception.message}"
			showEmbedded = false // equivalent to showBare = true
			break

		default:
			title       = "Please provide a title"
			description = "Please provide a description: ${g_exception.message}"
			showEmbedded = false // equivalent to showBare = true
			break
	}
}

Mit

def bMyError = g_exception.isCausedBy(java.lang.Exception)

wird getestet, ob es sich bei der auslösenden Exception um eine Exception handelt, auf die benutzerdefiniert reagiert werden soll. Ist in diesem Fall die auslösende Exception vom Typ "java.lang.Exception", ist der Rückgabewert "bMyError" true. Somit ist die anschließende if-Bedingung erfüllt. In Abhängigkeit von der aktuellen Sprache wird die definierte Fehlermeldung im Browser angezeigt. Zur Definition der Fehlermeldung können folgende Eigenschaften gesetzt werden:

Eigenschaft

Beschreibung

title

Text der Titelzeile in der Browser-Fehlermeldung

description

Beschreibungstext in der Browser-Fehlermeldung

showEmbedded

Durch die Angabe von true oder false kann gesteuert werden, ob der eigentlichen Fehlermeldung ein weiteres Hinweisfenster vorgelagert wird. Bei true wird folgendes Fenster angezeigt:

Erst mit Klick auf "Weitere Informationen" wird die benutzerdefinierte Fehlerbox angezeigt.

Die definierten Eigenschaften "title" und "description" werden im Fehlerfall im Browser angezeigt: Die über die lexikographische Ordnung festgelegte Abarbeitungsreihenfolge würde somit an dieser Stelle nach der Anzeige der Fehlermeldung abgebrochen werden.

Errorhandler im Einsatz

Exception-Analyse

Um das Auffinden einer Exception in einem Stacktrace bzw. die Suche nach der auslösenden Exception zu erleichtern, existieren über das g_exception-Objekt Methoden, die das Error-Objekt analysieren und entsprechende Ergebnisse zurückliefern.

g_exception.getCauseOfType(Class)
g_exception.getCauseOfType(String)

Im vorliegenden Error-Objekt wird nach dem ersten Vorkommen der übergebenen Exception gesucht. Ist die Suche erfolgreich, wird die Ursache zurückgeliefert, ansonsten null. Der zu suchende Fehlertyp kann als Class-Objekt oder als String mit einem voll qualifizierten Klassennamen übergeben werden. Beispiel:

g_exception.getCauseOfType("java.io.FileNotFoundException")
g_exception.getRootCause()

Liefert die ursprünglich zugrunde liegende Exception zurück.

g_exception.isCausedBy(Class)
g_exception.isCausedBy(String)

Hiermit kann überprüft werden, ob die vorliegende Exception vom Typ der übergebenen Exception ist. Liefert true zurück, wenn die Typen übereinstimmen, ansonsten false. Beispiel:

def bIsError = g_exception.isCausedBy("java.io.FileNotFoundException")
g_exception.isCausedByAccessControlException()

Überprüft, ob eine Exception aufgetreten ist, die ihre Ursache in einer fehlenden Zugriffserlaubnis hat (z.B. Datengruppenrechte usw.). Liefert true zurück, wenn dies der Fall sein sollte, ansonsten false. Beispiel:

def bNoPermission = g_exception.isCausedByAccessControlException()

Beachten Sie, dass bei allen hier aufgeführten Methoden die Objekthierarchien und die Vererbung berücksichtigt werden. So beinhaltet beispielsweise der Test auf eine java.io.Exception auch den Test auf java.io.FileNotFoundException.

Exceptions kontextbezogen verarbeiten

In den bisherigen Beispielen wurden Exceptions lediglich im Allgemeinen überprüft. Da jedoch derselbe Exceptiontyp in unterschiedlichen Prozessen oder Applikationen auftreten kann und je nach Kontext differenziert betrachtet werden können soll, muss eine Möglichkeit gegeben sein, diesen Kontextbezug herzustellen. Um solche Unterscheidungen zu realisieren, existieren diverse Möglichkeiten. Eine besteht darin, Exceptions immer mit einer kontextbezogenen Fehlermeldung zu versehen. Somit besteht innerhalb des Errorhandlers die Möglichkeit, nochmals auf die Fehlermeldung zu filtern und erst bei erfolgreicher Filterung die benutzerdefinierte Abarbeitung zu starten. Als Anregung seien hier z.B. spezielle Fehlercodes, applikations-bezogene Kürzel oder Ähnliches genannt. Beispiel:

throw new FileNotFoundException("PROJ_VERW_001")

Somit ist die Fehlermeldung mit einem kontextbezogenen Kürzel gekennzeichnet und kann anschließend entsprechend vorselektiert werden:

if (g_exception.isCausedBy(java.io.FileNotFoundException))
{
    def rootCause = g_exception.getRootCause()

    if (rootCause.message != null && 
        rootCause.message.contains("PROJ_VERW_001"))
        bMyError = true
    else
        bMyError = false
}

if (bMyError)
{
    switch (g_language)
    {
//...
        case "de":
            title        = "Projektverwaltung Fehler 001"
            description  = "Pflichtenheft-Datei wurde nicht gefunden."
            showEmbedded = false
            break
//...
    }
}

Durch die beiden if-Abfragen

if (g_exception.isCausedBy(java.io.FileNotFoundException))

und

if (rootCause.message != null && 
    rootCause.message.contains("PROJ_VERW_001"))

wird der benutzerdefinierte Errorhandler nur dann angesprochen und verwendet, wenn es sich um eine FileNotFoundException handelt und darüber hinaus das Kürzel PROJ_VERW_001 vorhanden ist. Andere Exceptions dieses Typs ohne Kürzel werden nicht gesondert behandelt. Als Alternative zur Vergabe applikationsbezogener Kürzel kann mit Hilfe von Session- und/oder Requestwerten oder auch SharedState-Variablen gearbeitet und somit eine kontextbezogene Verarbeitung realisiert werden. Im folgenden Beispiel wird eine Exception nur dann gesondert behandelt, wenn sich ein Benutzer innerhalb einer bestimmten Applikation befindet. Erreicht wird diese Selektion über die Abfrage der Requestvariable rq_AppGuid.

def bMyError = false

if (g_exception.isCausedBy(java.io.IOException) && 
    g_request["rq_AppGuid"] == "93BDFE88ADC4BAAB66630548C61B8ADD6F287AA6")
    bMyError = true

if (bMyError)
{
    switch (g_language)
    {
...
        case "de":
            title        = "Projektverwaltung"
            description  = """Es ist ein Fehler in der Applikation Projektverwaltung aufgetreten."""
            showEmbedded = false
            break
...
    }
}