User Guide - Error Messages

# Error Messages

The Library deals with two categories of errors: user errors and application errors. The former are all the errors that can occur when the Library is used in a "real" environment where network connections can disappear, disks can get full, and memory can get exhausted. The letter type of errors are at a lower level including invalid arguments passed to procedures, missing function calls, or any other application misbehavior. In this section we will examine the Library Error Manager which handles user errors. In a later section we will have a look at how to control the application errors at an application debug level using trace messages.

As a part of the core Library, the error object is intended to pass information about errors and messages occuring in the Library back to the application. Each error is kept as an object so multiple errors can be nested together using the well-known HTList object. Nested error management can be used to build complicated error messages which an arbitrary level of details, for example:

    	This URL could not be retrieved: http://www.foo.com
    	    Reason: The host name could not be resolved
    	        Reason: DNS service is not available

The principle behind the error manager is exactly like any other registration module in the Library in that it creates an object and binds it to a list that the caller provides. Often, errors are related to a specific request object and each request object will therefore keep its own list of errors. However, errors can also be maintained as separate lists which are not directly related to a request, for example, the application can keep its own list of errors independent of any Library errors.

Errors are roughly categorized into two classes: system errors and other errors. System errors include all errors that occur while interacting with the operating system. Often these errors occurs as a result of insufficient availability or authentication to a system resource. In many operating systems, the system provides a set of error messages which is associated with an error code made available to the application via the errno variable or equivalent. All other errors are registered with an error message belonging to the Library Error manager. Note, that there are no difference in how system errors and other errors are treated, they are the same data objects and can be registered together with no exception.

## Registering Errors

Now let's take a look at how a generic error list is maintained. Normal errors can be registered using the following function:

    extern BOOL HTError_add		(HTList *	list,
    				 HTSeverity	severity,
    				 BOOL		ignore,
    				 int		element,
    				 void *		parameter,
    				 unsigned int	length,
    				 char *		where);

The first argument is a list object and as always, we need to create a list object using the `HTList_new` method. The next element is an indication of how serious the error is in the situation where it occured. Classification of errors are known from many operating systems, for example VMS, and it gives the application the opportunity to decide whether the current operation should be continued or aborted. The Library provides four severity categories:

    typedef enum _HTSeverity {
        ERR_FATAL,
        ERR_NON_FATAL,
        ERR_WARN,
        ERR_INFO
    } HTSeverity;

It is not always that an error is an error immediately when it occurs. In some situations it might first become an error later in the process depending on the outcome of other factors - or it might be circumvented so that no special action is required. The ignore flag provides this functionality in that an error can be registered at any time with the notion: "Register this error but ignore it for now".

The `element` argument is an index into a table of all error messages. This table is maintained in the HTError Module and contains an error message together with a URl that might be included in an error message presented to the user. The values of the `element` argument itself is given by the `HTErrorElement` enumeration definition in the HTEvntrg Module.

The next two arguments are used to register any parameters associated with the error. This can for example be the file name of a file which could not be opened, a URL which could not be accessed etc. By letting the parameter be a void pointer together with a length indication, the parameter can be an arbitrary data object. The last argument is a location description to indicate where the error occured. Often this is the name of the function or a module.

One thing, we didn't mention when describing the request object was that the Request Object provides a similar function for directly associating an error object with a request object. These functions uses request objects and not a list as the basic data object and hence the caller does not have to worry about creating or assigning the list to the request object; this is done automatically. The request version of how to register an error looks very much like its more generic companion, and it should not be necessary to explain the arguments any further.

    extern BOOL HTRequest_addError (HTRequest * 	request,
    				HTSeverity	severity,
    				BOOL		ignore,
    				int		element,
    				void *		par,
    				unsigned int	length,
    				char *		where);

System errors can be registered in very much the same way as described above, but the set of parameters is a bit smaller and hopefully a bit easier to handle. The registration function is defined as:

    extern BOOL HTError_addSystem (HTList *		list,
    			       HTSeverity 	severity,
    			       int		errornumber,
    			       BOOL		ignore,
    			       char *		syscall);

The only difference is the `errornumber` argument which, as described above, in many situations is provided by the operating system, for example as a errno variable. The `syscall` is simply the name of the function. Also this function has a mirror function in the HTRequest object, and again they look very much alike:

    extern BOOL HTRequest_addSystemError (HTRequest * 	request,
    				      HTSeverity 	severity,
    				      int		errornumber,
    				      BOOL		ignore,
    				      char *		syscall);

Let's take a look at two examples of registering errors. The first example registers an informational error message explaining that the HTTP module received a redirection notification from the remote HTTP server. The first example uses the Request versions of the error registration functions, and the second example uses the generic versions:

    BOOL HTTPRedirect (HTRequest * request, int status, char * location)
    {
        if (location) {
    	if (status == 301) {
    	    HTRequest_addError(request, ERR_INFO, NO, HTERR_MOVED,
    			       location, strlen(location), "HTTPRedirect");
    	} else if (status == 302) {
    	    HTRequest_addError(request, ERR_INFO, NO, HTERR_FOUND,
    			       location, strlen(location), "HTTPRedirect");
    	}
    	return YES;
        } else {
    	HTRequest_addError(request, ERR_FATAL, NO, HTERR_BAD_REPLY,
    			   NULL, 0, "HTTPRedirect");
    	return NO;
        }
    }

The second example shows how to register a system error:

    BOOL HTReadDir (HTRequest * request, const * directory)
    {
        DIR *dp;
        if ((dp = opendir(directory))) {
    	STRUCT_DIRENT * dirbuf;
    	while ((dirbuf = readdir(dp))) {

    	    /* Read Directory */

    	}
    	closedir(dp);
    	return YES;
        } else {
    	HTError_addSystem(errorlist,  ERR_FATAL, errno, NO, "opendir");
    	return NO;
        }
    }

## Error Messages

Until now we have concentrated on how to register a set of errors in a list and how to associate errors with a request object. Another important thing about errors is that they often are to be presented to the user. The error manager can be configured to show almost any combination of the parameters in an error object, and all the flags are put together in a big enumeration:

    typedef enum _HTErrorShow {
        HT_ERR_SHOW_FATAL,		/* Show only fatal errors */
        HT_ERR_SHOW_NON_FATAL,	/* Show non fatal and fatal errors */
        HT_ERR_SHOW_WARNING,	/* Show warnings, non fatal, and fatal errors */
        HT_ERR_SHOW_INFO,		/* Show all of errors */
        HT_ERR_SHOW_PARS,		/* Show any parameters (if any) */
        HT_ERR_SHOW_LOCATION,	/* Show the location where the error occured */
        HT_ERR_SHOW_IGNORE,		/* Show errors even if they are ignored */
        HT_ERR_SHOW_FIRST,		/* Show only the first registered error */
        HT_ERR_SHOW_LINKS		/* Show any HTML links (if any) */
        HT_ERR_SHOW_DEFAULT,	/* Default level of details *
        HT_ERR_SHOW_DETAILED,	/* Somewhat detailed level */
        HT_ERR_SHOW_DEBUG,		/* Very detailed */
    } HTErrorShow;

The last three entries in the enumeration list are only for the convenience of the application. They provide some useful default values for how error messages can be presented to the user. The setup can be modified using the following functions:

    extern HTErrorShow HTError_show (void);
    extern BOOL HTError_setShow (HTErrorShow mask);

The actual generation of error messages often involves a platform dependent interface including special windows etc. In order to keep the error manager itself completely platform independent, the error presentation functionality is part of the Messaging Module which is described in detail later in this guide.

## Data Methods

The Error manager contains a large set of configuration options and methods for accessing information about registered lists. This guide is not intended to describe every single public function, so here we will only present the methods in a list. Often they are self explanatory, so you can probably get a clue of what is going on anyway!

    extern BOOL HTError_doShow		(HTError * info);
    extern BOOL HTError_ignoreLast		(HTList * list);
    extern BOOL HTError_setIgnore		(HTError * info);
    extern int HTError_index		(HTError * info);
    extern HTSeverity HTError_severity	(HTError * info);
    extern int HTError_parameter		(HTError * info, void *parameter);
    extern CONST char * HTError_location	(HTError * info);

## Cleaning up Errors

In case you are using the generic error interface (`HTError_add` and `HTError_addSystem`), the cleanup is done exactly like for all other list based registration mechanisms in the Library. In case you are using the request specific version, the request manager both handles creating and deletion of error lists, so you do not have to do anything. The generic interface for cleaning up looks like:

    extern BOOL HTError_deleteAll (HTList * list);
    extern BOOL HTError_deleteLast (HTList * list);

In the next section, we will see how to display errors in the application along with other user information such as progress reports etc.