Friday, November 28, 2008

Flex Error Handling: A Simple Example

I have recently read and heard about situations in which Flex error handling is misunderstood. In this blog entry, I'll attempt to demonstrate, with a simple example, how Flex error handling works and attempt to drive out some of the sources of confusion. To keep this entry a reasonable size, I'll focus specifically on the Flex handling of errors (synchronous) and won't address Flex handling of error events (asynchronous).

Before I get into the actual example of throwing, catching, and "handling" (printing error contents in this simple case) a synchronous error in Flex, I need to point out the significance of using the Flash Player Debugger Version. My example will demonstrate that an error will provide some information regardless of whether it is used with a regular Flash Player or a Debugger Flash Player, but the Error class's getStackTrace() method will only return a not-null value if run in a debugger version of the Flash Player.

As I explained in my blog post on the command-line fdb debugger, it makes sense to me to only be able to see the stack trace in the debugger player because you probably don't want to spill too much error information (including ugly stack traces) all over your average client's application. On the other hand, when you have debugging turned on and are running a debugger version of the Flash Player, you probably do want the ability to see the stack trace as provided by Error.getStackTrace().

Flex provides several pre-defined ActionScript-based error classes that a Flex developer can explicitly throw when appropriate and can also expect certain Flex methods to throw when the related exceptional condition is encountered. These error classes provided by Flex out of the box include ECMAScript-mandated errors and some Flash-specific errors. All errors should extend the Error class. For Java developers, extending Flex's Error class and catching thrown Flex errors (either Error or any of its children) will seem very similar to exception handling in Java. Even the try-catch-finally syntax is remarkably similar.

Let's look at some code. The following is the code listing for the simple example.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="900" height="900"
applicationComplete="testException()">

<!-- This example demonstrates a simple example of Flex error
handling. One of the main illustrated points in this
example is that the Error.getStackTrace() method returns
null when the regular Flash Player is used and only returns
a not-null value when run in a debugger Flash Player. The
ObjectUtil class is used to make it more obvious that a null
is returned from Error.getStackTrace() when not running in
the debugger Flash Player.

This example is associated with the blog "Dustin's Software
Cogitations and Speculations."

The Flash Players, regular and debugger, can be downloaded
at http://www.adobe.com/support/flashplayer/downloads.html. -->

<mx:Script>
import mx.utils.ObjectUtil;

/**
* Test Flex exception handling.
*/
public function testException():void
{
try
{
intentionallyThrowException();
}
catch (error:Error)
{
stackTraceText.text = ObjectUtil.toString(error.getStackTrace());
messageText.text = error.message;
toStringText.text = error.toString();
nameText.text = error.name;
errorIdText.text = String(error.errorID);
}
finally
{
// I get called whether an exception is caught or not.
}
}

/**
* Intentionally throw an exception for use in Flex exception
* testing.
*/
public function intentionallyThrowException():void
{
throw new SyntaxError("That was some bad syntax!");
}
</mx:Script>

<mx:VBox id="mainDisplay">
<mx:Form>
<mx:FormItem id="stackTraceItem" label="Exception Stack Trace">
<mx:Text id="stackTraceText" />
</mx:FormItem>
<mx:FormItem id="messageItem" label="Message">
<mx:Text id="messageText" />
</mx:FormItem>
<mx:FormItem id="toStringItem" label="toString">
<mx:Text id="toStringText" />
</mx:FormItem>
<mx:FormItem id="nameItem" label="Name">
<mx:Text id="nameText" />
</mx:FormItem>
<mx:FormItem id="errorIdItem" label="Error ID">
<mx:Text id="errorIdText" />
</mx:FormItem>
</mx:Form>
</mx:VBox>

</mx:Application>


A significant portion of the above code is actually comments. If you remove the explanatory comments, the code is pretty small. Either way, the code is straightforward, especially if you have written Java exception handling code before.

The example code presents key properties (ID, message, and name), a key method (getStackTrace()), and the toString() representation of an Error class in the Flex form for easy viewing. When the compiled Flex application (.swf file) is executed with a normal (non-debugger) Flash Player, the output appears as shown in the next screen snapshot:



Even without the debugger player being used, the "message" and "name" properties (the combination of which is provided by the Error's toString()) provide useful information. However, the stack trace is a null because we are not using a debugger player.

With a debugger player being used, the results are different as shown in the next screen snapshot:



Without making any changes to my code and, in fact, the only change being the use of a debugger Flash Player instead of the regular Flash Player, I now have access to the stack trace!

It is also worth mentioning here another advantage of the Flash Player Debugger version when working with errors in Flex development. The example above demonstrated a case where an error was anticipated and code was written to catch it. Flex, unlike Java but like most other languages, does not have checked exceptions/errors. This means that a developer is never required to catch a particular error. This also implies that unanticipated errors can and do get thrown. What happens to these differs depending on which Flash Player is being used. If the regular Flash Player is being used, there is usually no sign of an error condition, but often things don't work as they should either (because the underlying error is preventing something necessary from happening). With the debugger version of the Flash Player, a stack trace is automatically presented in a pop-up for any uncaught errors. Again, it makes sense that one would want this in development but would not necessarily want the average end-user with a regular Flash Player to see it.

To illustrate the uncaught error scenario, I am adding a method to the above example that calls the same method that intentionally throws the error. This second code listing also differs from the first because I made a concerted effort to rename things from Exception to Error to be more in line with Flex terminology.


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
width="900" height="900"
applicationComplete="testErrors()">

<!-- This example demonstrates a simple example of Flex error
handling. One of the main illustrated points in this
example is that the Error.getStackTrace() method returns
null when the regular Flash Player is used and only returns
a not-null value when run in a debugger Flash Player. The
ObjectUtil class is used to make it more obvious that a null
is returned from Error.getStackTrace() when not running in
the debugger Flash Player.

A second central point of this example is to demonstrate
handling on an unanticipated (and hence uncaught) error in
the regular Flash Player and in the Debugger Flash Player.

This example is associated with the blog "Dustin's Software
Cogitations and Speculations."

The Flash Players, regular and debugger, can be downloaded
at http://www.adobe.com/support/flashplayer/downloads.html. -->

<mx:Script>
<![CDATA[
import mx.utils.ObjectUtil;

/**
* Test error handling with and without explicit capture
* of an error.
*/
public function testErrors():void
{
testError();
testWithoutErrorHandling();
}

/**
* Demonstrate what happens when an error is not explicitly
* caught.
*/
public function testWithoutErrorHandling():void
{
intentionallyThrowError();
}

/**
* Test Flex Error handling.
*/
public function testError():void
{
try
{
intentionallyThrowError();
}
catch (error:Error)
{
stackTraceText.text = ObjectUtil.toString(error.getStackTrace());
messageText.text = error.message;
toStringText.text = error.toString();
nameText.text = error.name;
errorIdText.text = String(error.errorID);
}
finally
{
// I get called whether an exception is caught or not.
}
}

/**
* Intentionally throw an error for use in Flex error handling
* testing.
*/
public function intentionallyThrowError():void
{
throw new SyntaxError("That was some bad syntax!");
}
]]>
</mx:Script>

<mx:VBox id="mainDisplay">
<mx:Form>
<mx:FormItem id="stackTraceItem" label="Exception Stack Trace">
<mx:Text id="stackTraceText" />
</mx:FormItem>
<mx:FormItem id="messageItem" label="Message">
<mx:Text id="messageText" />
</mx:FormItem>
<mx:FormItem id="toStringItem" label="toString">
<mx:Text id="toStringText" />
</mx:FormItem>
<mx:FormItem id="nameItem" label="Name">
<mx:Text id="nameText" />
</mx:FormItem>
<mx:FormItem id="errorIdItem" label="Error ID">
<mx:Text id="errorIdText" />
</mx:FormItem>
</mx:Form>
</mx:VBox>

</mx:Application>


When the SWF compiled from the code immediately above is executed against the regular Flash Player, there is no visual evidence of the uncaught exception in the newly added ActionScript method testWithoutErrorHandling(). However, there is a dramatic difference when using the debugger Flash Player. It places the stack trace in an pop-up as shown in the next screen snapshot.



As the above screen snapshot shows, we did not need to add any extra code to see this stack trace. Simply using the debugger Flash Player led to the unanticipated error having its stack trace displayed.

Side Note: Because of advantages such as access to the stack trace via Error.getStackTrace() call, access to the stack trace automatically when an unanticipated error is not captured, and the ability to trace statements to the fdb debugger, I recommend to anyone considering working with Flex that they use the Flash Player debugger version by default during Flex development.

The results shown above were run on SWF files compiled with mxmlc using its default options as specified in the flex-config.xml file. I now move onto looking at how tweaking one of the settings in the flex-config.xml file can affect error handling in Flex.

There is an entry in the flex-config.xml file that looks like this:


<!-- Turns on the display of stack traces for uncaught runtime errors. -->
<verbose-stacktraces>false</verbose-stacktraces>'


Changing the "false" to "true" leads to more verbose output in the pop-up that appears for uncaught errors when run in the Flash Debugger Player as shown in the next screen snapshot. Similarly, this change leads to the same extra verbosity in the Error.getStackTrace() results. In short, the stack trace associated with an Error and available either implicitly for uncaught errors or explicitly for captured errors via the Error.getStackTrace() call, can have its level of verbosity controlled with this verbose-stacktrace setting.



If the two pop-ups are compared, it is obvious that the turning the option from "false" to "true" really did increase the stack trace verbosity. If the application is run in a regular, non-debug Flash Player, there is still no indication of an uncaught error ever occurring and null is still returned from Error.getStackTrace().


Conclusion

Flex provides error handling mechanisms that have many similar characteristics as Java exception handling, including the syntax. However, there are also some differences in the treatment of errors in ActionScript and Flex as compared to Java (such as no checked exceptions). Using a debugger version of the Flash Player for development of Flex is always a good idea, but this is particularly true when dealing with Flex error handling.

No comments: