Flex project setup - Part III. The advantages of keeping Data Providers in their own source path

This article is part of group of posts on Flex project setup or five use cases for multiple source paths linking.


In this part, I discuss how the same ideas can be used to isolate data providers so that alternative providers can be added at any time to the project without requiring any rewrite of the main code.

You will immediately notice an important difference compared to the other projects. Rather than create another flex project with the data provider, I simply create a new folder in the main application project ( same level as src) and I define that folder as a secondary source path.


Project Properties > source-path > add > service-xmlWidged

The reason to adopt this slightly different setup is that (1) the Flex project must have at least one data provider at any time, (2) a separate project will be created to contain the code to test the communication with the provider.

Provider Swap

Providers can be swapped at any time. XML to amfphp, for instance. A more common scenario is slight change in API from dev to staging.

For the code to support data providers that are truly plug-and-play, some basic rules need to be followed.

  • One and only one class is allowed knowledge of the provider. This is typically a specialist controller. In my mini-example, this is the controller for the page on "Art of War".
  • The provider is given an associated data converter that knows how to convert data in the specific format returned by that provider to some internal VOs. In our mini-example, this is the ArtXmlConverter.as file.
  • Except for the VOs that the provider data need to be converted to, the provider has no knowledge of anything outside its package. The provider doesn't even know about the controller. It dispatch events to signal results or errors. The specialist controller listens to these events.
  • Any bit of data that arrives to the controller is in an internal format. The specialist controller receives the data as a VO that the application knows how to process. The controller knows nothing about the idiosyncrasies of the data formatting for that provider.
  • An interface is created that lists all service calls that must be implemented for the application to run properly.

Provider Test

When benchmarking the application, it is sometimes important to be able to clearly assign the blame on a slow webservice. The worst I ever had was webservices that toke 30 seconds to return their data. The back-end webservices had been provided by another company, independently contracted by the client. The provision of an interface where the client could test the speed of each service call really helped him realize how bad it was.

With the provider access functionalities are well isolated in a folder, it is easy enough to create a service test subproject that will link to that folder and nothing else.

In our demo, a subproject Service-Test was created. Best is to keep that project very simple. On the left, the list of the different services and the parameters that can be specified for each call. On the right, the results, along with the delay in milliseconds. A reference to the provider is created and the provider is used to manage the service calls proper. The raw results are shown in the result text area.

You are missing some Flash content that should appear here! Perhaps your browser cannot display it, or maybe it did not initialize correctly.


<?xml version="1.0" encoding="utf-8"?>
<mx:VBox
xmlns:mx="http://www.adobe.com/2006/mxml"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
preinitialize="onPreinitialize()"
width="400" height="300"
>
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.rpc.events.ResultEvent;

import com.widged.warStories.pages.artOfWar.RemoteConstants;
import com.widged.warStories.pages.artOfWar.events.ArtContentEvent;

private var _provider:ArtProvider;

private function onPreinitialize():void
{
_provider = new ArtProvider( RemoteConstants.xmlBaseUrl );
}

/* ############################
Services under test
############################### */
// content_xml
public static const SERVICE_CONTENT_XML:int = 1;
[Bindable] private var localeList:ArrayCollection = new ArrayCollection([{label: "en_US"},{label: "fr_FR"}]);
// ... add other services
private function runService(serviceId:int):void
{
_updateView("Waiting for results....", "0", true); // resets the view
_resetTimer();
switch (serviceId)
{
case SERVICE_CONTENT_XML:
var localeCode:String = localeCombo.selectedLabel;
_provider.addEventListener(ArtContentEvent.RESULT, onXmlResult);
_provider.getArtContent(localeCode);
break;
}
}

private function onXmlResult(event:ArtContentEvent):void
{
_provider.removeEventListener(ArtContentEvent.RESULT, onXmlResult);
_updateView(event.vo.toString(), delayInMilliseconds.toString(), true);
}

/* ############################
Results View
############################### */
private function _updateView(resultText:String, delayText:String, isFinal:Boolean = false):void
{
if(isFinal && _timer)
_timer.stop();

resultArea.text = resultText;
if(delayText)
millisecondEllapsedText.text = delayText;
}

/* ############################
Service Delay Timer
############################### */
private var _timer:Timer;
private var _timeStart:Date
private function _resetTimer():void
{
_timer = new Timer(1000);
_timeStart = new Date();
_timer.addEventListener("timer", onTimerTick);
_timer.start();
}

private function onTimerTick(event:TimerEvent):void
{
var millisecs:Number = _timer.currentCount * 1000;
millisecondEllapsedText.text = millisecs.toString();
}

private function get delayInMilliseconds():int
{
var timeEnd:Date = new Date();
return (timeEnd.valueOf() - _timeStart.valueOf());
}

]]>
</mx:Script>
<mx:Spacer height="9" />

<mx:HBox>
<mx:Label text="Delay in milliseconds:" />
<mx:Spacer width="20" />
<mx:TextInput
id="millisecondEllapsedText"
width="100%"
/>
</mx:HBox>

<mx:HBox width="100%" height="100%" paddingRight="10" paddingBottom="10">

<!-- list of services to test -->
<mx:VBox>
<mx:ComboBox id="localeCombo" dataProvider="{localeList}" />
<mx:Button label="Fetch content.xml" click="runService(1)" />
</mx:VBox>

<mx:TextArea id="resultArea" styleName="body" width="100%" height="100%" />
</mx:HBox>
</mx:VBox>

Downloads

(Require Flash Builder 4)

Powered by Drupal, an open source content management system