Tips & Tricks - Groovy Web-Error-Handler
Foreword
Custom error handlers can be defined in Intrexx. These allow you to provide more specific and expressive error messages for users when errors occur in processes or Velocity scripts.
Error handler file
The user-defined error handlers are located in the portal directory "internal/system/vm/html/errorhandler/custom/". An error handler file must conform to the following conventions:
-
File name begins with "handler_"
-
be followed by a two digit number and an underscore
-
File extension ".groovy"
The folder already contains a sample file with the name "handler_50_xxx.groovy.example". The title between the digits (including underscore) and the file extension is free choice. Error handlers are sorted lexicographically and processed in this order. Processing ends with the first handler that defines a title and optionally a description. Therefore, you should make sure that a check is made as to whether the error should be handled separately before defining the title for the error message in the browser. The example file "handler_50_xxx.groovy.example" shows an exemplary implementation:
/*
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
}
}
With
def bMyError = g_exception.isCausedBy(java.lang.Exception)
a test is performed as to whether the triggering exception is an exception that should be responded to in a user-defined manner. In this case, if the triggering exception is of type "java.lang.Exception", the return value "bMyError" is true. The subsequent if condition is therefore fulfilled. The defined error message us shown in the browser depending on the current language. The following properties can be set to define the error message:
Property |
Description |
title |
Text for the title bar in the browser error message |
description |
Description text in the browser error message |
showEmbedded |
By specifying "true" or "false", you can control whether the actual error message is preceded by another message window. |
The defined properties "title" and "description" are displayed in the browser in the event of an error: The processing sequence defined via the lexicographical order would therefore be aborted at this point after the error message is displayed.
Error handler in use
Exception analysis
To make it easier to find an exception in a stack trace or to search for the exception that caused it, the "g_exception" object contains methods that analyze the error object and return the corresponding results.
g_exception.getCauseOfType(Class)
g_exception.getCauseOfType(String)
In this error object, a search for the first occurrence of the current exception is performed. If the search is successful, the cause is returned, otherwise zero. The error type to be searched for can be transferred as a class object or a string with a fully qualified class name. Example:
g_exception.getCauseOfType("java.io.FileNotFoundException")
g_exception.getRootCause()
Returns the originally underlying exception.
g_exception.isCausedBy(Class)
g_exception.isCausedBy(String)
Checks whether the current exception has the same type as the transferred exception. Returns true if the types match, otherwise false. Example:
def bIsError = g_exception.isCausedBy("java.io.FileNotFoundException")
g_exception.isCausedByAccessControlException()
Checks whether an exception has occurred due to missing access permissions (e.g. data group rights etc.). Returns "true" if this is the case, otherwise "false". Example:
def bNoPermission = g_exception.isCausedByAccessControlException()
Please note that, for all of the methods listed here, the object hierarchies and the inheritance will be taken into account. Thus, the test for a java.io.Exception also includes the test for a java.io.FileNotFoundException, for example.
Contextual handling of exceptions
In the previous examples, exceptions were only checked in general. However, since the same type of exception may occur in different processes or applications, and each of these should be seen individually based on the context, there needs to be a way to make this contextual reference. There are various ways to implement such distinctions. One option is to always provide exceptions with a contextual error message. This means that it is possible to filter for the error message again within the error handler and only start the user-defined processing if the filtering is successful. To provide you with inspiration, we have provided examples of special error codes, application-related abbreviations or similar. Example:
throw new FileNotFoundException("PROJ_VERW_001")
Marks the error message with a contextual abbreviation allowing it to be preselected accordingly:
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
//...
}
}
With the two if queries
if (g_exception.isCausedBy(java.io.FileNotFoundException))
and
if (rootCause.message != null &&
rootCause.message.contains("PROJ_VERW_001"))
the user-defined error handler is only addressed and used if it is a FileNotFoundException and the abbreviation "PROJ_VERW_001" is also present. Other exceptions of this type, but without the code, will not be handled separately. As an alternative to assigning application-related abbreviations, session and / or request values or SharedState variables can be used to realize context-related processing. In the following example, an exception is only handled separately if a user is located within a specific application. This selection is achieved by querying the request variable "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
...
}
}