Chapter 18: Exception Handling Frameworks

Exception handling is the mechanism by which objects raise and trap errors. This chapter shows you how you can use this mechanism in the objects you write.

All the code shown in this chapter uses the Micro Focus alternatives to the ISO 2002 syntax.

Overview

Exception handling gives you a flexible mechanism for handling errors in OO COBOL programs. When an object traps an error, it raises an exception. Each different kind of error is represented by a different exception number.

Objects in the supplied class libraries react to error conditions by raising exceptions. The Class Library has a set of exception numbers, and a file of error messages. You can define your own exception conditions for objects you create, associating each set of exception conditions with its own error messages file.

An object raises an exception by sending itself the "raiseException" message, passing an exception number as a parameter. The Base class implements the "raiseException" method, so it is inherited by all objects. Exception handlers trap errors raised by objects through the "raiseException" mechanism; they do not trap run-time system errors.

The ExceptionManager class in the Base Class Library provides most of the logic for actually handling exceptions, including a system exception handler. The system exception handler displays an error message and shuts down the application when an object raises an exception, but an application can define its own exception handlers and attach them to individual classes or objects to redefine their behavior.

You can replace the system exception handler with a system exception handler of your own.

Creating an Error Message File

Create a file with the extension .err, using the Editor. It should start with the statements:

$quote x

$set 1

where x is the character you wish to use as a quote character. Then, write each error message in the format:

nnnnn  "error-message-text" 

where:

nnnnn is the exception number

"error-message-text" is the text of the error message you wish to output for this exception number.

The double-quote character (") is assumed to be the quote character set in the $quote statement above. You can change quote statements at any point during the .err file; each change applies to all the messages which follow it up until the next $quote statement. You can also enter comments, by starting a line with a $ followed by a space. Here is an example:

$quote "

$
$set 1
1 "Invalid key type"
2 "Failed to open file for dictionary"
3 "Using a key of different size"
4 "Record exceeds maximum allowed length"
5 "Error reading control record"
6 "Indexed file has wrong key structure"
7 "Key not found in file"
8 "Attempt to write a record which has been deleted"
 ...
$ ************************************************************
$ Last modified on 95/08/01
$ ************************************************************

Net Express :
Next, add the .err file to the project for your application. The IDE compiles this to a .lng file.

Right-click on the .lng file, and package it as a Micro Focus library (.lbr) file. Name the library file filenamedf.lbr. For example, an error message file you refer to in your source code as myerrors has the filename myerrorsdf.lbr.

Server Express:
Run the shell script, mfmsg to create a .lng file and copy it into the message directory structure under $COBDIR/lang/. You need to be logged in as the superuser to run this script successfully. To run the shell script:

mfmsg output.lng input.err


Note: Earlier versions of OO COBOL only supported a single user error file, user.err, with exception numbers starting at 10,000. The ExceptionManager did not require you to register this file. This mechanism still works for backwards compatibility; any exception numbers in the range 10,000 to 65535 are assumed to refer to messages in user.err. All new error message files should use exception numbers starting from 1, and should be registered explicitly with the ExceptionManager as described in this chapter.


Registering an Exception Message File

Before an object can raise any exceptions, an error message file must have been registered with the ExceptionManager. When you register a message file with the ExceptionManager, it returns an error file offset. This is an integer number which uniquely identifies the file.

Any number of classes or objects can share the same error message file, and it doesn't matter if the file is registered more than once; the ExceptionManager will always return the same offset value during a particular run of an application.

The offset value returned depends on how many other message files have been previously registered during the application run. Never hard-code an error file offset value into your classes; always query the ExceptionManager for the value.

To register an error file:

invoke ExceptionManager "registerMessageFile"
                         using library errfile
                     returning anOffset

where:

library Name of the library containing the error file
errfile The name of the file containing your error messges.
anOffset PIC X(4) COMP-5.

The error file offset value.

Server Express:
The library name is ignored by the mechanisms which find the message file. It is still used by the ExceptionManager as part of the registration of the message file. You must use the same value for both parameters when you send a "queryMessageFile" message to the ExceptionManager referring to the same file. Although the first parameter is not strictly needed on Server Express, including it as part of the method interface keeps source code compatibility between Server Express and Net Express.

The example below is a method that registers an error message file with the ExceptionManager.

 method-id. "registerFile". 
 
 local-storage section. 
 01 errorFile            pic x(8)
 01 libraryFile          pic x(6)
 01 anOffset             pic x(4) comp-5. 
 
 procedure division. 
     move spaces to libraryFile, errorFile
     move "mylibf" to libraryFile
     move "err-file" to errorFile
     invoke ExceptionManager "registerMessageFile"
                                using libraryFile errorFile
                            returning anOffset
     exit method.
 end method "registerFile". 

Raising an Exception

Whenever a method detects an error (for example, a value out of range) it should raise an exception. Each different exception has an exception number relating it to a message in an error message file.

Before raising the exception, you add the error file offset to the exception number to get a unique exception ID. The object raising the exception may not be the object which registered the error message file, so it can query the ExceptionManager to get the offset for its associated error file. It then raises the exception by sending itself the "raiseException" message.

The "raiseException" method is implemented in the Base class and is inherited by all subclasses. If no exception handler is registered for the object, the system exception handler displays the exception number and error message, then terminates the application.

If you have registered an exception handler for the object, execution returns to the statement following the "raiseException" after your exception handler has been invoked.

In order of descending priority, the ExceptionHandler will try to call exception handlers registered with:

  1. The object instance.

  2. The class of the object.

  3. The system exception handler.

To raise an exception:

  1. Get the error message file offset for this type of object, by sending the "queryMessageFile" to the ExceptionManager

  2. Calculate the exception ID by adding the exception offset to the exception number.

  3. Send the "raiseException" message to self using the exception ID as a parameter.

The basic "raiseException" message doesn't allow you to supply any information about an error apart from the exception number. The following variations on "raiseException" enable you to send text with the exception number to provide extra information:

"raiseExceptionWithText" Enables you to pass up to six CharacterArrays as parameters with the exception number.
"raiseExceptionWithTextZ" Enables you to pass up to six null-terminated strings as parameters with the exception number.
"raiseExceptionWithTextCollection" Enables you to pass an OrderedCollection of CharacterArrays with the exception number.

The code fragment below is an example of how to raise an exception:

*    Get the error offset. 

     invoke ExceptionManager "queryMessageFile" 
                                using "mylibf"
                                      "err-file"
                            returning errorOffset
*    Calculate the exception ID
     add errorOffset to errorNumber giving exceptionId
*    Raise the exception
     invoke self "raiseException" using exceptionId
                           returning anObject

ErrorOffset, ErrorNumber and ExceptionId are all declared as PIC X(4) COMP-5.

Providing Your Own Exception Handlers

An exception handler is a CallBack to a method which knows how to deal with the exception. When the object raises an exception your exception handling method is invoked. To create an exception handler, you need to:

Writing Exception Handler Methods

An exception method enables you to handle an error raised by a COBOL object in the way that best suits the application. By registering your exception method against a class or instance object, you ensure that it gets invoked whenever those objects raise exceptions.

An exception method is passed the handle to the object that raised the exception, and the exception ID. The exception method needs to calculate the exception number from the exception ID. To do this, the exception method does the following:

  1. Queries the ExceptionManager for the exception offset for the error message file. The exception method either needs to know the name of the error message file, or the object raising the exception must implement a method to return it.

  2. Subtracts the error file offset from the exception ID to get the exception number.

Once the exception method has the exception number, it should test it to see if it has a value it expects. If it doesn't recognize the error, it should reraise the exception. The exception can then be trapped either by an exception handler that knows what to do with it, or by the system exception handler, which terminates your application.

The code below is an example of an exception handling method:

  method-id. "exceptionMethod".
 local-storage section. 

 01  errorNumber             pic x(4) comp-5. 
 01  lsOffset                pic x(4) comp-5. 

 linkage section
 01  anObject                object reference.
 01  anExceptionId           pic x(4) comp-5. 
 01  aDataItem               object reference.
 01  aTextCollection         object reference.

 procedure division using anObject 
                          anExceptionId 
                          aTextCollection
                returning aDataItem. 
*    Calculate the exception number
     invoke ExceptionManager "queryMessageFile"
                                 using "err-lib" 
                                       "err-file"
                             returning lsOffset
     subtract lsOffset from exceptionId giving errorNumber
*    process the error 
     evaluate errorNumber
         when knownError1 
*    Handle the error
         when knownError2
*    Handle the error
         when other
*    Unknown error, so reraise the exception. 
         invoke self "raiseExceptionWithTextCollection" 
                             using anExceptionId 
                                   aTextCollection
                         returning aDataItem
     end-evaluate

     exit method.
 end method "exceptionMethod". 

where:

anObject is the object which originally raised the error.
anExceptionId is the number representing the error raised.
aDataItem is an object returned by your exception method. This enables you to return a default parameter from a method which has raised an exception, provided the method is coded to pass back the parameter it receives from the "raiseException" method.

Registering an Object with the Exception Handler

To register an exception handler against an object:, you invoke the "register"method of the ExceptionManager class:

 invoke ExceptionHandler "register" using anObject
                                          anExceptionMethod

where anObject is the object reference to the object you want to register, and anExceptionMethod is a Callback to your exception method. For more information about Callbacks see the chapter Callback Frameworks.

Canceling an Exception Registration

To cancel the registration of an object with the ExceptionHandler, send it the "cancel" message:

 invoke ExceptionHandler "cancel" using anObject 
                              returning aBool

where anObject is the object to deregister, and aBool is a PIC X COMP-5. It contains the value 1 if the deregistration is successful.

Replacing the System Exception Method

The system exception method is an exception method registered for the whole system. It is invoked whenever an object not explicitly registered with the exception handler raises an exception. The default behaviour is to display the exception number with an error message, then end the program.

You can replace the system exception method with your own. Create a Callback for an exception method. Then send the "setSystemHandler" message to the ExceptionManager.

The "setSystemHandler" method returns you a handle to a CallBack for the existing system exception handler. If your replacement system exception method gets an exception it does not know how to handle, reraise the exception, and the default SystemHandler will actually still get invoked.

The example below shows you how to replace the system exception method:

invoke ExceptionManager "setSystemHandler" using newHandler
                                       returning oldHandler

where:

newHandler is the object handle to the Callback for your exception method.
oldHandler is the object handle to the Callback for the system exception method you have replaced.


Copyright © 2004 Micro Focus International Limited. All rights reserved.