Introduction

Articles covering the latest in the developer world, tutorials on my new projects, elegant solutions to common programming problems and visionary thoughts on programming.

June 7th, 2005

As2lib Reflection API

What are reflections? Is Flash supporting reflections? What functionalities is the As2lib Reflection API (org.as2lib.env.reflect) offering me and how can I use these functionalities?

Note that this article has also been published on the official as2lib homepage: As2lib Reflection API.

Reflection Basics

What are reflections? How do reflections help me with development? What other programming languages support reflections?

Reflections help you find out everything about your API at run-time; your API is reflected. For example the name of a class an instance was instantiated of or all methods declared by a class that match a given naming pattern. In theory there are nearly no bounds. In languages that support reflections by default, like Java or C#, it is even possible to find out about the parameters of methods and their types.

As I now already stated, the programming languages Java and C# have reflections support. The reflection mechanism of these languages is built-in. These reflection APIs are thus in some respects more powerful than the one I will present in this article.

The most common usage of reflections is to generate good string representations of objects. These string representations can then be used for logging and in error/detail messages. But you can also use reflections to map from a XML file, database or property file to an object. Another common usage is the creation of dynamic factories (GoF, Factory Design Pattern) that resolves classes by name at run-time or instantiates passed classes.

If you are an experienced ActionScript programmer you may have noticed that all these things are possible with a ‘little’ knowledge of ActionScript 1.0 and Flash right now; except finding out class, properties and methods names. But what is missing is a good API that accomplishes these tasks in a performant and save way. Performance is gained by caching results and by good algorithms, safety by comprehensive test suites, a well-typed API, good run-time checks and the assurance that everything works fine in Flash, Flex and with MTASC.

In case you have been hooked: go-on reading and step directly into the As2lib Reflection API.

The Reflection Utility

What is the light-weight reflection utility? What services does it offer? What are its advantages?

The As2lib Reflection API is split into two parts, a light-weight and a heavy-weight one. The light-weight part exists only of one class, the ReflectUtil. This class offers basic methods to look up names of types and methods and to find out whether a specific method is static (per class) or is a constructor.

Its advantages are:

  • It is light-weight. This means that the class has little dependencies and the SWF file size is thus only increased by a minimum.
  • Its usage is quite simple. You do not have to deal with multiple classes and instances, but only invoke static methods.
  • It is fast. Note that the heavy-weight part is also fast, but maybe a little slower because of more delegation. But this speed difference is really at a minimum.

Its disadvantages are:

  • It may not offer all needed functionalities.
  • It does not cache results.
  • It offers only a low-level API.
  • It is neither flexible nor extensible.

Depending on your needs this class may be all you need. The As2lib always uses this class internally to look up API names that are in most cases included in log or error messages.

The most common usage is to look up type names. You have two options: you can either get the type name directly for a class (this is a function in ActionScript) or for an instance of a class.

  1. import org.as2lib.env.reflect.ReflectUtil;
  2. import com.mydomain.myapplication.MyClass;
  3.  
  4. // traces the name of the class looked up by a class
  5. trace("By Class: " + ReflectUtil.getTypeName(MyClass));
  6.  
  7. // traces the name of the class looked up by an instance
  8. var myInstance:MyClass = new MyClass();
  9. trace("By Instance: " + ReflectUtil.getTypeName(myInstance));
  10.  

The output of the above code is:

  1. By Class: com.mydomain.myapplication.MyClass
  2. By Instance: com.mydomain.myapplication.MyClass

The same approach applies to looking up method names. You again have the option between using a class or an instance as basis. Note that either the class itself or any super-class must implement the searched for method. Note that the following example is not a real-life example but just a demonstration of the different ways of performing the wanted task.

  1. import org.as2lib.env.reflect.ReflectUtil;
  2. import com.mydomain.myapplication.MyClass;
  3.  
  4. // traces the name of the per class method looked up by class and method
  5.  
  6. trace("By Class: " + ReflectUtil.getMethodName(MyClass.myStaticMethod, MyClass));
  7.  
  8. // traces the name of the per instance method looked up by class and method
  9. trace("By Class: " + ReflectUtil.getMethodName(MyClass.prototype.myMethod, MyClass));
  10.  
  11. // traces the name of the per instance method looked up by instance and method
  12. var myInstance:MyClass = new MyClass();
  13. trace("By Instance: " + ReflectUtil.getMethodName(myInstance.myMethod, myInstance));

If you run the above code you get the following output:

  1. By Class: myStaticMethod
  2. By Class: myMethod
  3. By Instance: myMethod
  4.  

A futher important method is the “getTypeAndMethodInfo” method. This method returns all needed information about the type and the method. This is whether the method is static (per class), the fully qualified name of the type and the name of the method. You may wonder why not just using the available methods to fulfill all these tasks step-by-step. This would work, but it is slower and needs more method calls. Another more important issue is that the method may not be directly implemented by the passed type, but by its super-type. In such a case you’d want the type name to be the one that implements the method and not the passed one. All this is taken into account by this method, which makes it important to mention.

  1. import org.as2lib.env.reflect.ReflectUtil;
  2. import com.mydomain.myapplication.MyClass;
  3.  
  4. // gets the type and method information
  5. var info:Array = ReflectUtil.getTypeAndMethodInfo(MyClass, MyClass.myStaticMethod);
  6.  
  7. // if the method is static ‘info[0]’ is ‘true’ otherwise ‘false’
  8. var output:String = info[0] ? "static " : "";
  9.  
  10. // adds the fully qualified type name
  11. output += info[1];
  12.  
  13. // adds the method name
  14. output += "." + info[2] + "()";
  15.  
  16. // traces the output
  17. trace(output);

The output is:

  1. static com.mydomain.myapplication.MyClass.myStaticMethod()

If the method were not static and were implemented by a super-class the output would have looked something like this:

  1. com.mydomain.myapplication.MySuperClass.myMethod()

For the full-range of methods and their usage take a look at the API documentation. Now let’s go-on to the full-featured API.

The Class Info

How can I find out everything about a class? What methods and properties does it declare? What are its super-classes? What is its namespace and of which package is it a member of? How can I get all its methods and properties that match a given criteria?

When you use the As2lib Reflection API the first thing you need is in most cases a ClassInfo instance. Given this instance for a specific class you can perform almost all operations offered by the API related to your class. Note that the “ClassInfo” represents classes as well as interfaces because a differentiation is not possible at run-time. To get a “ClassInfo” instance for your specific class you need either the class itself, an instance of that class or the name of the class.

  1. import org.as2lib.env.reflect.ClassInfo;
  2. import com.mydomain.myapplication.MyClass;
  3.  
  4. // gets a class info by class
  5. var classInfoByClass:ClassInfo = ClassInfo.forClass(MyClass);
  6.  
  7. // gets a class info by instance
  8. var myInstance:MyClass = new MyClass();
  9. var classInfoByInstance:ClassInfo = ClassInfo.forInstance(myInstance);
  10.  
  11. // gets a class info by name
  12. var classInfoByName:ClassInfo = ClassInfo.forName("com.mydomain.myapplication.MyClass");
  13.  

All these class infos are functionaly the same, they are even the same instances; the only thing that differs is how they were obtained. Given such a class info instance you can get the following basic information.

  1. trace("The class’s name: " + classInfo.getName());
  2. trace("The class’s fully qualified name: " + classInfo.getFullName());
  3. trace("The super class’s fully qualified name: " + classInfo.getSuperType().getFullName());
  4. trace("The class’s namespace: " + classInfo.getPackage().getFullName());
  5. trace("The class’s constructor: " + classInfo.getConstructor());
  6. trace("The declared methods: " + classInfo.getMethods(true));
  7. trace("The declared properties: " + classInfo.getProperties(true));
  8.  

If you like to you can also create a new instance of the represented class.

  1. var newInstance:MyClass = classInfo.newInstance("arg1", 2);

To get a MethodInfo instance that represents a method of your class or any super-class you have the name of or the concrete method as function you can use the “ClassInfo.getMethod” method. Note that the following works totally the same for properties, but with a PropertyInfo and the “ClassInfo.getProperty” method.

  1. import org.as2lib.env.reflect.MethodInfo;
  2.  
  3. // gets a method info by name
  4. var methodInfoByName:MethodInfo = classInfo.getMethod("myMethod");
  5.  
  6. // gets a method info by concrete method
  7. var myInstance:MyClass = new MyClass();
  8. var concreteMethod:Function = myInstance.myMethod;
  9. var methodInfoByConcreteMethod:MethodInfo = classInfo.getMethod(concreteMethod);
  10.  

Now let us come to a little more complex method, the “ClassInfo.getMethods” method. In its simplest from, which I used in an example at the beginning, this method simply returns either only the methods declared by the class itself or this method and the one of all super-classes. But it is also possible to use your own filter criteria. You must therefore at first create a class that implements the TypeMemberFilter interface. You can then implement your filter criteria in the “filter” method that returns “true” if the passed-in type member shall be excluded/filtered or “false” if not. Let us say we want to filter all methods that do not start with the string “add”.

  1. import org.as2lib.env.reflect.TypeMemberFilter;
  2. import org.as2lib.env.reflect.TypeMemberInfo;
  3.  
  4. class MyMethodFilter implements TypeMemberFilter {
  5.  
  6.     public function TypeMemberFilter(Void) {
  7.     }
  8.  
  9.     public function filter(typeMember:TypeMemberInfo):Boolean {
  10.         if (typeMember.getName().indexOf("add") == 0) {
  11.             return false;
  12.         }
  13.         return true;
  14.     }
  15.  
  16.     public function filterSuperTypes(Void):Boolean {
  17.         return false;
  18.     }
  19. }
  20.  

You can now use an instance of the filter “MyFilter” as parameter for the “ClassInfo.getMethods” method to receive only methods that have the prefix “add”.

  1. var methods:Array = classInfo.getMethods(new MyMethodFilter());
  2.  

The above mechanism for methods works the same for properties. You only have to use the equivalent property methods. Take a look for these in the API documentation of the “ClassInfo” class.

The Package Info

How can I find out everything about a package? What members, classes and packages, does it have? What are its parent packages? How can I get all its members that match a given criteria?

As with class infos you can get PackageInfo instances either by a concrete package or by name.

  1. import org.as2lib.env.reflect.PackageInfo;
  2.  
  3. // gets a package info by concrete package
  4. var packageInfoByPackage:PackageInfo = PackageInfo.forPackage(com.mydomain.myapplication);
  5.  
  6. // gets a package info by name
  7. var packageInfoByName:PackageInfo = PackageInfo.forName("com.mydomain.myapplication");
  8.  

The two returned package infos are functionally and physically the same, they are the same instance. Given either of these instances you can find out the following basic information.

  1. trace("The package’s name: " + packageInfo.getName());
  2. trace("The package’s fully qualified name: " + packageInfo.getFullName());
  3. trace("The parent package’s name: " + packageInfo.getParent().getName());
  4. trace("Is this package a/the root package?: " + packageInfo.isRoot());
  5. trace("The package’s member classes: " + packageInfo.getMemberClasses());
  6. trace("The package’s member packages: " + packageInfo.getMemberPackages());

A specific member class or package can also be obtained either by concrete class or package or by name.

  1. import org.as2lib.env.reflect.ClassInfo;
  2. import com.mydomain.myapplication.MyClass;
  3.  
  4. // gets a class member by name
  5. var classMemberInfoByName:ClassInfo = packageInfo.getMemberClass("MyClass");
  6.  
  7. // gets a class member by concrete class
  8. var classMemberInfoByClass:ClassInfo = packageInfo.getMemberClass(MyClass);
  9.  
  10. // gets a package member by name
  11. var packageMemberInfoByName:PackageInfo = packageInfo.getMemberPackage("mymodule");
  12.  
  13. // gets a package member by concrete package
  14. var packageMemberInfoByPackage:PackageInfo = packageInfo.getMemberPackage(com.mydomain.myapplication.mymodule);
  15.  

In the previous article on class infos we talked about getting methods or properties that match a given criteria. This is also possible with package infos but the criteria must now be met by member classes and member packages. We do again need a filter that filters/excludes unwanted package members. This filter must implement the PackageMemberFilter interface. This time we want to include all abstract classes that start with the “Abstract” string to be included in the result; sub-packages shall also be searched through.

  1. import org.as2lib.env.reflect.PackageMemberFilter;
  2. import org.as2lib.env.reflect.PackageMemberInfo;
  3.  
  4. class MyClassMemberFilter implements PackageMemberFilter {
  5.  
  6.     public function PackageMemberFilter(Void) {
  7.     }   
  8.  
  9.     public function filter(packageMember:PackageMemberInfo):Boolean {
  10.         if (packageMember.getName().indexOf("Abstract") == 0) {
  11.             return false;
  12.         }
  13.         return true;
  14.     }
  15.  
  16.     public function filterSubPackages(Void):Boolean {
  17.         return false;
  18.     }
  19. }

We can now use our filter as follows:

  1. var classMemberInfos:Array = packageInfo.getMemberClasses(new MyClassMemberFilter());

Exactly the same mechanism can also be applied to member packages. Take a look at the API documentation of the “PackageInfo” class for the appropriate methods.

June 4th, 2005

As2lib Exception API

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.

  1. class org.myproject.mymodule.MyException extends MyBaseException {
  2.     public function MyException(message:String, scope, args:Array) {
  3.         super (message, scope, args);
  4.     }
  5. }
  6.  

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.

  1. /**
  2. * …
  3. * @throws MyException if …
  4. */
  5. public function doSomething(Void):Void {
  6.     …
  7.     throw new MyException("This is an informative error message.", this, arguments);
  8.     ….
  9. }

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.

  1. try {
  2.     myInstance.doSomething();
  3. } catch (myException:org.myproject.mymodule.MyException) {
  4.     // do something specially related to this special exception
  5. } catch (myBaseException:org.myproject.mymodule.MyBaseException) {
  6.     // do something general
  7. } finally {
  8.     // do something that must be done in every case
  9. }
  10.  

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:

  1. org.myproject.mymodule.MyException: This is an informative error message.
  2.   at org.myproject.mymodule.MyClass.doSomething()
  3. Caused by: org.myproject.myothermodule.MyLowLevelException: This is a low level message.
  4.   at org.myproject.myothermodule.MyOtherClass.doSomethingDifferent(Number, String)
  5.  

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.

  1. class org.as2lib.test.mock.MethodCallRangeError extends AssertionFailedError {
  2.     private var methodCalls:Array;
  3.     private var expectedMethodCallRanges:Array;
  4.     private var actualMethodCallRanges:Array;
  5.    
  6.     public function MethodCallRangeError(message:String, thrower, args:Array) {
  7.         super (message, thrower, args);
  8.         methodCalls = new Array();
  9.         expectedMethodCallRanges = new Array();
  10.         actualMethodCallRanges = new Array();
  11.     }
  12.  
  13.     public function addMethodCall(methodCall:MethodCall, expectedMethodCallRange:MethodCallRange, actualMethodCallRange:MethodCallRange):Void {
  14.         methodCalls.push(methodCall);
  15.         expectedMethodCallRanges.push(expectedMethodCallRange);
  16.         actualMethodCallRanges.push(actualMethodCallRange);
  17.     }
  18.  
  19.     public function doToString(Void):String {
  20.         var message:String = getMessage() + ":";
  21.         for (var i:Number = 0; i < methodCalls.length; i++) {
  22.             message += "\n  " + methodCalls[i] + ": expected: " + expectedMethodCallRanges[i] + ", actual: " + actualMethodCallRanges[i];
  23.         }
  24.         return message;
  25.     }
  26. }

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.

June 2nd, 2005

As2lib Logging API

This tutorial covers logging basics, the simple and complex features of the As2lib Logging API (org.as2lib.env.log) and the integration of other logging APIs and custom behavior.

This article has also been published on www.as2lib.org: As2lib Logging API.

Logging Basics

Why is logging important? Why is logging superior to tool debugging? What are the basic requirements of a logging API? How do I approach logging? What is good and what bad practice?

Code must be debugged. With small projects debugging is not much of a problem, but when it comes to larger ones it is a pain to track down bugs that are only recognizable at run-time. These bugs may depend on some previous state and the interaction of large application modules. To make debugging easier it is necessary to trace the application’s execution. In Flash this can be done using “trace”. The problem with “trace” is that it is very primitive. It does not allow for different levels of output nor for custom output devices (an exception right now is the Motion-Twin ActionScript 2.0 Compiler, that allows you to replace calls to “trace” by a custom method prior to compilation). What is needed is a general easy to use logging API that allows for different log levels and that can be easily expanded to support an endless amount of different output possibilities and that can easily integrate other logging APIs without compromising their special functionalities.

You may wonder, why not just use a debugging tool? There are some major differences with instrumentation and debugging.

  • Debugging sessions help you track down today’s bugs, but not tomorrow’s.
  • Debugging is time consuming. It may be quicker to step through a log file to search for the bug source.
  • Logging makes you think about your application.
  • Logging let’s you easily track down bugs that occured when a client worked with your program. You may not be able to reproduce the bug or to fix it if you had not logged.
  • Logging is more informative than debugging in that it provides detailed human written description on what is going on.
  • Logging gives you control on how and where to log. You are not bloating your log file with trivial information.

As already stated a logging API should fulfil some general requirements.

  • A simple API.
  • Simple configuration.
  • Several log levels or priorities, like debug, info, warning, error and fatal and the ability to choose which messages at which level to log.
  • The ability to query whether messages with a given level will be logged. This may improve performance because it prevents unnecessary construction of messages that will not be logged.
  • The ability to change message formatting.
  • The ability to change output devices.

Given these functionalities it is also important when and how to log.

  • Log heavily in important and error-prone code sections.
  • Modify and improve log messages whenever you think of it to be necessary, like with ambiguous or unclear messages.
  • Think about the correct log level for log messages. Log messages at the same level should be of the same level of detail.

The Logger

How to work with the core and most important interface “Logger”, what this interace offers you and what its advantages are.

“Logger” is the most important interface of the As2lib Logging API. It is the only interface you work with in your application code. All other classes and interfaces of the API just offer special configuration functionalities that are not visible to your application code; you only may use them for configuration on start-up.

That said, what exactly offers the “Logger” interface?

  • Generic methods to log at different levels. These methods are in ascending priority: “debug”, “info”, “warning”, “error” and “fatal”.
  • Generic methods to figure out whether messages at a given level will actually be logged. These methods, again in ascending priority, are: “isDebugEnabled”, “isInfoEnabled”, “isWarningEnabled”, “isErrorEnabled” and “isFatalEnabled”.

All these methods are generic because they are not bound to any underlying API. The log methods take any type of message as parameter and the query methods return primitive boolean values. How the messages will be logged or how the level is determined depends on the specific implementation. This general approach makes it very easy to switch between different implementations and gives you the ability to work with the logging API of another framework to savor all its special functionalities without tieing your logging code in your application to this logging API.

The As2lib itself offers broad support for different logging APIs and consoles. These include ASCB Library Logging API, LuminicBox Logging API, Flash Debug Panel Source from Bit-101, Alcon, Flashout and SWF Console from Music Theory. This support is offered by “Logger” implementations designed to work with the specific APIs and by “LogHandler” implementations which are discussed in a following tutorial page.

In your application code you obtain a logger through a general access point like your application context or config or from the “LogManager” of the As2lib Logging API. The “LogManager”s duty is simply holding a reference to a logger instance that shall be returned on a call to the static “LogManager.getLogger” method or holding a repository that is responsible for the actual creation or look-up of the logger instance to return. But again the application code is unaware of where the logger comes from.

In your application code you best use a logger as follows.

  1. var logger:Logger = getLogger();
  2. if (logger.isInfoEnabled()) {
  3.     logger.info("An informative message.");
  4. }

As you can see I am first checking whether the info message will actually be logged and only if this holds true I will actually log the message. In the above example this check may not be necessary because we are not concatenating strings or looking-up a class’s name using reflections, so this may not improve performance. But in most cases it really improves performance; thus I think of it to be good practice.

Note that also I used a string message in the above example, I could also have logged any kind of object. How the string representation is obtained from the object depends on the concrete “Logger” implementation. It may obtain it just by invoking the object’s “toString” method, but it may also iterate over the object’s variables and methods and log these. Using an object as message may also improve performance because for example the “toString” method of the object is only invoked if the message is actually going to be logged. This approach could be called “Lazy Log Message Generation”.

There is one method we have not talked about yet: “getLogger”. This method is in the above example responsible for returning the “Logger” instance. If you have an application context or config this method’s implementation may look like this:

  1. return MyContext.getLogger();

As already mentioned you can also use the “LogManager” to get your logger.

  1. return LogManager.getLogger();

The only thing you must keep care of is that the “LogManager” is configured with the logger to return before its “LogManager.getLogger” method is invoked.

  1. LogManager.setLogger(new TraceLogger());

In the above case we are configuring the “LogManager” with a “TraceLogger” instance. “TraceLogger” simply formats all messages using the “LogMessage” class and “trace”s the formatted message. The formatted message includes for example the log level of the message. You just have to replace this single line of configuration code to make all your application code log to a new console. If you are for example switching from the Macromedia Compiler (MMC) to the Motion-Twin ActionScript 2.0 Compiler (MTASC) you may want to log with Flashout.

  1. LogManager.setLogger(new FlashoutLogger());

I said above that the “Logger” interface can also be used just as delegate and general interface for other logging APIs. Such a API is for example the ASCB Library Logging API. Joey Lott has written a great article about this API: Flash Logging - A Better Trace. To gain all strengths of this API you can configure the “LogManager” with the “AscbLogger”. The “AscbLogger” simply wraps a logger of the ASCB Library Logging API. You can thus do everything the same as Joey Lott explains it in his article. The only thing that differs are the calls to the log methods; you call them on an instance of the generic “Logger” interface and not on the ASCB Logger directly.

  1. LogManager.setLogger(new AscbLogger());

As you may have noticed there is a weakness with our way of configuring the “LogManager”. We always return the same logger. It would be great if we could for example return different loggers depending on different logger names. These loggers may also be part of a hierarchy and share their configuration, like the log level, with their children. As you may have guessed this functionalities can actually be achieved with no more than a few lines of configuration code. This is covered in the next tutorial page; just go on.

The Logger Repository

What exactly is a logger repository, how can I use it and what are its advantages compared to returning always the same logger?

A logger repository manages multiple loggers. The “LoggerRepository” declares only one method “getLogger(String):Logger”. This method returns a logger appropriate to the passed-in logger name. At its simplest form a “LoggerRepository” implementation just creates, stores and returns loggers by name. But a logger repository can also be more complex.

Let us begin with a simple logger repository, the AscbLoggerRepository. This repository does exactly the things I said when refering to the simplest form of repository. On invocation of the “getLogger” method it first looks whether a logger with the given name has already been stored. If so, this logger will be returned. Otherwise a new “AscbLogger” instance will be created, stored and then returned. As you can see there is not much magic going on. The great thing about this is that the returned loggers are configured with the given name and that this name is shown (by default) with the logged message. This makes it very easy to find out who logged which messages. We can of course still use the “LogManager” as on the previous tutorial page. We just have to configure it with the “AscbLoggerRepository”.

  1. LogManager.setLoggerRepository(new AscbLoggerRepository());

We also have to change the call to the static “LogManager.getLogger” method; we must include a name now. Note that you should decide at the beginning of the project whether to use named loggers or just one logger for the whole project. This largely depends on the size of your application. With small applications it may not be necessary to include a logger name in a message. You can also just always invoke the “LogManager.getLogger” method with a name regardless of whether the underlying configuration supports named loggers. This enables you to switch between the two styles of logger management as you please. You might also use a complex and slower logger management during production to be able to track down problems fast and a simple and fast logger management when the application is in use. The simple management may just ignore the given name. Following is the body of the “getLogger” method of the example of the previous tutorial page.

  1. return LogManager.getLogger("org.myproject.mymodule.MyClass");

A complex logger repository is the “LoggerHierarchy”. It manages loggers hierarchical. Every class that gets a logger passes in its name (as we did in the previous example) that is used as the logger’s name. This firstly clearly states which class logged which message in the log output and secondly makes up the hierarchical structure. A fully qualified class name consists of its namespace, that is its preceding package structure, plus the name of the class; for example “org.as2lib.env.except.Exception”. In this case the parent of the logger with full name “org.as2lib.env.except.Exception” is “org.as2lib.env.except” and the parent of this parent is “org.as2lib.env” and so on. It is now possible to configure every node in this hierarchical structure and its children will inherit this configuration unless they overwrite it. The call to the “LogManager.getLogger” method stays the same. Again, what has to be changed is the configuration.

  1. var repository:LoggerHierarchy = new LoggerHierarchy();
  2. var coreLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.myproject");
  3. coreLogger.addHandler(new TraceHandler());
  4. coreLogger.addHandler(new FlashoutHandler());
  5. repository.addLogger(coreLogger);
  6. var invisibleLogger:SimpleHierarchicalLogger = new SimpleHierarchicalLogger("org.myproject.invisiblemodule");
  7. invisibleLogger.setLevel(SimpleHierarchicalLogger.NONE);
  8. repository.addLogger(invisibleLogger);
  9. LogManager.setLoggerRepository(repository);

In the above configuration example we are first creating a new “LoggerHierarchy” instance. We are then instantiating two hierarchy able loggers, “coreLogger” and “invisibleLogger”, that are assigned to two nodes in the hierarchy, “org.myproject” and “org.myproject.invisiblemodule”, and adding these two loggers to the hierarchy. The hierarchy itself is set as repository on the “LogManager”. The “coreLogger” logs to the Flash Output Panel (”TraceHandler”) and to the Flashout Console (”FlashoutHandler”). The “invisibleLogger” inherits the handlers from its parent logger, the “coreLogger”, but it sets its log level to “NONE”. This means that it does not log messages.

If we now execute the following code, the message “This is an informative message.” will be logged to the Flash Output Panel and to the Flashout Console.

  1. LogManager.getLogger("org.myproject.mymodule.MyClass").info("This is an informative message.");

But if our class is somewhere in the “org.myproject.invisiblemodule” package or any sub-package no log output will be made.

  1. LogManager.getLogger("org.myproject.invisiblemodule.mysubpackage.MyOtherClass").info("This informative message will not be logged.");

It is important to note that with “SimpleHierarchicalLogger” instances the log level is, if not specified directly, the level of its parent and the handlers are all handlers of its parents. The children inherit the configuration of their parent or parents.

In the above example I have introduced a new notion: log handlers. The following tutorial page gives more insight into their usage.

The Log Handler

What are log handlers and why and when are they used.

A “LogHandler” is responsible for the actual logging of messages with some loggers. All loggers that offer hierarchy support for the “LoggerHierarchy” repository must work with log handlers, because otherwise inheriting the output devices of the parents would not be possible. Such a logger is the “SimpleHierarchicalLogger”. But log handlers are not only helpful in a hierarchy. Also non-hierarchical loggers like the “SimpleLogger” can benefit from log handlers. Log handlers enable a logger to support more than one output device. With log handlers one and the same logger can for example log to the Flash Output Panel and to a log file.

  1. var logger:SimpleLogger = new SimpleLogger("MyLogger");
  2. logger.addHandler(new TraceHandler());
  3. logger.addHandler(new MyCustomHandler());
  4.  

When you now invoke any log method on the above “SimpleLogger” instance it will dispatch a “LogMessage” instance containing all information about the actual message, like its level, to all regitered handlers that are then responsible for creating the string representation of the given “LogMessage” instance, probably via the “LogMessage.toString” method, and for writing this string to the actual output device.

As you can see, loggers supporting log handlers are much more flexible than the ones that do not, like “TraceLogger” that is hard-coded to log to the Flash Output Panel. But you must also see the downside of this approach: it costs performance, even if there is only one handler added. I thus recommend using loggers with handler support only in production and only if they are needed and switching to hard-coded loggers when the application is actually in use.

The Log Message

What does the log message and how can its string representation be customized?

There are some loggers that work with the “LogMessage” class; above all loggers that work with “LogHandler”s. A “LogMessage” simply holds all information regarding an actual log message. These information are the message itself, the level at which the message is logged, the name of the logger the message is logged on and the time at which the log message was created. To now actually log the message it must be stringified, this means a string representation of the message including all extra information must be generated. To do this you simply have to invoke the “LogMessage.toString” method. The string this method returns is generated through a stringifier that can be set via the “LogMessage.setStringifier” method. There also exists a customizable “LogMessage” stringifier, “PatternLogMessageStringifier”. You can use this stringifier or a custom one to format log messages as you please.

  1. <p>LogMessage.setStringifier(new PatternLogMessageStringifier(true, true, "HH:nn:ss.S"));</p>
May 31st, 2005

As2lib Event API

This tutorial covers event basics, different approaches to event handling, their advantages and disadvantages and how these approaches were realized in the As2lib Event API (org.as2lib.env.event).
Note that this article has also been published on the official as2lib homepage: As2lib Event API. You may find it better structured there. If you are familiar with the old already known event approaches you may just scroll down until the “Distributor” part.

Event Basics

What is an event? When are events used? Are events related to the Observer Design Pattern of GoF (Gang of Four)?

An event is every change in an application an event handler receives. Events are for example triggered if a certain point in an application is reached or if a user interacts with the application. In theory everything can trigger events. An event-driven API normally consists of two main modules.

  • A dispatcher is responsible for dispatching the event to multiple event handlers. Dispatchers are also called broadcasters, distributors, publishers and multicasters.
  • Event Handlers are responsible for handling events. They receive the event and operate in accordance with it. They may also consume the event and thus stop the event to be dispatched to further event handlers. Event handlers are also called listeners, subscribers and observers.

In most event APIs, event handlers can register themselves at a dispatcher. If an event occurs the dispatcher then dispatches the event to all registered event handlers. Thus the dispatcher has two main responsibilites.

  • Managing a collection of event handlers.
  • Dispatching events to all registered event handlers if they occur and maybe managing an event queue that holds unprocessed events.

Some dispatchers also support the consumption of events. The dispatcher then dispatches an event to event handlers step-by-step, until an event handler consumes the event. This is sometimes called to veto to an event.

Events are used when there is more than one object or there are an unknown number of objects that may want to be notified if some specific operation took place. For example if a user pressed a button. It is bad pracitice to use events for every interaction between objects because this adds unnecessary complexity and slows the execution of the program down. So, think carefully if using events really benefits your application in your specific case.

The Observer Design Pattern is closely related to events. The “Subject” is analog to the “Dispatcher” and the “Observer” to the “Event Handler”. Reading the Observer’s intent you can clearly see the connection: “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.”.

Listener Source

What is a listener source? Which listener source implementations are provided by the As2lib Event API and what are their differences?

In the As2lib Event API we call event handlers listeners and thus a collection of event handlers listener source. The “EventListenerSource” interface declares common methods needed to manage a collection of listeners. A listener source is so to speak a data holder that holds listeners. The As2lib Event API offers two types of listener sources.

  • The “SimpleEventListenerSource” manages listeners in the simplest way possible.
  • The “TypeSafeEventListenerSource” manages listeners and ensures that it contains only listeners of a specific type.

Depending on your event handling implementation you may not need a type-safe listener source because you may have ensured the correct type of the listeners somehow else. In such a case use the simple listener source or the type-safe listener source with a flag that the type shall not be checked. Otherwise use the type-safe listener source because it prevents unexpected behavior caused by wrong listeners.

Broadcaster

What is the As2lib Broadcaster Event API (org.as2lib.env.event.broadcaster)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

This As2lib Broadcaster Event API is built around the “EventBroadcaster” interface. This interface combines listener source and dispatch functionalities. The “dispatch” method expects exactly one parameter that must be of type “EventInfo”. This event info is also passed-to the listeners. It holds at least the name of the event that is also the name of the method to invoke on the listeners, the event method. It may also contain any additional information. The normal process when working with this API is as follows:

  • Creating a listener interface that declares the event method. This event method takes one parameter that must be of type “EventInfo” or a sub-interface or implementation-class.
    1. interface MyListener {
    2.     public function onMyEvent(myEventInfo:MyEventInfo):Void;
    3. }
    4.  
  • Creating the specific event info class that has been used as type for the event method’s parameter. This class must implement the “EventInfo” interface. The “getName” method should return a hard-coded string, the name of the event method.
    1. class MyEventInfo implements EventInfo {
    2.     private var information:String;
    3.     public function MyEventInfo(information:String) {
    4.         this.information = information;
    5.     }
    6.     public function getInformation(Void):String {
    7.         return this.information;
    8.     }
    9.     public function getName(Void):String {
    10.         return "onMyEvent";
    11.     }
    12. }
    13.  

In your application you instantiate an event broadcaster for every event, listeners can register to and implement the appropriate add and remove methods that should be properly typed to the listener interfaces. Now when an event occurs you instantiate the appropriate event info with all needed information and dispatch to the added listeners with the appropriate broadcaster passing-in the instantiated event info.

  1. class MyClass {
  2.     private var myBroadcaster:EventBroadcaster;
  3.     public function MyClass(Void) {
  4.         this.myBroadcaster = new SpeedEventBroadcaster();
  5.     }
  6.  
  7.     private function dispatchMyEvent(information:String):Void {
  8.         this.myBroadcaster.dispatch(new MyEventInfo(information));
  9.     }
  10.  
  11.     public function addMyListener(myListener:MyListener):Void {
  12.         this.myBroadcaster.addListener(myListener);
  13.     }
  14.  
  15.     public function removeMyListener(myListener:MyListener):Void {
  16.         this.myBroadcaster.removeListener(myListener);
  17.     }
  18. }

The advantages of this approach are:

  • If the event method’s name of a listener changes there is only one place to change the event method’s name, the event info class.
  • Because of your typed methods to add and remove listeners you ensure that only listeners that really implement the expected interface are added, so there is no need for further type-checking by the listener source, which leads to better performance.
  • Because of your event info class that contains all event specific information you are not bloating the event method’s parameter list. Note though that in the above case this would not have been necessary because we have only one information.
  • This approach is rather fast because it does not have to make any detours.
  • This approach does not prescribe the existence of a listener interface or class. Note that I myself think of not declaring an explicit listener type as bad practice.
  • If one of the listeners needs more information one can simply extend the event info without having to alter other listeners.

But there are also many disadvantages:

  • The compiler does not check whether the event method’s name on the listener and the one in the event info class are in sync.
  • The listener must always get its information from the passed-in event info. If there is no information needed or if the information needed is only one value, as in the above example, an event info is overkill and unintuitive.
  • The listener is bound or has a dependency to the As2lib Broadcaster Event API because it declares one parameter of type “EventInfo”.
  • There must be one, in some cases unnecessary class, the “EventInfo” implementation.

Let’s move on and take a look at the As2lib Multicaster Event API that eliminates some of the disadvantages, but adds some new ones.

Multicaster

What is the As2lib Multicaster Event API (org.as2lib.env.event.multicaster)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

The core interface of the As2lib Multicaster Event API is the “EventMulticaster”. This interface combines, like the “EventBroadcaster” interface of the previous tutorial page, listener source and dispatch functionalities. The difference is that this time the “dispatch” method expects any number of parameters of any type that are passed-to the event methods of all registered listeners; the listener interface is thus not bound to an event info and a specific event API. All the rest stays basically the same.

  1. interface MyListener {
  2.     public function onMyEvent(information:String):Void;
  3. }

  1. class MyClass {
  2.     private var myMulticaster:EventMulticaster;
  3.     public function MyClass(Void) {
  4.         this.myMulticaster = new SimpleEventMulticaster("onMyEvent");
  5.     }
  6.  
  7.     private function dispatchMyEvent(information:String):Void {
  8.         this.myMulticaster.dispatch(information);
  9.     }
  10.  
  11.     …
  12. }

The advantages are:

  • We do not bind our listeners anymore to a specific event API by allowing custom parameters.
  • We can still package information/parameters into one data transfer object if we want to, but it is not prescribed. And this object also does not have to extend or implement a specific class or interface.
  • Dispatching events and declaring listeners feels much more natural and is more intutitive.

But again this is connected with some disadvantages:

  • The name of the event method is in most implementations passed-in on instantiation of every event multicaster. This is more error-prone because it is easy to make a typo. If the listener’s event method’s name changes we also have to change the event method name in multiple locations.
  • Compile-time checking of whether the event method’s name on the listener and the ones we pass-to our event multicasters is still not given.

Again, there issstill much that can be improved; most notably the lack of compile-time checking. This issue is addressed by the As2lib Distributor Event API, so go on.

Distributor

What is the As2lib Distributor Event API (org.as2lib.env.event.distributor)? What are its advantages and disadvantages compared to other event APIs? In which scenarios is it worthwhile to use this API?

Unlike the previously discussed event APIs, this API’s base interface is not a dispatcher but a dispatcher control: “EventDistributorControl”. This interface acts as a listener source and a provider and controller of a typed distributor. You again create such a control per event providing it the listener interface or class.

  1. var myControl:EventDistributorControl = new SimpleEventDistributorControl(MyListener);

You can then use this control to add and remove listeners.

  1. myControl.addListener(new SimpleListener());
  2. myControl.addListener(new ComplexListener());

If the event occurs you first get a typed distributor from the control and invoke the event method on it passing-in the appropriate parameters. The neat thing about this is that you get compile-time checking whether the method you are invoking exists and whether the parameters are of correct type.

  1. var distributor:MyListener = myControl.getDistributor();
  2. distributor.onMyEvent("myArg1", 2, new Object());

The advantages are obvious:

  • You have proper compile-time checking when dispatching the event; type-checking of parameters and existence-checking of method.
  • You can have proper type-checking of listeners because you specified the expected listener type.
  • You can use custom parameters and are not bound to an event info. But if you want to package the information in one object you are free to do so; this object must not extend or implement any specific class or interface, its just a design choice.
  • The listener is not bound to any specific event API.

One disadvantage may be that if the name of the event method on the listener changes you have to make a change in every place where you dispatch events. But the compiler tells you that and where these changes have to be made. A good IDE also changes all calls to the method automatically if you change the method’s name. So, this is not much of an issue.

May 31st, 2005

As2lib 0.9 Release

I am proud to finally announce a new release of the As2lib after 9 months of hard work to make it faster, lighter, more stable and more intuitive.

Download As2lib Version 0.9.

The most important changes are:

  • Mtasc and Flex compatibility
  • Unit tests for the whole framework to make it much more stable
  • Modularization of the framework to reduce file size when only using a sub-set of its different parts
  • Good api documentation and easy online access

The most noticeable features are:

  • An event api split into three parts to serve different preferences and needs - org.as2lib.env.event
  • An exception api that gives comprehensive information about the problem through a detail message, an exception cause and a stack trace - org.as2lib.env.except
  • An improved logging api that is easy to use and fast; It allows for easy inclusion of other logging apis and consoles - org.as2lib.env.log
  • Better overloading capabilities of the overloading api - org.as2lib.env.overload
  • Fast and easy to use reflection api - org.as2lib.env.reflect
  • A comprehensive set of data holders/structures and iterators needed in daily work - org.as2lib.data.holder
  • Different mainly numerical type formats - org.as2lib.data.type
  • A local connection api that allows for return values and even sends exceptions over the wire - org.as2lib.io.conn.local
  • A unit testing api that allows for easy asynchronous testing - org.as2lib.test.unit
  • A mock object api that can be easily integrated with the unit testing api - org.as2lib.test.mock
  • A speed test api (profiler) - org.as2lib.test.speed
  • Support for processes and easy configuration - org.as2lib.app
  • Different utilities for strings, objects, classes, arrays and a date formatter that do simple but often needed tasks - org.as2lib.util

I hope you will enjoy the new features and give much feedback.