This tutorial covers exception basics, the key parts of the As2lib Exception API (org.as2lib.env.except) and how they improve your application and help you during development on your Flash application.
Note that this tutorial has also been published on www.as2lib.org: As2lib Exception API.
Exception Basics
What exactly is an exception and what problems does it solve? Which features make up a good exception API? What are good practices when working with exceptions and to what issues do I have to pay special attention to?
Programmers try to avoid the potential of errors as good as they can; but unfortunately errors nevertheless occur. Thus handling errors is an important issue when writing applications. If errors are properly handled you improve your program in readability, reliability and maintainability. But, to handle errors properly you need a good underlying error handling API. While the ActionScript 2.0 programming language provides needed key constructs like try..catch..finally-blocks and a throw-statement it lacks a good API.
The only help Macromedia’s Error API offers is a class “Error” which can be passed a message and which returns that message on an invocation of its “toString” method. This is definitely to less to make up a good error handling API. Missing features are:
- A stack trace that tells at least who, which class and method, threw the error and which parameters were passed-to the method when it threw the error. In the best case, this stack trace contains the whole method execution sequence beginning at the method that threw the error and ending at the application’s entry point.
- The ability to assign cause-errors. These are catched errors that themselves caused other errors to be thrown.
- At least two types of exceptions that differ in fatality. The less fatal exception may be caused by a wrong user input and the fatal exception indicates a programming error.
These are the features of the As2lib Exception API. With the term exception we refer to an error or better an exceptional event. This means an exception is an event that occurs during the execution of an application that disrupts its normal flow. Note that this does not mean that the exception will break the application. The exception should and must be handled somewhere in the application before this happens. At best the application will then recover itself and at worst it prints an error message to the user. With fatal exceptions indicating programming errors you may also think about directly informing the developer about it.
When you use exceptions in your application you should pay special attention to the following issues:
- Make your exception informative by providing a detailed message intended for developers that includes as much context information as possible and maybe even information on how to resolve the problem. You may also provide an error code either as string or number that can be used to obtain an error message to display to the user.
If you want to include the content of a variable or method etc. specify its name surrounded by single quotation marks “‘”, followed by the string representation of its content surrounded by a squared open “[” and close “]” brace, for example “… ‘myVariable’ [itsContent] …”. - Create a base exception per module of you application or event per package. In an application these base exceptions are in most cases normal, not fatal, exceptions. They enable you to catch exceptions at different levels of detail.
- Distinguish different problems not only by error messages and error codes but by separate sub-classes with an informative name of your common base classes. This is because you cannot catch exceptions based on error messages or error codes and thus prevent code to react appropriate if different reactions are required.
Now enogh of all that theory; let’s take a look at the As2lib Exception API.
The Throwable
How are the spoken about exception basics realized in the As2lib Exception API? How can I best use this API? To what must I pay attention to when working with exceptions in ActionScript 2.0?
The As2lib Exception API supplies two kinds of exceptions:
- The “Exception” class that indicates a normal exceptional event that can in most cases be handled. Such an exception is for example the “UnknownServiceException” that is thrown if an external service cannot be reached. The application can in such a case retry to connect after waiting a few milliseconds.
- The “FatalException” class that indicates a fatal exceptional event that is mostly caused due to a programming error and can mostly only be resolved by the programmer itself. A fatal exception is for example the “AbstractOperationException” that indicates that a method has been called that was marked as abstract and should have been overridden by the programmer.
Both these exceptions are implementations of the “Throwable” interface that is the core piece of the API. The most important methods this interface declares are:
- The “initCause” method that initializes the cause of the new throwable. Sometimes you want or you have to transform a low level exception into a high level exception. You can do this by just catching the low level exception and throwing a new high level exception. But doing so without initializing the low level exception as the cause of the high level exception you would use all its useful information.
- The “addStackTraceElement” method that adds a new element to the stack trace. Make use of this method at key points in your application to make it easier following the stack trace and tracking down the exception’s cause.
When you encounter that you need to throw an exception, first look whether such an exception already exists, if not create a new one with an informative name that extends your base exception.
-
class org.myproject.mymodule.MyException extends MyBaseException {
-
public function MyException(message:String, scope, args:Array) {
-
super (message, scope, args);
-
}
-
}
-
You can now throw this exception at the appropriate place providing an informative message and other helpful information. Do not forget to document the exceptions that are thrown in your method documentation. Because otherwise it is not possible to know which exceptions are thrown besides browsing the code or encountering an exception at run-time.
-
/**
-
* …
-
* @throws MyException if …
-
*/
-
public function doSomething(Void):Void {
-
…
-
throw new MyException("This is an informative error message.", this, arguments);
-
….
-
}
The last two parameters are are not mandatory but it is good pratice to provide them; otherwise where the exception came from will not be included in the resulting string message.
When you catch the exception you must pay attention to a quirk in ActionScript 2.0 compiled by Flash (this quirk does not exist when you use the Motion-Twin ActionScript 2.0 Compiler or Flex). If you do not declare the fully qualified exception name as exception type in catch-blocks, exceptions of exactly this type will not be catched. Thus do always declare the fully qualified exception name.
-
try {
-
myInstance.doSomething();
-
} catch (myException:org.myproject.mymodule.MyException) {
-
// do something specially related to this special exception
-
} catch (myBaseException:org.myproject.mymodule.MyBaseException) {
-
// do something general
-
} finally {
-
// do something that must be done in every case
-
}
-
If you do not catch an exception or if you trace the string representation of an exception in the Flash Output Panel may appear something like this:
-
org.myproject.mymodule.MyException: This is an informative error message.
-
at org.myproject.mymodule.MyClass.doSomething()
-
Caused by: org.myproject.myothermodule.MyLowLevelException: This is a low level message.
-
at org.myproject.myothermodule.MyOtherClass.doSomethingDifferent(Number, String)
-
The Error Message
What is an error message? Why are detailed error messages important? To what should I pay attention to when writing error messages? How can I provide detailed error messages that are generated lazily or how can I manage to generate detailed error messages only when they are needed?
Error messages or detail messages are provided with every exception. They describe in detail what the problem is, why it is a problem and maybe even how this problem may be solved; try to give as much context information as possible. These messages are inteded for developers, not for display to users of your application. If you want to display a message to users, provide an error code with your exception. This error code can either be a string or a number. The advantage of a string error code is that it may itself make sense to users. You can use this error code to get an error message intended for users from a XML or property file that fits the user’s locale.
You may wonder why error messages are so important. The answer is simple. When you work on a project there are probably at least two programmers involved that work on code and use each others’ code. With detailed error messages you can quickly fix the problem if it occurs, without having to ask the other developer what the problem may be and how it can be fixed. The developer who wrote the code you must use may also have left your company some time ago, so you are not even able to ask him. Another issue is when you need to use code you wrote maybe 2 years before and you encounter an error. In all these situations (and there are definitely more) you are more than thankful that yourself and your co-workers took the time to write detailed error messages that help you to fix problems in just a few minutes.
When you write error messages you should pay attention to the format. You should not use different error message formats in one application. Following are common error message standards.
- Write full sentences that start with an upper case letter and end with a period. An error message can consist of multiple sentences and even multiple lines.
- Put in-line API names like parameter names, variable names, method names etc. in single quotes “‘”.
- Put in-line content of for example a variable in square brackets “[]”. Ensure that the string representation of the content is useful and not just something like “[type Function]” but “org.as2lib.data.holder.map.HashMap”. You may want to use the As2lib Reflection API to format the content.
- Write error messages in simple present.
- Write as precise as possible.
I suggested using reflections to look up API names or do some similar tasks. The problem at this is that it costs time even though the exception may be catched one method down in the stack and the detailed error message may not be needed at all and was thus generated in vain. If it is likely that the exception is catched, that is mostly with non-fatal exceptions, it is a good idea to generate detailed error messages lazily. With the As2lib Exception API you can therefor override the “doToString” or the “getMessage” method. Do not override the “toString” method because it does some more work than just generating the string representation of the exception; it also forwards the exception’s string representation to a logger of the As2lib Logging API if the exception was not catched. This more than helpful information would be lost otherwise. The “MethodCallRangeError” of the As2lib Mock Object API uses this method of error message generation.
-
class org.as2lib.test.mock.MethodCallRangeError extends AssertionFailedError {
-
private var methodCalls:Array;
-
private var expectedMethodCallRanges:Array;
-
private var actualMethodCallRanges:Array;
-
-
public function MethodCallRangeError(message:String, thrower, args:Array) {
-
super (message, thrower, args);
-
methodCalls = new Array();
-
expectedMethodCallRanges = new Array();
-
actualMethodCallRanges = new Array();
-
}
-
-
public function addMethodCall(methodCall:MethodCall, expectedMethodCallRange:MethodCallRange, actualMethodCallRange:MethodCallRange):Void {
-
methodCalls.push(methodCall);
-
expectedMethodCallRanges.push(expectedMethodCallRange);
-
actualMethodCallRanges.push(actualMethodCallRange);
-
}
-
-
public function doToString(Void):String {
-
var message:String = getMessage() + ":";
-
for (var i:Number = 0; i < methodCalls.length; i++) {
-
message += "\n " + methodCalls[i] + ": expected: " + expectedMethodCallRanges[i] + ", actual: " + actualMethodCallRanges[i];
-
}
-
return message;
-
}
-
}
As you can see al the detailed information that tooks some time to be constructed is only generated if needed. This also factors the error message generation out of the actual code and into the exception, where it belongs. Note that in the above example we have overridden the “doToString” method and not the “getMessage” method. We are thus actually working with two levels of error messages, one detailed that is generated lazily and is returned by the exception’s “toString” method and one not that detailed that is passed-in on construction and returned by the exception’s “getMessage” method. If you override the “getMessage” method, the “doToString” method is the default one that uses the stringifier that can be edited. In this case the stack trace and possible causes are by default also included in the resulting string on invocation of the “toString” method. Note that this is not the case in the above example.
