A resource error is an error that appears at runtime due to the unavailability or the malfunction of a resource needed by the application. A resource error disrupts the normal operation of an application.
Examples of resource errors are:
a file cannot be accessed because it has been deleted or locked by another process
a network connection is unavailable
a database error occurs suddenly due to a shutdown of the database server
Although program errors and resource errors both create problems at runtime, there is an important difference between these two kinds of runtime errors. As explained in a previous section, program errors are unintentionally introduced by the programmer; therefore they cannot be anticipated and they appear randomly at any location in the source code. On the other hand, resource errors can very well be anticipated, because they can only appear when a resource is used in a script. Hence, a programmer should always consider the case of a resource error, and handle it appropriately, especially if he or she wants to write robust and reliable, or fault-tolerant software. The following section explains how to do this.
Whenever an application is executed, scripts call other scripts. For example, a main script script1 calls script2, then script2 calls script3, and script3 calls script4. A script that calls another script is frequently named the client script, and the called script is then named the supplier script.
Now suppose that a supplier script needs a resource to properly execute its task. What should this script do in case of a resource error? This question is similar to asking: What should a worker do if he has no more bricks to build a wall? In practice he will just tell his superior who will then decide about what to do. The same is true for any supplier script. A supplier script cannot know how to handle the error in a way that would suit every client script. The best it can do is to simply return information about the error. The client script then handles the error in any way that is appropriate for the given case. If it cannot handle it itself, then it also just returns the error to its client script (that's like a superior who informs his superior about the lack of bricks). Thus, the error can bubble up the called scripts, until there is a client script that handles the error.
A concrete example would be a client script that calls a supplier script to get current exchange rates. If the supplier does this by retrieving data over the net (e.g. using a webservice), and the net is not available, then it just informs the client script about the failure. The client script might then try to retrieve the data from a local file that stores the most recent data successfully retrieved from the net, possibly by calling another supplier script. If this also fails, the script returns an error to its client script, informing it about the failure to get the exchange rates.
The recommended way to pass information about a resource error from a supplier to a client script is to provide an output argument in the supplier script that returns an error in case of a failure. The identifier of this output argument is typically o_error and its type is a child-type of ty_resource_error. In case of a failure, o_error contains an object describing the problem, and if the operation is successful, o_error is void. Therefore property voidable must explicitly be set to yes. Thus, the typical definition of an output argument that returns a resource error looks like this:
out error type:resource_error voidable:yes end
It goes without saying that each client script should always consciously check the state of any error returned by a supplier script. The examples in the next section show how to do this.
![]() | Note |
|---|---|
In this context it is useful (and amusing) to mention command number 6 of The Ten Commandments for C Programmers, written in Old English by Henry Spencer (see annotated version at http://www.lysator.liu.se/c/ten-commandments.html; it's really worth a reading): [If a function be advertised to return an error code in the event of difficulties, thou shalt check for that code, yea, even though the checks triple the size of thy code and produce aches in thy typing fingers, for if thou thinkest "it cannot happen to me", the gods shall surely punish thee for thy arrogance.] |
An interesting question is: What happens if the lazy or sloppy programmer does not check for errors.
In most cases, a program error will occur soon or later. But there is no guarantee for a program error. The consequence can also be a wrong result stored in a database, and discovered only after the software has been shipped to 1000000 customers! If the programmer is lucky, a program error will appear immediately, so that he or she is forced to improve the code. (Remark: the programmer really has to be lucky, because resource errors tend to appear less often during the development phase, in which the programmer has set up a much less error-prone environment (e.g. local files instead of files on a remote server, local webservice instead of a 'real' webservice, etc.)). For example, if the result of a command is a string object that is void in case of an error, then a 'void object' program error appears immediately if the program immediately tries to execute a feature on the string, for example a conversion to lowercase letters. But if the result is simply stored to a database field that allows void values, then the error might stay undiscovered until detected by an unlucky user.
Suppose a service command is used to write a date to a text file in a standardized ISO 8601 format (e.g. 2008-01-18). The command has two input arguments: the date to be stored, and the file handle to which the date is to be stored.
As a file operation can always fail, the command also has an output argument that returns a file_error object in case of a failure.
This leads to the following code:
service error_handling_examples
command store_date_to_file
in date type:date end // the date to be stored
in new_or_existing_file type:file_handle end // the file used to store the date
out error type:file_error voidable:yes end // returns any file problems encountered
script
o_error = i_date.co_to_standardized_string.co_store_to_new_or_existing_file ( i_new_or_existing_file )
end
end
end serviceThe script converts the date to a standardized string, and then uses the string's store_to_new_or_existing_file command to store the date as text in a file. In case of an error returned by store_to_new_or_existing_file, this error is simply passed to the client of our service command.
Let's now create another service command that makes a backup of some data, and then stores the date of the last backup in a text file. We can do this by using the previously defined store_date_to_file service command. In case of a failure to store the last backup date in a file, we simply display an error message on the system console:
service error_handling_examples
// this attribute defines the file used to store the date of last backup
attribute last_backup_date_file type: file_handle &
default: fa_file_handle.co_create ( '''c:\temp\last_backup_date.txt''' ) end
command do_backup
script
// do backup (instructions not shown here)
// ...
// now store date of this backup in text file
// get today's date
const date last_backup_date = se_date.current_date
// use 'co_store_date_to_file' to store the date
// in case of a failure, 'error' holds the error returned by 'co_store_date_to_file'
const file_error error = co_store_date_to_file ( &
date = c_last_backup_date &
new_or_existing_file = a_last_backup_date_file )
// display a message in case of an error
if c_error #r void then
console.message ( """Last backup date ({{c_last_backup_date.to_string}})
could not be stored to file
"{{a_last_backup_date_file.to_string}}"""" )
end if
end
end
end serviceIn case of an error, the following message is displayed:
Last backup date (Jan 18, 2008) could not be stored to file "c:\temp\last_backup_date.txt"
As we defined a store_date_to_file service command, it is logical to also define a counterpart, namely command restore_date_from_file
This command has one input argument which is the file from which the date is to be restored.
The date restored from the file is returned with output argument result. Because reading from a file can fail at runtime, we add a second output argument, error, to signal any file problems to the client script. Each time this command is executed, there are two possible outcomes:
the operation succeeded: o_result contains the date restored from file, and o_error is void
the operation failed: o_result is void, and o_error contains the error encountered with the attempt to read the file
This condition can easily be expressed with an exclusive or (xor) operator. Either o_result or o_error is different from void, but not both. Hence we use contract programming to ensure this condition (see Chapter 17, Contract programming for more information about contract programming.
Thus, the code looks like this:
service error_handling_examples
command restore_date_from_file
in existing_file type:file_handle check: i_existing_file.exists end // the file in which the date is stored
out result type:date voidable:yes end // the date read from the file
out error type:runtime_error voidable:yes end // any problem encountered with file reading
out_check check: i_result #r void xor i_error #r void end // exactly one output argument must be void
script
// store the content of a text file into a 'string' object
var string date_in_file // holds the string read from file
fa_string.co_create_from_text_file ( i_existing_file ) &
( v_date_in_file = o_result &
o_error = o_error )
// if the operation failed, leave immediately
// ('o_error' contains the error reported by 'fa_string.co_create_from_text_file')
if o_error #r void then
exit script
end if
// a string could be read from the file
// now this string must be converted into a 'date' object
// first, we check if the string really is a formatted date
// (we use 'se_date.co_is_string_standardized_date' to do this)
if se_date.co_is_string_standardized_date ( v_date_in_file ) then
// ok. the string can be converted into a 'date' object
// the date is returned in 'o_result'
o_result = fa_date.co_create_from_standardized_string ( v_date_in_file )
else
// not ok. the string is not a valid date
// we return a 'string_to_object_conversion_error' error
o_error = fa_string_to_object_conversion_error.co_create ( &
description = "Invalid date format" &
string = v_date_in_file &
error_position = void &
type_of_object = "date" )
end if
end
end
end serviceTo use the above command we will now create another command that simply displays the date of the last backup on the system console. The code shown below should be self-explanatory:
service error_handling_examples
command display_last_backup_date
script
// first we check the existence of the file
if not a_last_backup_date_file.exists then
console.message ( """Last backup date is unknown, because file "{{a_last_backup_date_file.to_string}}" doesn't exist!""" )
exit script
end if
// use 'co_restore_date_from_file' to read date from file
var date last_backup_date
var runtime_error error
co_restore_date_from_file ( a_last_backup_date_file ) &
( v_last_backup_date = o_result &
v_error = o_error )
// check if the operation succeeded
if v_error =r void then
// ok. no problem.
console.message ( "Last backup was done on " & v_last_backup_date.to_string )
else
// problem!
console.message ( """Last backup date could not be defined!
The following error occured when reading from file "{{a_last_backup_date_file.to_string}}":
{{v_error.to_string}}""" )
end if
end
end
end service