Tips & Tricks - Groovy Web Error Handler

Foreword

In Intrexx, you can define your own error handlers. This allows errors that occur in processes or Velocity scripts to be specified more precisely and provides users with more meaningful error messages.

Error Handler File

The custom error handlers are located in the portal directory "internal/system/vm/html/errorhandler/custom/". An error handler file must comply with the following conventions:

  • The file name begins with "handler_"

  • followed by a two-digit number with an underscore at the end

  • File extension ".groovy"

The folder already contains a sample file named "handler_50_xxx.groovy.example". The title between the numbers—including the underscore and the file extension—can be chosen freely. Error handlers are sorted lexicographically and processed in that order. The processing ends with the first handler that defines a title and, optionally, a description. Therefore, before setting a title for the error message in the browser, you must verify whether the error requires special handling. The sample file "handler_50_xxx.groovy.example" shows an example 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)
        

The system checks whether the triggering exception is one for which a user-defined response is required. If, in this case, the thrown exception is of type "java.lang.Exception," the return value of "bMyError" is true. Thus, the subsequent if condition is satisfied. Depending on the current language, the defined error message is displayed in the browser. The following properties can be set to define the error message:

Property

Description

title

Text of the title in the browser error message

description

Description text in the browser error message

showEmbedded

By specifying "true" or "false," you can control whether an additional information window appears before the actual error message.



If set to "true," the notification window appears, and the custom error box is displayed only after the user clicks "More Information."

If an error occurs, the defined "title" and "description" properties are displayed in the browser: The processing order determined by lexicographic order would therefore be interrupted at this point after the error message is displayed.

Error Handlers in Action

Exception Analysis

To make it easier to locate an exception in a stack trace or to find the exception that triggered it, the "g_exception" object provides methods that analyze the error object and return the corresponding results.

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

        

This error object searches for the first occurrence of the passed exception. If the search is successful, the cause is returned; otherwise, zero is returned. The type of error to search for can be passed as a class object or as a string containing a fully qualified class name. Example:

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

        

Returns the original underlying exception.

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

        

This allows you to check whether the current exception is of the same type as the exception passed in. 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 that is caused by a lack of access permissions (e.g., data group permissions, etc.). Returns "true" if this is the case; otherwise, "false". Example:

            def bNoPermission = g_exception.isCausedByAccessControlException()
        

Please note that all the methods listed here take object hierarchies and inheritance into account. For example, the test for a java.io.Exception also includes the test for java.io.FileNotFoundException.

Handling Exceptions in Context

In the examples so far, exceptions have only been checked in general terms. However, since the same exception type can occur in different processes or applications and should be considered differently depending on the context, there must be a way to establish this contextual relationship. There are various ways to implement such distinctions. One is to always include a context-specific error message with exceptions. This means that within the error handler, it is possible to filter the error message again and start the user-defined processing only after the filter has succeeded. As a suggestion, examples include specific error codes, application-specific abbreviations, or similar items. Example:

            throw new FileNotFoundException("PROJ_VERW_001")
        

Thus, the error message is marked with a context-specific abbreviation and can then 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
//...
    }
}

        

Through the two if statements

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

        

and

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

        

The custom error handler is called and used only if a FileNotFoundException occurs and the code "PROJ_VERW_001" is also present. Other exceptions of this type that do not have an abbreviation are not treated separately. As an alternative to assigning application-specific identifiers, session and/or request values—or even SharedState variables—can be used to implement context-sensitive processing. In the following example, an exception is handled separately only if a user is within a specific application. This selection is achieved by querying the "rq_AppGuid" request variable.

            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
...
    }
}