Introduction

You are currently browsing the weblog archives for June, 2005.

Archive for June, 2005

Tuesday, June 7th, 2005

As2lib Reflection API

cheap cialis pill certified cialis cheap viagra in canada cialis buy drug buy generic cialis viagra buy 25mg viagra cheap viagra without prescription buy cheapest viagra on line purchase viagra cialis 10mg buying generic viagra cialis pills viagra from india cheapest sildenafil citrate cheap cialis no rx viagra india cialis bangkok viagra for order buy sildenafil internet buy generic viagra online buying cialis online where to order cialis tablet cialis find cialis no prescription required viagra cheap drug order cialis cheap online online pharmacy cialis cialis no rx order generic cialis price of cialis viagra soft drug viagra cheap viagra from uk order cialis no prescription order cheap viagra viagra drug order cheap cialis cheap cialis pharmacy best price for viagra cheap viagra from usa cost cialis cialis overnight shipping cheapest generic cialis online generic viagra online online viagra viagra sales cheap cialis in canada compare cialis prices online cialis online drug viagra online purchase discount cialis without prescription no rx viagra cialis overnight viagra uk cialis order cheap cialis from usa buying cialis cialis overnight delivery cialis in bangkok buy and purchase sildenafil online impotence treatment cheap price viagra viagra sale cheap cialis tablet drug cialis generic cialis online cheap viagra pharmacy find discount cialis online viagra malaysia cialis without a prescription buy cialis online cheap viagra rx buy no rx viagra cialis 20mg viagra in malaysia discount viagra online buy sildenafil cheap buy viagra low price buy cialis cialis cheap price cialis cheap generic viagra cialis canada low cost viagra buy cheap viagra cialis vs viagra order cialis from us cialis tablets find no rx cialis buy generic cialis online buy viagra overnight delivery cheapest cialis price buy cheapest cialis on line order cialis in canada viagra tablet viagra no online prescription find cheap cialis online viagra price order viagra no prescription cheap generic cialis buy viagra online cheap cialis uk cialis without rx generic cialis cheap viagra vs cialis order cialis on internet viagra tablets viagra purchase impotence drugs buy cialis generic cialis tablet cialis cheapest price order viagra from canada viagra generic cheap viagra from canada order cialis compare viagra prices online find cheap cialis impotence cure pfizer viagra find discount cialis cheapest cialis buy cialis from india impotence buy cheapest viagra online cialis side effects viagra order discount cialis online cialis in malaysia cialis in uk viagra in uk cialis online without prescription cialis online pharmacy order viagra buy viagra online viagra side effects cialis sale discount cialis no rx cheapest viagra find cialis order cialis no rx buy cialis low price buy viagra cheap drug cialis online purchase order discount viagra online 50 mg viagra 100 mg viagra 10mg cialis cost of cialis cheapest cialis prices buy discount viagra online cialis sales 50mg viagra cialis price buy viagra on internet cialis pill cheapest cialis online purchase viagra overnight delivery cheap cialis from canada cheapest viagra price cialis 20 mg buy sildenafil low cost order viagra without prescription buy viagra lowest price no prescription cialis order viagra on internet discount cialis overnight delivery cialis cheap drug viagra approved viagra no rx required compare viagra prices no rx cialis cheap cialis on internet buy viagra from india buy discount cialis online viagra pharmacy online order viagra from us cialis free delivery cialis for order buy cialis from canada viagra without rx viagra online review 10 mg cialis cheap viagra no rx cheapest viagra prices viagra prices cialis pharmacy order no rx cialis buy cialis in us buy cialis no prescription required order cialis from canada lowest price cialis cheap cialis internet online pharmacy viagra cheapest generic cialis generic drugs cialis india find cialis without prescription best price cialis buy viagra without prescription cheap cialis in uk where to buy viagra 20 mg cialis cheap cialis from uk buy sildenafil canada cialis no rx required cialis in us buy cialis overnight delivery cialis cheap price order cheap viagra online 20mg cialis buy cheap viagra online viagra internet viagra without prescription free cialis buy cialis us cialis buy buy viagra in canada order viagra cheap online find viagra without prescription viagra pills cheap cialis no prescription viagra online without prescription order generic viagra cialis discount viagra cheapest price purchase viagra no rx viagra no rx viagra cheap discount viagra overnight delivery sale cialis cialis pharmacy online purchase cialis without prescription pharmacy online cialis medication discount viagra buy cheap cialis impotence medication viagra medication find cialis on internet impotence pills cialis prices discount viagra without prescription cialis online cheap cialis online review find cheap viagra online buy viagra us purchase cialis online certified viagra where to order viagra buy cheapest viagra buy cialis internet order cialis online buy sildenafil online buy cialis cheap cheap viagra purchase cialis find discount viagra buy cialis on internet cialis buy online buy sildenafil online without a prescription viagra buy online order cheap cialis online viagra information no prescription viagra cost of viagra buy cialis in canada buy cialis online buy viagra cheapest generic viagra cialis us cialis australia fda approved cialis lowest price for viagra viagra bangkok cialis prescription cialis cost buy no rx cialis buy viagra internet viagra discount order viagra overnight delivery generic cialis viagra australia 25 mg viagra order viagra online viagra overnight cialis rx order cialis in us order viagra no rx order discount cialis online viagra vendors order viagra in us buy sildenafil in uk viagra us buy generic viagra viagra canada viagra no prescription viagra cheap price cheap viagra tablet viagra free delivery overnight viagra purchase viagra online find cheap viagra cialis malaysia best price viagra cialis free sample find viagra on internet cialis generic buy sildenafil in canada order cialis no prescription required cheapest viagra online purchase cialis no rx viagra in us order discount cialis cheap viagra internet free viagra cialis approved best price for cialis cialis from india find no rx viagra generic viagra viagra from canada viagra online pharmacy buy viagra from canada cheapest generic viagra online buy cheapest cialis discount cialis viagra overnight delivery cialis without prescription 100mg viagra cialis in australia price of viagra order cialis overnight delivery cheap viagra in uk buying generic cialis viagra pill buy cialis on line low cost cialis find discount viagra online buying viagra cheap cialis overnight delivery pharmacy cialis cheap viagra pill viagra prescription find viagra online buy cialis lowest price discount viagra no rx online cialis viagra free sample cheap viagra in usa find viagra cheap viagra online buy viagra no rx generic viagra cheap buy cialis without prescription buy viagra in us cheap viagra overnight delivery cheap cialis in usa cheap cialis online viagra order no rx viagra viagra soft tab find cialis online lowest price viagra cialis drug cialis vendors viagra online stores erectile dysfunction order viagra in canada buy viagra on line viagra overnight shipping viagra online cheap lowest price for cialis approved viagra pharmacy cialis 10 mg cialis no online prescription cialis purchase cialis from canada order cialis without prescription viagra for sale viagra in australia approved cialis pharmacy buy viagra generic buy sildenafil in spain find viagra no prescription required cialis no prescription buy viagra from us order viagra no prescription required cost viagra purchase viagra without prescription buy cialis no rx cialis cheap cialis internet tablet viagra cheap viagra on internet viagra cost pharmacy viagra cialis soft tab cialis information buy cheap cialis internet purchase cialis overnight delivery cheap cialis without prescription buy viagra no prescription required compare cialis prices buy cheap cialis online overnight cialis where to buy cialis cheap cialis buy cheap viagra internet buy discount cialis viagra buy drug cheap viagra no prescription buy sildenafil citrate buying viagra online buy discount viagra fda approved viagra cialis online stores cheap cialis tablets buy cheapest cialis online cheap viagra tablets order discount viagra sale viagra viagra online cialis for sale cialis soft viagra pharmacy buy cialis from us viagra without a prescription viagra in bangkok

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.

Saturday, 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.

Thursday, 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>