Archive for the 'Flex' Category

10
Mar

Test Driven Development (TDD) with FlexUnit 4 - Complete Tutorial

After completing my preso at 360|Flex San Jose I realized how many people are having problems starting with Test Driven Development (TDD). I had many people walking up to me and telling me about their interest of starting using TDD and how it’s hard to get started. I decided to share my knowledge. This blog entry includes the following:

  • Complete Tutorial building a “real life” application
  • My 360 presentation
  • Complete Project

You can download the doc, preso and project files from here:
http://eladelrom-preso.googlecode.com/files/TDD.zip

Let’s get started.

As Flash applications become more dynamic and complex, they become more difficult to maintain and scale, particularly when business requirements change throughout the course of development. These challenges are significant and they are common in all types of development, including mobile, web, and desktop applications.

For software engineers, this problem is neither new nor confined to a specific platform. Java and ASP developers have been challenged with the same issues and have found Test Driven Development (TDD) a useful technique for creating applications that can be easily maintained.

Luckily, the new version of FlexUnit 4 just got closer in similarity to the JUnit (http://www.junit.org/) project and supports many of the features JUnit has, and more! FlexUnit4 combines features from the previous FlexUnit 1.0 features and Fluint (http://code.google.com/p/fluint/).

FlexUnit 4 allows you can create tests in order to achieve reliability, stability and maintainability of your application, however without proper understanding how to create tests and how to shift your coding style you will not be able to fully take advantage of FlexUnit 4.

The purpose of this wiki page is to give you the tools you need to shift your coding style and start using FlexUnit 4 to the fullest.

Test Driven Development quick overview

That’s exactly where TDD (Test Driven Development) comes in, but what’s TDD anyway? In short, the concept is pretty simple. You write your tests before you write your code. It’s that simple and worth repeating: write tests before code!

Test Driven Development is a software development technique in which programmers are writing a failed test that will define the functionality before writing the actual code.

Furthermore, TDD is about realizing an act of hypocrisy. As software developers our job is to be lazy. We automate repetitive tasks.

Yet the balance of the time spent developing code is actually spent testing and debugging our code manually (80% as some studies suggest). Why would you choose to do this repetitive and annoying task when you automate all of the others?

What it means is that we can let humans do the work they do best while letting computers do the work they do best and ending up with code that is more stable and more maintainable.

The concept of TDD is based on Extreme Programming (XP) development paradigm, which talks about teams that work on the development of dynamic projects with changing requirements and a development cycle that includes TDD for writing the test before the code itself. TDD is not the complete development cycle; it is only part of the Extreme Programming (XP) development paradigm. Preparing the tests before writing the code helps a development team to demonstrate their work in small steps, rather than making the customer or other stakeholders wait for the complete result.

Moving in small increments also makes it easier to accommodate changing requirements and helps ensure that your code does what it needs to do, and nothing more. It is important to mention that the focus of the TDD technique is to produce code and not to create a testing platform. The ability to test is an added benefit.

TDD is based on the idea that anything you build should be tested and if you are unable to test it, you should think twice about whether you really want to build it.

By now, there are many resources that explain how to create simple application, in order to implement TDD, however I yet to find a tutorial that gives a real application so I decided to use a real life example so you can better understand FlexUnit and TDD. While trying to come up with an example I realized that one of my tasks these days is to create an AIR application similar to Adobe MAX Companion AIR Application for a conference I am organizing called: FlashAndTheCity (http://www.flashandthecity). Creating a real application can show you how does the process goes in real development and not in a fake example that tries to make everything simple and easy, since creating applications is complex and change often even as you build your application.

Defining Application’s Objective

Understanding the application objectives is as important as coding your application. Here’s a lessons we can learn from a Master Carpenter: Measure Twice, Cut Once!

You need to understand what you are developing before you get started. In our case, we are building an application that will do the following:

1. Allow attendees to communicate with each other through twitter API.
2. The class will keep a list of tweets with #FlashAndTheCity hashtag
3. The class will check for updates of tweets often

Take a look at the Adobe MAX Companion AIR application (Figure 1), the application we are building is very similar in functionality.

11
Figure 1: Adobe MAX Companion AIR application

In order to understand the problem we need to be able to explain the problem in an everyday language.

User Stories

A good approach to take to ensure the tasks are defined well is to follow Agile software development mythology. The Agile mythologies talks about creating one or more informal sentences in an everyday or business language, this type of approach is known as a User Story. The User Story should be limited in characters and should fit a small paper note card. The user stories are usually written by the client in order to give direction to build the software. Think of this list as your todo list.

In our case here are the User Stories:

1. Retrieve tweets with #FlashAndTheCity HashTag from Twitter API.
2. Have a service call to twitter and retrieve tweets every few seconds.
3. Login into user’s twitter account and retrieve personal information.
4. Store user’s information so user wouldn’t have to login again every time.
5. Post a tweet on twitter from a user
6. Add #FlashAndTheCity Hashtag to a tweet so user wont have to type it every time and the application will be able to retrieve all tweets related to the conference.
7. Keep a list of tweets with the ability to add a tweet & remove a tweet.

Getting started

Creating the application

With the knowledge of what we need to develop we are ready to get started. The first step is to create the application open Eclipse or Flash Builder 4 Beta and create a new project name Companion (see instructions below).

• Select File > New > Flex Project to create the project.
• For the Project Name, type Companion, ensure you set the project as Desktop application type.
• Click Finish.

See figure 2.
2
Figure 2: Create new Flex project for desktop

Creating the class to be tested

The architecture we will follow is creating a utility helper class that will wrap the Twitter API and provide the ability to access the different methods in Twitter API.

Create a new class and call it TwitterHelper based the class on the EventDispacher super class so the utility class will be able to dispatch events when new tweets are available. (I recommend writing the actual class needed after writing the test, however, I wanted to show the two approaches).

3
Figure 3: Create new ActionScript Class

Once you select Finish, the class is created automatically for you, see code below:


package utils
{
     import flash.events.EventDispatcher;
     import flash.events.IEventDispatcher;

     public class TwitterHelper extends EventDispatcher
     {
          public function TwitterHelper(target:IEventDispatcher=null)
          {
               super(target);
          }
     }
}

What important more than deciding if you want to create this class or not is to ensure that the helper class is not implemented, since we need to follow TDD process and write the test before we write the actual code. Our next step is to create the Test Suite and Test case.

Creating your first Test Suite

The next step is to create a test suite.

A test suite is a composite of tests. It runs a collection of test cases. During development you can create a collection of tests packaged into test suite and once you are done, you can run the test suite to ensure your code is still working correctly after changes have been made.

To create a test suite, choose File > New > Test Suite Class (see Figure 4).

4

Figure 4: Creating a new Test Suite Class in Flash Builder 4

After you select New Test Suite Class a wizard window opens up. Fill in the following information:

• In the New Test Suite Class dialog box, name the class CompanionTestSuite.
• Select New FlexUnit 4 Test (see Figure 5).
• Click Finish.

5

Figure 5: Creating a New Test Suite Class named CompanionTestSuite

Note that Although in Extreme Programming you are encouraged to write tests before creating the code (and ideally that’s how you should work), in real life there are many times where you will find yourself writing the tests after the code. Such decisions are made case by case, and it is OK to adjust the methodology to fit your workflow.

Flash Builder 4 added the following class under the flexUnitTests folder:


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
	}
}

The Suite metadata tag indicates that the class is a suite. The RunWith tag instructs the test runner to execute the tests that follow it using a specific class. FlexUnit 4 is a collection of runners that will run a complete set of tests. You can define each runner to implement a specific interface. You can, for example, specify a different class to run the tests instead of the default runner built into FlexUnit 4.

Add your first test case class

Next, you need to create a test case. A test case comprises the conditions you want to assert to verify a business requirement or a User Story. Each test case in FlexUnit 4 must be connected to a class. To create the class, follow these steps:

Create the Test Case class:

1. Choose File > New > Test Case Class.
2. Select New FlexUnit 4 Test.
3. Type flexUnitTests as the package.
4. Type TwitterHelperTester as the name.
5. Type utils.TwitterHelper as the Class To Test (see Figure 6).

6

Figure 6: Creating a new Test Case class

In case you are creating tests for a class that already existing methods you can select Next and choose the methods you want to test. In our case we are following TDD and creating the test before writing the code so no need to select any methods and you can hit Finish to complete the process and create the Test Case class.

Important: Ensure that there is a reference in CompanionTestSuite.as to TwitterHelperTester:


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
		public var twitterHelperTester:TwitterHelperTester;
	}
}

You are ready to start writing test code. Open TwitterHelperTester.as and notice that classToTestRef reference has been created automatically for you. Here is the generated code:


package flexUnitTests
{
	import flexunit.framework.Assert;

	import utils.TwitterHelper;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}
	}
}

Implementing your User Stories

Retrieve Tweets User Story

We can start with the first User Story, Retrieve tweets with #FlashAndTheCity HashTag from Twitter API. See figure 7.

71
Figure 7: Retrieve tweets user story

In case you used FlexUnit 1 you recall that each method you create must start with “test”, to enable the test runner to recognize the method. As a result, the method name was changed to testAdditionMethod. In FlexUnit 4, method names do not need to start with “test”; instead they are recognized by the [test] metadata, so feel free to refactor the method names to names that most suite your need.

The first step in implementing the first user story is to understand what’s your goal here. In our case we are testing that the service is working correctly. We are using a public API that is maintained by Twitter and creating a Mashup application. Using a well maintain API helps us creating our application quickly, however it also store a disadvantage that in any time Twitter may change their API and our application will stop working. We are testing that the fail and success events are dispatching correctly and ensuring that the API is working, see the code below:


package flexUnitTests
{
	import flash.events.Event;

	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelper;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}

		[Test(async,timeout="500")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = "retrieveTweets";

			classToTestRef.addEventListener(EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 500 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "retrieveTweets" );
		}
	}
}

As you can see we are writing a test that the method that calls the twitter API works and it retrieve results. We are referencing a method that doesn’t exist: retrieveTweetsBasedOnHashTag.

FlexUnit 4 incorporated Fluint functionality and it supports enhanced asynchronous and includes asynchronous setup and teardown. This feature is possible by every test including the overhead of the asynchronous script. Notice that we are setting a meta data with a timeout:


[Test(async,timeout="500")]

The test will wait 500 milliseconds for to get retrieveTweets dispatched meaning that the results have been retrieved.

Working on the compilation errors

Save the file and now you see a compile time error:

Call to a possibly undefined method retrieveTweetsBasedOnHashTag through a reference with static type utils:TwitterHelper.

This is actually a good. The compiler is telling you what you need to do next. We can now create the method in TwitterHelper in order to get rid of the compiler error.


package utils
{
     import flash.events.EventDispatcher;
     import flash.events.IEventDispatcher;

     public class TwitterHelper extends EventDispatcher
     {
          public function TwitterHelper(target:IEventDispatcher=null)
          {
               super(target);
          }

		  /**
		   * Method to send a request to retrieve all the tweet with a HashTag
		   * @param hashTag defined hashtag to search
		   *
		   */
		  public function retrieveTweetsBasedOnHashTag(hashTag:String):void
		  {
			  // implement
		  }
     }
}

Notice that I only wrote the minimum code to get rid of the compiler error, I haven’t implemented the method yet.

Compile the project and we don’t have any compile time errors and you can run the tests. Select the Run icon carrot and in the drop menu select FlexUnit Tests.

8

Figure 8: Start FlexUnit tests top menu

Once you selected the Run FlexUnit Tests a wizard gives you the opportunity to select entire classes or individual methods. Select TwitterHelperTester and hit OK, see Figure 9.

9

Figure 9: Select and run FlexUnit Tests window

Review the Results

Flash Builder opens a new window where the tests are run and shows the results of your test, see below:

• 1 total test was run
• 0 were successful
• 1 was a failure
• 0 were errors
• 0 were ignored

10

Figure 10: FlexUnit Test Run results

After the result window shows up you can close it and review the results in Flash Builder

• Close the web browser that was opened by builder
• You should see a new view opened in your Flash Builder called FlexUnit Results
• If you don’t see the window select: Windows > Other Views > Flash Builder > FlexUnit Results will open it
• The Results view will show you similar information to the web browser with much more detail and interactivity
• Note the Red Bar and double click on testSampleMethod in the results view (see Figure 11).
• Flash Builder will take you to that method in your code.
• Note that the failure message in the Failure Trace is the same as the contents of the Assert.fail call.

111

Figure 11: FlexUnit result view in Eclipse

The results view provides several action buttons worth reviewing see Figure 12.
12
Figure 12: results view options

Change the Test from Fail to Pass

In order to pass the test you now need to write the actual code. At this point we wrote the test and once we write the code the test will succeed. I have implemented the code to call Twitter and retrieve results that includes #FlashAndTheCity hashtag, see below:


package utils
{
	import com.adobe.serialization.json.JSON;

	import flash.events.EventDispatcher;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	public class TwitterHelper extends EventDispatcher
	{
		/**
		 * Holds the service class
		 */
		private var service:HTTPService;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------
		public function TwitterHelper()
		{
			// implement
		}

	   /**
	    * Method to send a request to retrieve all the tweet with a HashTag
	    * @param hashTag defined hashtag to search
	    * @url	the twitter API url
	    *
	    */
	   public function retrieveTweetsBasedOnHashTag(hashTag:String, url:String):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   //--------------------------------------------------------------------------
	   //
	   //  Event handlers
	   //
	   //--------------------------------------------------------------------------

	   /**
	    * Method to handle the result of a request to retrieve all the list
	    * @param event
	    *
	    */
	   private function onResults(event:ResultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   var rawData:String = String( event.result );
		   var object:Object = JSON.decode( rawData );
		   var results:Array = object.results as Array;
		   var collection:Vector.<TweetVO> = new Vector.<TweetVO>;

		   results.forEach( function callback(item:*, index:int, array:Array):void {
			   var tweet:TweetVO = new TweetVO( item.from_user, item.from_user_id, item.geo, item.id,
				   item.profile_image_url, item.source, item.text, item.to_user, item.to_user_id );

			   collection.push( tweet );
		   });

		   // dispatch an event that holds the results
		   this.dispatchEvent( new TwitterHelperSuccessEvent( collection ) );
	   }
	}
}

Notice that I am using JSON class, which is part of AS3 CoreLib (http://code.google.com/p/as3corelib/)

Additionally, in order to achieve the test I decided to do few things such as creating a Value Object (VO) to hold the results and create a custom event that will be dispatch once the results are retrieved. The VO holds must of the properties retrieved from Twitter API, see complete code below:


package utils
{
	[Bindable]
	public final class TweetVO
	{
		public var from_user:String;
		public var from_user_id:int;
		public var geo:String;
		public var id:int;
		public var profile_image_url:String;
		public var source:String;
		public var text:String;
		public var to_user:String;
		public var to_user_id:int;		

		public function TweetVO(from_user:String, from_user_id:int, geo:String, id:int, profile_image_url:String,
							source:String, text:String, to_user:String, to_user_id:int)
		{
			this.from_user = from_user;
			this.from_user_id = from_user_id;
			this.geo = geo;
			this.id = id;
			this.profile_image_url = profile_image_url;
			this.source = source;
			this.text = text;
			this.to_user = to_user;
			this.to_user_id = to_user_id;
		}
	}
}

Additionally, I have created a custom event that will hold the constant event type and allow passing the collection of tweets received, see complete code below:


package utils
{
	import flash.events.Event;

	public class TwitterHelperSuccessEvent extends Event
	{
		public static const RETRIEVE_TWEETS:String = "retrieveTweets";

		public var collection:Vector.<TweetVO>;

		public function TwitterHelperSuccessEvent( collection:Vector.<TweetVO> )
		{
			this.collection = collection;
			super( RETRIEVE_TWEETS );
		}
	}
}

Keep it simple: to the sake of simplicity I am keeping all the classes related to our class under the same package but feel free to refactor and place the event under an event folder. Also I haven’t implemented the clone method, which is recommended, but feel free to add these changes.

Run the test again and observe the results, see Figure 13.

• A test that does not fail succeeds
• Click the Run Completed Button
• Observe the Green Bar that indicates success

13
Figure 13: FlexUnit results view showing green bar

One thing to note is that I have set the service to 500 milliseconds, however the request took longer and I the test still failed so I quickly adjusted the code to wait 20000 milliseconds (20 seconds) to accommodate cases where the network is slow, feel free add a test to ensure the respond is quick enough.

To complete the user story we need to also test fault request, so just as we did before let’s write the test and than implement the code.

Add the following test:


[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = "serviceFailure";

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}

		private function handleAsyncFaultEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "serviceFailure" );
		}

Run the test and watch the fail results. Refactor TwitterHelper to include the fail method.


 	   public function retrieveTweetsBasedOnHashTag( hashTag:String, url:String ):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);
		   service.addEventListener(FaultEvent.FAULT, onFault);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   /**
	    * Holds the fault method in case the service failed
		 *
	    * @param event
	    *
	    */
	   private function onFault(event:FaultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   this.dispatchEvent( new TwitterHelperFailureEvent( event.fault.message ) );
	   }

Make sure to create the custom event for the fail event:


package utils
{
	import flash.events.Event;

	public class TwitterHelperFailureEvent extends Event
	{
		public static const SERVICE_FAILURE:String = "serviceFailure";

		public var message:String;

		public function TwitterHelperFailureEvent( message:String )
		{
			this.message = message;
			super( SERVICE_FAILURE );
		}
	}
}

Test again and you should see a green light, see Figure 14.

14
Figure 14: Result view showing our two test completed successfully

Refactor

At this point, we can do a small refactoring to the test case and include the static method from the custom event instead of having the string attached, which will ensure our tests still pass in case we refactor the event type string, see code below:


		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = TwitterHelperSuccessEvent.RETRIEVE_TWEETS;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
                              classToTestRef = new TwitterHelper();
			var EVENT_TYPE:String = TwitterHelperFailureEvent.SERVICE_FAILURE;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}

Tests have duplication that can become painful to maintain

• In our case, we need to instantiating TwitterHelper in each test.
• Granted, there are just two, but this will grow
• We can solve this problem by using additional metadata provided by FlexUnit 4 called [Before] and [After]
• [Before] can be applied to any method that you wish called before each test.
• [After] will be called after each test

For good measure, let’s add a method name tearMeDown() and mark it with the [After] metadata.

• The [After] method gives you a place to clean up from your test case.
• In this case just set classToTestRef equal to null so the instance is available for garbage collection
• In more complicated tests it is often crucial to remove listeners, etc. in this way. In our case TwitterHelper is already removing the listeners so we don’t need to do that, but many other times you will.


		[Before]
		public function setMeUp():void
		{
			classToTestRef = new TwitterHelper();
		} 

		[After]
		public function tearMeDown():void
		{
			classToTestRef = null;
		}

See the complete test code below:


package flexUnitTests
{
	import flash.events.Event;

	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelper;
	import utils.TwitterHelperFailureEvent;
	import utils.TwitterHelperSuccessEvent;

	public class TwitterHelperTester
	{
		// Reference declaration for class to test
		private var classToTestRef : utils.TwitterHelper;

		public function TwitterHelperTester()
		{
		}

		[Before]
		public function setMeUp():void
		{
			classToTestRef = new TwitterHelper();
		} 

		[After]
		public function tearMeDown():void
		{
			classToTestRef = null;
		}		

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTag():void
		{
			var EVENT_TYPE:String = TwitterHelperSuccessEvent.RETRIEVE_TWEETS;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "http://search.twitter.com/search.json");
		}

		[Test(async,timeout="20000")]
		public function testRetrieveTweetsBasedOnHashTagFail():void
		{
			var EVENT_TYPE:String = TwitterHelperFailureEvent.SERVICE_FAILURE;

			classToTestRef.addEventListener( EVENT_TYPE, Async.asyncHandler( this,  handleAsyncFaultEvnet, 20000 ), false, 0, true );
			classToTestRef.retrieveTweetsBasedOnHashTag("FlashAndTheCity", "");
		}		

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "retrieveTweets" );
		} 		

		private function handleAsyncFaultEvnet(event:Event, passThroughData:Object):void
		{
			Assert.assertEquals( event.type, "serviceFailure" );
		}
	}
}

In addition to refactor the tests you should also refactor the actual code. We focus on passing the test and didn’t care that much about the code, however you may find out that the code is too complex and can be simplify by implementing a design pattern, or just adding some small modification. For instance, I can add metadata so when you instantiate the class and add event listeners in MXML component you will get the event type available automatically. Add the code below to the TwitterHelper class:


	/**
	 *  Success custom event
	 *
	 *  @eventType utils.TwitterHelperSuccessEvent.RETRIEVE_TWEETS
	 */
	[Event(name="retrieveTweets", type="utils.TwitterHelperSuccessEvent")]

	/**
	 *  Failure custome event
	 *
	 *  @eventType utils.TwitterHelperFailureEvent.SERVICE_FAILURE
	 */
	[Event(name="serviceFailure", type="utils.TwitterHelperFailureEvent")]

You can see the complete code for the TwitterHelper class below:


package utils
{
	import com.adobe.serialization.json.JSON;

	import flash.events.EventDispatcher;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;

	/**
	 *  Success custom event
	 *
	 *  @eventType utils.TwitterHelperSuccessEvent.RETRIEVE_TWEETS
	 */
	[Event(name="retrieveTweets", type="utils.TwitterHelperSuccessEvent")]

	/**
	 *  Failure custome event
	 *
	 *  @eventType utils.TwitterHelperFailureEvent.SERVICE_FAILURE
	 */
	[Event(name="serviceFailure", type="utils.TwitterHelperFailureEvent")]	

	public class TwitterHelper extends EventDispatcher
	{
		/**
		 * Holds the service class
		 */
		private var service:HTTPService;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------
		public function TwitterHelper()
		{
			// implement
		}

	   /**
	    * Method to send a request to retrieve all the tweet with a HashTag
	    * @param hashTag defined hashtag to search
		* @url	the twitter API url
	    *
	    */
	   public function retrieveTweetsBasedOnHashTag( hashTag:String, url:String ):void
	   {
		   service = new HTTPService();
		   service.url = url;
		   service.resultFormat = "text";

		   service.addEventListener(ResultEvent.RESULT, onResults);
		   service.addEventListener(FaultEvent.FAULT, onFault);

		   var object:Object = new Object();
		   object.q = hashTag;
		   service.send( object );
	   }

	   //--------------------------------------------------------------------------
	   //
	   //  Event handlers
	   //
	   //--------------------------------------------------------------------------

	   /**
	    * Method to handle the result of a request to retrieve all the list
	    * @param event
	    *
	    */
	   private function onResults(event:ResultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   var rawData:String = String( event.result );
		   var object:Object = JSON.decode( rawData );
		   var results:Array = object.results as Array;
		   var collection:Vector.<TweetVO> = new Vector.<TweetVO>;

		   results.forEach( function callback(item:*, index:int, array:Array):void {
			   var tweet:TweetVO = new TweetVO( item.from_user, item.from_user_id, item.geo, item.id,
				   item.profile_image_url, item.source, item.text, item.to_user, item.to_user_id );

			   collection.push( tweet );
		   });

		   // dispatch an event that holds the results
		   this.dispatchEvent( new TwitterHelperSuccessEvent( collection ) );
	   }

	   /**
	    * Holds the fault method in case the service failed
		 *
	    * @param event
	    *
	    */
	   private function onFault(event:FaultEvent):void
	   {
		   service.removeEventListener(ResultEvent.RESULT, onResults);
		   service.removeEventListener(FaultEvent.FAULT, onFault);

		   this.dispatchEvent( new TwitterHelperFailureEvent( event.fault.message ) );
	   }
	}
}

Retrieve tweets every few seconds User Story

The second user story objective is to call the same method we just created every X seconds so we can keep retrieving tags that contain FlashAndTheCity keyword from Twitter, see Figure 15.

151
Figure 15: Retrieve tweets every few seconds User Story

This type of logic doesn’t have to be included in the utility class we created since it’s a specific implementation of the class and I prefer to keep the class generic so it can be re-used.

In the application I am creating I decided to avoid using any micro-architecture framework since the application is simple in nature and I am the only developer working on the application so no need to add complexity.

The architecture I prefer to implement is to separate the API from the implementation and handle this logic in the actual implementation of the class rather than the utility class. To achieve that I will create a design patter that will help me to separate the view and logic. It’s highly recommended to use some sort of presentation model (also known as code behind), so you can easily separate the data and logic from the view, state and transitions (animations).

This type of separation will allow you to better test your code. In my case I decided to implement the Passive Presentation Model, but there are many other ways to achieve the same goal. I am not going to go into too much detail, but feel free to visit the following link to find out more:
http://blogs.adobe.com/paulw/archives/2007/11/presentation_pa_6.html#more

Since we are using a different class for the implementation this user story I would create a new test case for this class. If you recall last time we created the class before the test case, this time I will create the Test Case and once I get compile time error I will add the class.

Create a new Test Case and fill in the information below, see Figure 16.

• Use TweetListPresenterTester as the name
• New FlexUnit 4 test
• Select Finish

16

Figure 16: Create TweetListPresenterTester Test Case Class

Make sure to add a reference of the Test Case to the Test Suite, otherwise it wont run the test.


package flexUnitTests
{
	[Suite]
	[RunWith("org.flexunit.runners.Suite")]
	public class CompanionTestSuite
	{
		public var twitterHelperTester:TwitterHelperTester;
		public var tweetListPresenterTester:TweetListPresenterTester;
	}
}

The unit test will include a reference to the presenter and will test the method that retrieve tweets every few seconds.


package flexUnitTests
{
	import flexunit.framework.Assert;

	import org.flexunit.async.Async;

	import utils.TwitterHelperSuccessEvent;

	public class TweetListPresenterTester
	{
		// Reference declaration for class to test
		private var classToTestRef : presenter.TweetListPresenter;

		public function TweetListPresenterTester()
		{
     classToTestRef = new TweetListPresenter();
		}

		[Test(async,timeout="2000")]
		public function testRetrieveTweetsEveryFewSeconds():void
		{
			classToTestRef.twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, Async.asyncHandler( this, handleAsyncTweetSuccessEvnet, 2000 ), false, 0, true );
			classToTestRef.retrieveTweetsEveryFewSeconds( 4 );
		}

		//--------------------------------------------------------------------------
		//
		//  Asynchronous handlers
		//
		//--------------------------------------------------------------------------

		private function handleAsyncTweetSuccessEvnet(event:TwitterHelperSuccessEvent, passThroughData:Object):void
		{
			Assert.assertTrue( event.collection.length > 0 );
		}
	}
}

After you compile the application you will get the following compile time errors:

1. Type was not found or was not a compile-time constant: TweetListPresenter
2. Call to a possibly undefined method retrieveTweetsEveryFewSeconds through a reference with static type presenter:TweetListPresenter
3. Access of possibly undefined property twitterHelper through a reference with static type presenter:TweetListPresenter.

Once again, these compile time errors are a good thing, so our first task is to solve these errors by creating the TweetListPresenter class, method retrieveTweetsEveryFewSeconds and property twitterHelper. The test will start the method that should create a timer and call the service every four seconds. Once the service is called we should retrieve some data to indicate that the test succeeded.

I have created TweetListPresenter class and added the method and property so the compile time errors will be fixed see code below:


package presenter
{
	import utils.TwitterHelper;

	public class TweetListPresenter
	{
		public var twitterHelper:TwitterHelper;

		public function TweetListPresenter()
		{
			twitterHelper = new TwitterHelper();
		}

		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			// implement
		}
	}
}

Run the test and you will see that it fails since we haven’t created the logic yet, see Run FlexUnit Tests wizard below.

17

171
Figure 17: Run FlexUnit Tests wizard to test testRetriveTweetsEveryFewSeconds method

18
Close the results view and observe the results, see Figure 18.

Figure 18: Results view shows testRetriveTweetsEveryFewSeconds have failed

As I explained the utility class we created is a wrapper for the Twitter API and doesn’t include any implementation logic, with that said by creating a presenter we can separate the view from the logic and better test the implementation logic.

The approach is to create a passive presenter that controls the view components passively. Create the application entry point and paste the following code, which will add a view to the application and pass that view to the presenter so it can adjust the view properties, such as adding a list of tweets to a datagrid:


<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
					   xmlns:s="library://ns.adobe.com/flex/spark"
					   xmlns:mx="library://ns.adobe.com/flex/halo"
					   xmlns:view="view.*"
					   creationComplete="creationCompleteHandler()">
	<fx:Script>
		<![CDATA[
			import presenter.TweetListPresenter;

			private var tweetListPresenter : TweetListPresenter;

			protected function creationCompleteHandler():void
			{
				tweetListPresenter = new TweetListPresenter( tweetListView );
			}

		]]>
	</fx:Script>

	<view:TweetListView id="tweetListView" />

</s:WindowedApplication>

Notice that we are passing an instance of the view so we can control the view based on the logic in the presenter class. Next adjust the constructor in the presenter class:


		// Corresponding view
		private var _tweetListView : TweetListView;

		public function TweetListPresenter( tweetListView:TweetListView )
		{
			_tweetListView = tweetListView;

			twitterHelper = new TwitterHelper();
		}

Additionally, create an empty view and name it TweetListView.mxml that we will be using to place the view components and sub-components once we are ready:


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

</s:Group>

At this point we can compile the application and see an empty screen since we didn’t added any view components.

Write code

We are now ready to write the code needed for our test to pass.


package presenter
{
	import flash.events.TimerEvent;
	import flash.utils.Timer;

	import mx.collections.ArrayCollection;

	import utils.TweetVO;
	import utils.TwitterHelper;
	import utils.TwitterHelperFailureEvent;
	import utils.TwitterHelperSuccessEvent;

	import view.TweetListView;

	public class TweetListPresenter
	{
		/**
		 * Corresponding view
		 */
		private var _tweetListView : TweetListView;

		/**
		 *  Twitter helper utility class instance
		 */
		public var twitterHelper:TwitterHelper;

		/**
		 *   Holds a timer so we will be able to update list
		 */
		public var timer:Timer;

		//--------------------------------------------------------------------------
		//
		//  Default Constructor
		//
		//--------------------------------------------------------------------------

		public function TweetListPresenter( tweetListView:TweetListView )
		{
			_tweetListView = tweetListView;

			twitterHelper = new TwitterHelper();
			twitterHelper.addEventListener( TwitterHelperSuccessEvent.RETRIEVE_TWEETS, onRetrieveTweets );
			twitterHelper.addEventListener( TwitterHelperFailureEvent.SERVICE_FAILURE, onFaultRequest );
		}

		/**
		 * Method to go and retireve tweets every defined number of seconds
		 *
		 * @param seconds
		 *
		 */
		public function retrieveTweetsEveryFewSeconds( seconds:int ):void
		{
			timer = new Timer( seconds*1000, 100000 );
			timer.addEventListener(TimerEvent.TIMER, onTimerHandler);

			timer.start();
		}

		//--------------------------------------------------------------------------
		//
		//  Handlers
		//
		//--------------------------------------------------------------------------		

		/**
		 * Method to handle a timer event
		 *
		 * @param event
		 *
		 */
		private function onTimerHandler( event:TimerEvent ):void
		{
			twitterHelper.retrieveTweetsBasedOnHashTag( "FlashAndTheCity", "http://search.twitter.com/search.json" );
		}

		/**
		 * Handler for results
		 *
		 * @param event
		 *
		 */
		private function onRetrieveTweets( event:TwitterHelperSuccessEvent ):void
		{
			var dataProvider:Array = new Array();

			event.collection.forEach( function callback(item:TweetVO, index:int, vector:Vector.<TweetVO>):void {
				dataProvider.push( item );
			} );

			this._tweetListView.dataGrid.dataProvider = dataProvider;
		}

		/**
		 * Handler for fault
		 *
		 * @param event
		 *
		 */
		private function onFaultRequest( event:TwitterHelperFailureEvent ):void
		{

		}
	}
}

The code have a method called retrieveTweetsEveryFewSeconds, which starts a timer and allow us to call twitterHelper.retrieveTweetsBasedOnHashTag every few seconds. Add a DataGrid to the TweetListView.mxml so we can show the results;


<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"
		 xmlns:s="library://ns.adobe.com/flex/spark"
		 xmlns:mx="library://ns.adobe.com/flex/halo"
		 width="400" height="300">

	<mx:DataGrid id="dataGrid" width="500" height="400" />

</s:Group>

Run the test and observe the passed test in the result view, see Figure 19:

19

Figure 19: Results View window showing testRetriveTweetsEveryFewSeconds Method passed

At this point you can also run the application and see the DataGrid shows results every four seconds, see Figure 20.
20

Figure 20: Companion application shows twitter results in a DataGrid

Refactor

I have done some additional refactoring to the code and you can download the complete project from here: Companion.fxp

Conclusion

This article covered Test Driven Development in Flex projects. We first defined the project we are creating by defining our user stories and than created the tests using FlexUnit 4. We followed the TDD process without suger coat the process, but showing a real realistic example you can follow and allow you to understand the process of using TDD.

After learning how to create FlexUnit test suites and test cases as well as follow the process, I hope you are inspired to use TDD on your mobile, web, and desktop Flash applications, and write better, more scalable, and more reusable code.

You can download the doc, preso and project files from here:
http://eladelrom-preso.googlecode.com/files/TDD.zip

Cheers :)

19
Dec

Adobe closed major security concerns with Flash Player 10.0.42.34 however these changes may effect the ability to access swfs when setting Security.allowDomain

It appears that Adobe closed some major security concerns with Flash Player 10.0.42.34 see here, however I believe these security concerns have changed the ability to access swf when setting Security.allowDomain.

ASDOC says:

“If two SWF files are served from different domains — for example, http://siteA.com/swfA.swf and http://siteB.com/siteB.swf — then, by default, Flash Player does not allow swfA.swf to script swfB.swf, nor swfB.swf to script swfA.swf. A SWF file gives SWF files from other domains by calling Security.allowDomain(). This is called cross-domain scripting. By calling Security.allowDomain(”siteA.com”), siteB.swf gives siteA.swf permission to script it.” > see here.

In the example below I am creating the example ASDOC is describing. I will create two application which will be hosted on two separate domain names, however I am unable to change properties in the accessed application, see detail below:

Accessed application will holds a label and set the Security.allowDomain to wild card (*) so any application should be able to load this swf and change properties. See below:


<Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   minWidth="1024" minHeight="768" preinitialize="application1_initializeHandler(event)">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;

			protected function application1_initializeHandler(event:FlexEvent):void
			{
				Security.allowDomain("*");
			}

		]]>
	</fx:Script>

	<Label id="label" text="Hello from accessed application!"  x="8" y="9"/>

</s:Application>

The second application (accessing application) will be hosted on a separate domain and load the accessed swf and change the label property:


<Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   minWidth="1024" minHeight="768"
			   initialize="initializeHandler()">
	<fx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.events.FlexEvent;

			// define variables
			private var loader:Loader;
			private var content:*;

			// load swf
			private function initializeHandler():void
			{
				loader = new Loader();
				loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
				loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadContent_onComplete);
				loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				loader.load(new URLRequest("http://zeen.com/temp/bin-debug/AccessedApplication.swf"));

				component.addChild(loader);
			}

			// Event Handler

			private function loadContent_onComplete(event:Event):void
			{
				content = event.target.content;

				var onContentApplicationComplete:Function = function(event:Event):void
				{
					// content loaded successfully
				}

				content.addEventListener(FlexEvent.APPLICATION_COMPLETE, onContentApplicationComplete);
			}

			private function ioErrorHandler(event:IOErrorEvent):void
			{
				loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
				Alert.show(event.text);
			}

			private function securityErrorHandler(event:SecurityErrorEvent):void
			{
				loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
				Alert.show(event.text);
			}          

			// methods to access loaded swf

			private function callAccessedApplication():void
			{
				this.content.document.label.text = "label change!";
			}

		]]>
	</fx:Script>

	<mx:UIComponent id="component" width="400" height="82" x="11" y="43" />

	<Button label="Call accessed application"
			  click="callAccessedApplication()" x="84" y="0"/>   

</s:Application>

I also have set the cross-domain policy on the accessed applciation server to allow access:


<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*"/>
</cross-domain-policy> 

The swf is loaded successfully, but with a warning alert. Additionally, I am unable to change the label properties on the accessed application, see screen shot below:

screen-shot-2009-12-19-at-71327-pm

The application is hosted here:
http://eladelrom.com/bin-debug/AccessingApplication.html

I am hoping to get a clarification: https://bugs.adobe.com/jira/browse/FP-3513

01
Dec

Test Driven Development using Flash Builder 4 beta and FlexUnit on Devnet

There has been a great deal of interest in TDD (and unit testing in general) recently, because it leads to applications that are easier to scale and maintain and less prone to errors. I just published an article on Devnet that covered unit testing and Test Driven Development using Flash Builder 4 and FlexUnit, including some of the new features in the FlexUnit 4 framework.

Check it out here:
http://www.adobe.com/devnet/flex/articles/flashbuilder4_tdd_02.html

05
Nov

Object pooling in Flex / ActionSctipt

Back in March of 2008 I published an object pooling utility class that allow you to place objects into a collection and use them when needed. That class came handy on a recent project where I had to use few instances of DataGrid, which is known to be one of the most expensive components in Flex which many believe should be avoided at all cost. The app I built had to create instances of data grid during runtime and than remove them from the display object. Object pooling is a great design pattern to handle these cases where you need to use the same object over again and again.

The reason is that object creation takes much resources and should be avoided in cases where you need the object often. I looked over the class and although I wrote it in 2008 it is still in good shape. One thing I noticed is that it was using an ArrayCollection. I have upgraded the class to use a Dictionary instead of ArrayCollection using a HashCollection utility class I developed which is using the Dictionary with a weak reference.

Download and look at the ReusablePool utility class:
http://code.google.com/p/eladlib/source/browse/trunk/EladLibFlex/src/com/elad/framework/objectpoolmanager/ReusablePool.as

To implement the class and show you an example I will be using an application that uses three objects: video, image and a list component. When you add the element to the application and then remove it and keep adding these objects agains and again you put a memory expense and you can see a small hickup. However, when you are using Object pool you keep the memory usage low since the objects are cached and you save on the object instantiation cost. As you can see the application is running smoothly while adding and removing objects from the display.

The complete implementation code can be seen below:


<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/halo"
			   minWidth="1024" minHeight="768"
			   creationComplete="setObjects()"
			   viewSourceURL="srcview/index.html">	

     <fx:Script>
          <![CDATA[
               import com.elad.framework.objectpoolmanager.Reusable;
               import com.elad.framework.objectpoolmanager.ReusablePool;

               import mx.collections.ArrayCollection;
               import mx.containers.Canvas;
               import mx.controls.Image;
               import mx.controls.VideoDisplay;
               import mx.core.UIComponent;

               import net.hires.debug.Stats;

               private var reusablePool:ReusablePool = ReusablePool.getInstance();
               private var canvas:Canvas = new Canvas;

               private function setObjects():void
               {
                    // adding stats
                    component.addChild( new Stats() );
                    addElement(component);                         

                    // adding the object to the collection
                    reusablePool.setReusable( new Reusable(createUI(), "FlexImage") );

                    // adding an array object
                    reusablePool.setReusable( new Reusable(createArray(), "arrayList") );

                    // adding video UI object
                    reusablePool.setReusable( new Reusable(createVideoUI(), "videoUI") );                                                            

                    this.addElement(canvas);
               }

               private function useUIObject(name:String):void
               {
                    // getting the collection
                    var reusable:Reusable = reusablePool.acquireReusable(name);
                    var component:UIComponent = reusable.object as UIComponent;

                    // use the object
                    canvas.removeAllChildren();
                    canvas.addElement(component);

                    // return collection back to the pool of objects
                    reusablePool.releaseReusable(reusable);
               }

               private function useArrayObject():void
               {
                    // getting the collection
                    var reusable:Reusable = reusablePool.acquireReusable("arrayList");
                    var dp:ArrayCollection = reusable.object as ArrayCollection;

                    list.dataProvider = dp;

                    // return collection back to the pool of objects
                    reusablePool.releaseReusable(reusable);
               }

               private function noCacheArrayObject():void
               {
                    var dp:ArrayCollection = createArray();
                    list.dataProvider = dp;
               }

               private function noCacheUIObject():void
               {
                    canvas.removeAllChildren();
                    canvas.addElement(createUI());
               }

               private function noCacheVideoUIObject():void
               {
                    canvas.removeAllChildren();
                    canvas.addElement(createVideoUI());
               }     

               private function createUI():UIComponent
               {
                    var image : Image = new Image();
                    image.source = "http://blog.digitalbackcountry.com/wp-content/uploads/flash_builder_logo.png";
                    image.width = 400;
                    image.height = 300;
                    image.x=10;
                    image.y=300;                    

                    return image;
               }

               private function createVideoUI():UIComponent
               {
                    var videoDisplay:VideoDisplay = new VideoDisplay();
                    videoDisplay.source = "http://kakonacl.dip.jp/PlayerTry/TestMovies/FLV/FLV4/Flash.flv";
                    videoDisplay.x = 10;
                    videoDisplay.y = 300;
                    videoDisplay.width = 300;
                    videoDisplay.height = 300;

                    return videoDisplay;
               }

               private function createArray():ArrayCollection
               {
                    var array:ArrayCollection = new ArrayCollection();

                    for (var i:int=0; i<10000; i++)
                    {
                         array.addItem({label: i});
                    }

                    return array;
               }               

          ]]>
     </fx:Script>

     <s:Label x="10" y="10"
                text="These examples shows memory usage with using the object pooling collection and without it."
                fontWeight="bold" height="33"/>

     <s:Label x="10" y="61"
                text="Cached Object Pooling" width="200" fontWeight="bold"/>

     <s:Button x="10" y="87"
                 label="UIobject"
                 click="useUIObject('FlexImage');" width="200"/>

     <s:Button x="10" y="117"
                 label="array"
                 click="useArrayObject();" width="200"/>                  

     <s:Button x="10" y="147"
                 label="Video UI"
                 click="useUIObject('videoUI');" width="200"/>

     <s:List id="list"
               x="325" y="79"
               width="126" height="200" />

     <s:Label x="10" y="178"
                text="Regular objects" width="200" fontWeight="bold"/>

     <s:Button x="10" y="202"
                 label="UIobject"
                 click="noCacheUIObject();" width="200"/>

     <s:Button x="10" y="232"
                 label="Array"
                 click="noCacheArrayObject()" width="200"/>          

     <s:Button x="10" y="262"
                 label="Video UI"
                 click="noCacheVideoUIObject()" width="200"/>

     <mx:UIComponent id="component" x="237" y="83" width="70" height="100"/>     

</s:Application>

screen-shot-2009-11-05-at-104133-pm

To measure the memory usage you can use the small statistic class that was developed by Mr.doob. Additionally, you can take a look at the Eclipse Profiler. In first scenario where I add an element to the application and remove it, the profiler shows as if we have a memory leak since the usage peaked to the “red” lines. I am not saying this is a memory leak, but I am pointing out that the memory usage is high since the objects were removed but haven’t been picked up by the GC yet. As you know using the removeAllChildren() remove the object from the component but is not necessarily ensure the GC will come and release the memory right away. See Profiler screen shot below:

screen-shot-2009-11-05-at-105952-pm

In the second scenario, the objects are cached and I am not putting any memory constrains on my application. See figure below.

screen-shot-2009-11-05-at-110137-pm

The utility class can be limited to the amount of objects you want to store, plus you can tie the class to a logic that will clear all the objects when needed.

Let’s take a look at the code to create an instance of the utility class and add an object.

The utility class is a singleton so we can use the same object through the life cycle of the application.


private var reusablePool:ReusablePool = ReusablePool.getInstance();

To add an object you create a Reusable object that holds the object and the name of the object:


// adding the object to the collection
reusablePool.setReusable( new Reusable(createUI(), "FlexImage") );

When you are ready to retrieve the object use the acquireReusable method, which will remove the object from the object collection:


// getting the collection
var reusable:Reusable = reusablePool.acquireReusable(name);
var component:UIComponent = reusable.object as UIComponent;

When you are done and want to send the object back to the object pool collection use:


reusablePool.releaseReusable(reusable);

Cheers :)

05
Nov

Flex (Flash) Camp Wall Street conference in two weeks

I will be speaking this year at the Flex (Flash) Camp Wall Street conference. Although the recession is not over yet, the second edition of the prestige Flex (Flash) Camp Wall Street conference will be held in New York again. The event will be on November 16-17, 2009 and has a recession price of $49!

wallst

I am very excited for the opportunity to participate at the conference.
Speaker includes James Ward, Yakov Fain, Jeff Tapper, Jeffry Houser, Wade Arnold, Christian Saylor, Brian O’Connor, Daniel Holth and Adam Flater.

For more details goto:
http://www.flexcampwallstreet.com

07
Oct

AIR 2.0 Enhancements Complete Overview

During MAX 2009, Adobe released public information about AIR 2.0 and added new capabilities that better tie with the operation systems which gives your application more control while increasing performance. Below is an application I created that will help you overview the new capabilities. To use the application, click each main category and browse to see the capability.

This small application complimentary to the InsideRIA article I published:
http://www.insideria.com/2009/10/air-2-enhancements-complete-ov.html

01
Sep

Convert ByteArray into an image and NetStream limitation

Flash Player 10 and AIR allow loading files from your local system as well as saving files. Once the files are loaded you have access to the ByteArray.

Convert ByteArray into an image

You can easily convert the ByteArray into an image:


var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(event:Event):void
{
      event.currentTarget.removeEventListener(event.type, arguments.callee);
      image.source=loader.content;
});

loader.loadBytes(event.file.fileContent);

Play ByteArray

Unfortunately, you cannot set the bytes of a video stream through the NetStream Object. What you can do is write the URLStream’s bytes to a local file, and then pass that file to the NetStream object. You can do a workaround such as writing out the URLStream’s bytes as they come in with the help of the “append” method.

What it means is that you cannot play ByteArray in AIR or Flex Application. I know that that feature is on the Adobe’s team list but until they adjust the code you will have to find a workaround.

01
Sep

Tips & Tricks: Mixing Halo and Spark components

Mixing Halo and Spark components is still not working perfectly and causing all sort of problems, but in case you need to do inline rendering using Halo component, it’s possible. This is how you would do it for a Spark list component:


	<s:List id="output">
		<s:itemRenderer>
			<fx:Component>
				<mx:HBox>
					<mx:Image source="{data.thumbnail}" height="50" width="50" />
					<mx:Label text="{data.label}" width="300" height="20" />
				</mx:HBox>
			</fx:Component>
		</s:itemRenderer>
	</s:List>
29
Jul

Flash Catalyst: not just a designer tool - developers workflow techniques

Flash Catalyst enables designers to increase their creative productivity and leverage their existing skills to create a stand-alone Flex application. It also facilitates collaborating in a team when creating Flex applications. Designers can create their graphics using Adobe Photoshop CS4, Illustrator CS4, or Adobe Fireworks CS4, and then convert these graphics into a Flex application by importing the native files into Catalyst, while keeping the layers intact. Catalyst acts as a bridge between Flex Builder and CS4.

This is all great, but as a developer you shouldn’t wait for designers to adopt to Catalyst and you can leverage Catalyst today.

In this post, I would like to talk about integration and workflow between Flash Catalyst and Flex working with Catalyst as a developer tool. This blog post is based on my experience working with Catalyst and is based on creating production grade applications. I believe that Flash Catalyst is a valuable tool and once you get to work with it and utilize it well, you will find it hard to work without it, mainly since it will save you valuable development time.

While Flash Catalyst got great reviews by many people since it was released as Beta, I also hear criticism in the Flex community about the tool and I would like to touch that before we diving into workflow tips.

This is the image from the T-Shirt given to participants during FlashCamp San Francisco this year:

catalyst

I am not sure if Flash Catalyst is really a gift to the designer?! Remember that old TV show Smurfs:

catalystgift1

I think that the source of the criticism is because of the following reasons:

  • Catalyst was labeled as a designer tool - Adobe put a label on Flash Catalyst and categorized the tool as a designer tool. Even the t-shirt in the above picture (from Adobe’s San Francisco Flash Camp event) labeled the product for designers, and not just designers, but female designers! We see the same on the Catalyst site (http://labs.adobe.com/technologies/flashcatalyst/) where you have a male developer on one side and female on the other side. I have to tell you, I have a lot of designer friends that are male and they are as talented and as capable as female designers. I personally believe that Adobe should release the product without putting any labels on who should use it.
  • High expectations - Adobe’s Marketing department has portrayed the product as if it allows seamless and effortless round tripping integration between CS4 and Flex, when in reality the round trip is not as seamless as described. Lower your expectations before using the tool.
  • Designer? tough pitch! - it’s a hard to tell designers “look, I will give you this amazing tool that will add work for you”. Designers are busy just as much as developers and many are not going to be as thrilled to adopt to a new tool that will require them to put more time and add them more responsibilities.
  • Adoption - designers adopting the technology may take a year or two since although it’s similar to other design products, there is still a learning curve and round tripping should be improved for better integration.


Who feels the pain? Designers or developers?

When it comes to skinning, interactivity and converting pixels to code, I believe that developers are feeling the pain much more than designers, since every time they need to skin a component or create interactivity, coding in Flex 3 is a tedious process and not easy unless you do it on a regular basis. Sure, designers feel the pain when their work is degraded during translation of graphics to code, but it’s usually solved by going over the work with the developer and adjusting the pixels.

4

Developers can benefits from using Catalyst:

  • Speed: increase your development time for simple to complex projects.
  • Agile workflow: work parallel with designers
  • Interaction design: add interactivity with the help of a designer.
  • Consistent graphics: There’s no need to slice graphics and skin components, since Catalyst takes care of that part, and the files created in CS4 convert easily.
  • Prototyping: create prototype applications.
  • Developers tool - you can leverage Catalyst as a developer without knowing how to use Illustrator or Photoshop.
  • How you can leverage Flash Catalyst as a developer:

    Create your view in Catalyst and integrate it using the presentation model

    Before using Catalyst you would get a comp from a designer and convert it into code. That process, at least to me, was a painful process that I always hated since I have to bother myself with tweaking CSS files and creating custom components which takes a lot of effort on complex components. With Catalyst you can import Illustrator / Photoshop files to Catalyst without opening CS products. I found that the best approach is to identify the different views you would like to break your application into components instead of trying to create one application. When you create an application you usually break your views to different components and I would do the same with Catalyst. What I mean is that you would figure out how you would like to split your view and create Catalyst projects based on that.

    Your assumption should be that you will have to change the Catalyst’s code and adjust it instead of assuming that the code Catalyst will spit out will be production grade code. Once you completed your work with Catalyst you can import, the FXP (Flash XML Project) file into Flash Builder and separate the ‘view’ from the ‘logic’ using the presentation model (similar to code behind) design pattern. There are many presentation model approaches, but the key is to have a ‘clean’ code that doesn’t have functionality so you can easily make changes to the view and follow Flex 4 and Catalyst architecture of separating pixels from the plumbing. You would import it as it’s own project and copy paste the classes you need while refactoring the location and names, as well as changing the generated code so it’s more readable.

    Skin component

    Another useful technique I found with Flash Catalyst is to skin components or at least create the scaffolding to skin components. For instance if you want to skin a button, sure you can code the class and states from scratch, but it will be done faster with Catalyst. Using Catalyst you can easily take an Illustrator file and convert a pixel to a button or any of the available components in Catalyst as well as create the states and interactivity which will save you time as well as generate FXG when available. FC also uses optimization techniques when possible, such as package pixels in a SWF.

    Using the FXG rather than images can give you a much better crisp look to your applications, not to mention decrease the size of your SWFs (if image embedded) or increase performance since the FP10 drawing API is faster than loading external images during run-time.

    21


    Create interactivity scaffolding

    I used Catalyst many times to create simple wire framing for interactivity. For instance, let’s say you have three components that you want to create animation (states) on certain user gesture, you can create boxed using Rectangle and convert them into ‘Custom / Generic Component’ then add the different states and interactivity. Export the project as FXP and then use the files as the scaffolding in Flash Builder and replace the custom component with the real component.

    12


    Designers and developers can work in parallel

    The designer can create the pixel and adjust the pixel as you need to make changes. So while you will be the one converting the pixel into code, you can work with the designer to make changes as you need them. For instance, you may notice that adding a shade to a vector graphic in Illustrator will convert the graphic into an image in Catalyst that doesn’t happens all the time but in certain cases, so you can work with the designer and have these images adjusted so they can turn into FXG code. As you work with the designer you can show him/her how you are working with Catalyst so the designer can slowly take over some of Catalyst work when he/she is ready.

    I am sure that as Catalyst rolls out of Beta the round tripping and integration with FB4 will be easier, meanwhile have fun and enjoy the tool when you can.

    Cheers, Elad Elrom :)

    21
    Jul

    Getting Strarted with Open Source Media Framework (OSMF) formerly Strobe

    The Open Source Media Framework (OSMF) formerly known as Strobe goes open source and provides a a framework for media playback of any kind.

    To download the code visit:
    http://opensource.adobe.com/wiki/display/osmf/Open+Source+Media+Framework

    The video player ecosystem is complex. There are other elements that make a video player a complete composition, such as advertisement elements, social network elements, reporting, content management, DRM etc. Adobe recently announced a new framework called Adobe Open Source Media Framework that is aimed at solving these issues.

    OSMF is an open source (vanity license) AS3 media framework that supports the workflow around video playback and monetization. Video players have different features set. The skins are different and the integration is different as well as the architecture workflow. But they do essentially the same thing and can be created using the OSMF framework. The framework is based on the quality of the video player (OVP) and addresses the common challenges.
    Adobe vanity license means that it’s ok to use and redistribute. Modifications can be submitted to Adobe for redistribution.

    The foundation of the framework is Qos (quality of service), which focuses on OVP and provides a quick start for playing videos (smallest buffer size needed to start the video), efficient connection logic, and switching bitrates dynamically (recall metric monitor service in OVP).

    The framework by itself is not powerful without having the CDNs and the publishers onboard. Adobe OSMF is based on the “chicken and the egg” theory, meaning which will come first? In fact, Adobe is currently trying to get the publishers and the CDNs onboard and it seem that they got very positive responses. The idea is that each CDN will integrate their plugins to the OSMF framework and the publisher will be able to easily switch CDNs. This type of pluggable component can allow publisher to easily switch as well as test performance and service over different CDNs.
    Advantages:

    • Reduce the barrier of entry for new publishers – by offering a framework to integrate the different pieces of the video player, new publishers can get started quickly and with fewer resources and scale up as requirements increase.
    • Provide flexible framework – OSMF provides an easy way to extend each component and allow these components to act as a Lego piece that can be compositable and extensible.
    • Leverage existing code – The OSMF framework uses the Flash Player from Open Video Player (OVP).
    • Drive Standard and allow custom workflows – many of the elements that connect to a video player are not standard yet and Adobe OSMF will help standardize these components as well as allow them to be configured.
    • No runtimes or framework dependency – the framework is based on Flash 10 AS3 and is not depend on any framework such as Flex SDK, Cairngorm or others. With that said some integrated elements may be created using a framework but these are loosely coupled and can be replaced if needed.
    • Partners focus – There are two partners: publishers and CDNs. Each can focused on their expertise. CDN can focus on services and integration and publisher can focus on user experience.
    • Integration with existing Adobe tools – Adobe OSMF will be integrated with other Adobe Suite tools and services such as Catalyst, Illustrator, FMS 3.5 and FMRMS.
    • Optimize – by the ability to separate the core framework and each element as a separate SWC you can increase performance by keeping file size to the minimum and based on components used.

    The Adobe OSMF framework consists of three interests, which can be represented as an MVC pattern, as follows:

    • Model (Media elements) - media element (based on IMediaElement) may be video, image, SWF or others. Every media element has defined capabilities (traits based on IMediaTrait) and a life cycle, they are called Regions, for instance Region 1 (Image), Region 2 (Video) etc. Each media element implement IMediaElement contract, which contains the dynamic media traits (capabilities) and you can add or modify a trait. The traits are the building blocks and they can be set once and be reused.
    • Controller (Media compositions) – a media composition (IMediaFactory) is a collection of media elements. Each media can be rendered based on set of rules as well as media region that define how to use the capability and the life cycle.
    • View (Media configurations) – collection of media regions with properties that map to defined capabilities. Each media region can render each media element and can be spatially arranged and the entire media configuration can be the stage.

    Let’s take a closer look at the higher architecture. The UI management is the set of tools we use to create our video player and skin it such as Flash Professional, Flex and soon we will be able to use Flash Catalyst for Flash 10 applications. Advertising companies such as DoubleClick or eyewonder allow us to add rich advertisement elements such as pre, mid and post roll ads, in playlist, or as companion ads or overlay ads. Applications provided by sites such as KickApps and gigya allowing embedding of the video player in social networks such as Facebook. The Flash Platform enable playback of video files. Companies such as Level(3) and Akamai provide syndications of feeds that can be consumed by other APIs. Lastly, stream management is available through Adobe’s server.

    strobe1

    The framework architecture was designed to allow you to integrate at multiple points and support different use cases, it also allow to use only the pieces you need, as well as the ability to scale up when ready. There are three types of implementation you may find useful.

    • Media Framework (level 1 integration) - small in size and include the basic integration such as the video player by OVP and pluggable CDNs. It allows hooking into your higher-level API, tweak the UI and access the needed classes and methods you define such as play and pause.
    • Media Framework and Composition Framework (level 2 integration) - In addition to level 1 level 2 allows creating a composition framework, which includes the ability to integrate to plugins that can provide monetization workflow for ads and tracking.
    • Configuration Framework, Media Framework and Composition Framework (level 3 integration) – in addition to the features that level 2 and level 1 offers, the level 3 integration offers dynamic chrome and syndication.

    strobe2

    Let’s see the framework in action

    Let’s take a look at a simple implementation of the framework provided by Adobe based on the beta version. Keep in mind that the implementation was provided by Adobe and it’s under Adobe agreement; please refer to Adobe in regarding to the license agreement in term of usage.

    
    package com.adobe.strobe.player
    {
    	import com.adobe.strobe.loaders.*;
    	import com.adobe.strobe.media.*;
    	import com.adobe.strobe.media.events.*;
    	import com.adobe.strobe.traits.*;
    	import com.adobe.strobe.traits.events.*;
    	import com.adobe.strobe.view.*;
    
    	import flash.events.*;
    	import flash.net.*;
    
    	public class MainWindow extends MainWindowLayout
    	{
    

    The childrenCreated method will be called automatically by the component once the child objects are created, since it’s replacing the default UIComponent method.
    We create the MediaFactory, which holds all the media element using IMediaInfo. We than can call the registerDefaultMedia. And adds event listeners to the buttons. As well as bring back mediaPlayer object which is the component which is capable of playing any type of media by holding a media element that has all the traits we needs such as IPausible, which is a trait to pause and resume playing.

    
    override protected function childrenCreated():void
    		{
    			super.childrenCreated();
    
    			// Construct a loader factory:
     			factory = new MediaFactory();
    			registerDefaultMedia(factory);
    
    			// Add button handlers:
    			buttonStop.addEventListener(MouseEvent.CLICK,onStopClick);
    			buttonPlay.addEventListener(MouseEvent.CLICK,onPlayClick);
    			buttonPause.addEventListener(MouseEvent.CLICK,onPauseClick);
    			buttonResume.addEventListener(MouseEvent.CLICK,onResumeClick);
    			buttonToggleMute.addEventListener(MouseEvent.CLICK,onToggleMuteClick);
    			buttonLoad.addEventListener(MouseEvent.CLICK,onLoadClick);
    
    			// Get a reference to our player:
    			player = flexMediaPlayer.mediaPlayer;
    			player.scaleMode = ScaleMode.LETTERBOX;
    		}
    

    registerDefaultMedia method creates all the media element that our application will be supporting, such as progressive download, streaming, audio files, images and SWFs.

    
    private function registerDefaultMedia(factory:IMediaFactory):void
    		{
     			var loader:ILoader = new VideoLoader();
    			factory.addMediaInfo
    				( new MediaInfo
    					( "progressive"
    					, loader as IMediaResourceHandler
    					, VideoElement
    					, [VideoLoader]
    					)
    				);
    
     			loader = new RTMPLoader();
    			factory.addMediaInfo
    				( new MediaInfo
    					( "streaming"
    					, loader as IMediaResourceHandler
    					, VideoElement
    					, [RTMPLoader]
    					)
    				);
    
    			loader = new SoundLoader();
    			factory.addMediaInfo
    				( new MediaInfo
    					( "audio"
    					, loader as IMediaResourceHandler
    					, SoundElement
    					, [SoundLoader]
    					)
    				);
    
    			loader = new ImageLoader();
    			factory.addMediaInfo
    				( new MediaInfo
    					( "image"
    					, loader as IMediaResourceHandler
    					, ImageElement
    					, [new ImageLoader()]
    					)
    				);
    
    			loader = new SWFLoader();
    			factory.addMediaInfo
    				( new MediaInfo
    					( "swf"
    					, loader as IMediaResourceHandler
    					, ImageElement
    					, [SWFLoader]
    					)
    				);
    		}
    

    loadMediaByURL method creates new media element based on the URL the user provided add listener and set it in our player component.

    
    		private function loadMediaByURL(url:IURLResource):void
    		{
    			// Stop listening to the current media, if any.
    			toggleMediaListeners(player.media,false);
    
    			// Create the new media.
    			var media:IMediaElement = factory.createMediaElement(url);
    
    			// Listen for events related to the new media.
    			toggleMediaListeners(media,true);
    
    			// Set it on our media player.
    			player.media = media;
    		}
    

    toggleMediaListeners method adds the event listeners to the media. Once the state have changed and the on flag is set to true it will call the event handler onMediaStateChange. The media element has a trait for loading the asset and once the onLoadableStateChange is captured the text field will display the information.

    
    		private function toggleMediaListeners(media:IMediaElement,on:Boolean):void
    		{
    			if (media != null)
    			{
    				if (on)
    				{
    					media.addEventListener(MediaStateChangeEvent.STATE_CHANGE,onMediaStateChange);
    				}
    				else
    				{
    					media.removeEventListener(MediaStateChangeEvent.STATE_CHANGE,onMediaStateChange);
    				}
    
    				var loadable:ILoadable = media.getTrait(ILoadable) as ILoadable;
    				if (loadable)
    				{
    					if (on)
    					{
    						loadable.addEventListener(LoadableEvent.LOADABLE_STATE_CHANGE,onLoadableStateChange);
    					}
    					else
    					{
    						loadable.removeEventListener(LoadableEvent.LOADABLE_STATE_CHANGE,onLoadableStateChange);
    					}
    				}
    			}
    		}
    

    The event listener will handle the interaction with the media element based on the traits that got assign to the media.

    
    		private function onStopClick(event:MouseEvent):void
    		{
    			player.stop();
    		}
    
    		private function onPlayClick(event:MouseEvent):void
    		{
    			player.play();
    		}
    
    		private function onLoadClick(event:MouseEvent):void
    		{
    			var url:String = urlInput.text;
    			if (url.length > 0)
    			{
    				player.unload();
    				loadMediaByURL(new URLResource(url));
    			}
    		}
    
    		private function onLoadableStateChange(event:LoadableEvent):void
    		{
    			loadState.text = event.newState.toString();
    		}
    

    The onMediaStateChange event handler will handle the display and hiding of the toolbar.

    
    		private function onMediaStateChange(event:MediaStateChangeEvent):void
    		{
    			if (player.media)
    			{
    				buttonStop.visible 	= buttonPlay.visible 	= player.media.getTrait(IPlayable) != null;
    				buttonPause.visible = buttonResume.visible  = player.media.getTrait(IPlayable) != null;
    				buttonToggleMute.visible = player.media.getTrait(IAudible) != null;
    			}
    		}
    
    		private var factory:IMediaFactory;
    		private var media:IMediaElement;
    		private var player:MediaPlayer;
    

    Constants to hold the different media files we can test.

    
    		private static const REMOTE_PROGRESSIVE:String 	= "http://flipside.corp.adobe.com/pirates3/Pirates3-1.flv";
    		private static const REMOTE_MP3:String 			= "http://flipside.corp.adobe.com/brian/strobe/media/Remember.mp3";
    		private static const REMOTE_STREAM:String 		= "rtmp://flipside.corp.adobe.com/trailers/EvanAlmighty";
    		private static const REMOTE_IMAGE:String 		= "http://www.adobe.com/products/mediaplayer/assets/images/amp_icon.jpg";
    		private static const REMOTE_SWF:String 			= "http://flipside.corp.adobe.com/testing/swfPlayback/as3/Objects/Color/ObjClr_setRGB.swf"
    	}
    }
    

    strobe3