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 an example file called "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. If the triggering exception has the type "java.lang.Exception", the returned value "bMyError" is true. The subsequent "if" condition is therefore met. 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.



If "true", the message window is displayed and the user-defined error box is only displayed when you click on "More information".

The defined "title" and "description" properties and shown in the browser if an error occurs: The processing order defined by the lexicographical order would therefore be terminated at this point after displaying the error message.

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, then the cause will be returned, otherwise null. 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. This 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 creates an option to filter the error message again within the error handler, and to only start the custom processing if the filter applies. 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 both "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 will only be handled separately if the user is in 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
...
    }
}