LiveCycle Process Management ES2 Task Access Control List

Recently, I had an interesting discussion with a customer representative regarding the nature of LiveCycle Process Management ES2 and the use of shared queues.  Specifically, the issue came down to a misunderstanding of how the Assign Task Operation’s Task Access Control List (ACL) parameters effected who could see items in a user’s shared queue.

With the help of Chantal Richard and Jasmin Charbonneau, I learned a lot more about what these parameters do.    I though it would be a good idea to share the information in case others are as confused as I was.

Say we have a task that is assigned to a user.  The particular user is decided at runtime based on some process data.  The Task Access Control List (ACL) provides restrictions on what that user can do with the task.  It does not say who the task can be shared/consult/forward (etc.) with.  The “Add ACL for shared queue”  only states whether the task can be viewed in a shared queue, not who gets access to that shared queue.   You can use the “Reassignment Restrictions” to specify which group(s) a user can share with/forward to /consult with.

For example, consider the following setup for a task assignment (see image):

clip_image001

A task is assigned to a user based on the XPath expression – let’s say, based on form and process information,  it gets assigned to Sarah Rose.  The system then checks to see if Sarah is in the Task Access Control List (ACL).  In this case she is, and her options say she can share the task.  The Task Access Control List (ACL) does not say with whom she can share.  The other users in this list (John Jacobs and Kara Bowman) are in the list in case they get assigned the task and the system needs to decide what they can do.  The Reassignment Restrictions section tells us that this task can only be shared with the “All principals in Default Domain” group.

Since the “Add ACL for shared queue” is checked, the task will show up in Sarah’s shared queue (if it was not on then it would not show up in her shared queue).  This means that if Sarah shares her queue with another user they will see that task in her queue.  An important note – the “Add ACL for shared queue” option is not affected by the “Reassignment Restrictions”.  In this example; if Sarah shares here queue with Bob Jones who is not in the “All principals in Default Domain” group, then when Bob looks in Sarah’s queue he will see the task.

If we now look at the entry for John Jacobs in the Task Access Control List (ACL) list, we will see that he is not allowed to Share or Consult the task.

image

This means that if the task was assigned to John, he would not be allowed to share the task or use Workspace to consult with others.  He has different permissions than Sarah.

Debugging in Mosaic

Adobe’s Rich Internet Application (RIA) framework product, LiveCycle Mosaic, allows developers to create applications that are made up of smaller micro-apps called Tiles. Tiles can be either Flex applications or HTML pages and there are very mature tools for development and testing of individual Tiles.  Since one of Mosaic’s biggest features is that Tiles can communicate with each other on the client side (not requiring round trips to the server) its very useful to test Tiles running in the framework.     In this post I’m going to describe a few tools and tips to make this a bit easier.

Mosaic Debug WAR

There is a special debug version of the Mosaic WAR file that is built specifically for developers.  When you install/unzip Mosaic you fill find the debug version in the deploy/war/{logversion}/debug folder (logversion refers to either the log4j-logging or jdk-logging folder).  This version of Mosaic is the same as the production version, except it includes hooks for QTP and the Mosaic plugin for Flash Builder (see below).   If you want to create a debug environment, simply deploy this debug version to your application server.

Debug WAR in Standalone

Mosaic comes with a handy-dandy standalone version for development.  It comes bundled with its own Tomcat so you can start building things without worrying about setting up a more complex production environment.  As of the time of this post, the standalone version comes with the production version of Mosaic.  The first thing I always do is to replace it with the debug version.  Here’s how:

  • Stop the standalone Tomcat if its running
  • Go to the {mosaic home}/standalone/webapps folder
  • Delete the existing mosaic.war (don’t panic, you still have a copy in the deploy folder)
  • Delete the mosaic folder and everything in it – This will delete any deployed Mosaic apps
  • Copy the debug version from {mosaic home}/deploy/war/log4j-logging/debug/mosaic.war into the {mosaic home}/standalone/webapps folder
  • Start the standalone Tomcat and redeploy any apps

Debug Tools

Flash Player Debugger

If you are doing any Flash or Flex development you owe it to your self to get the Flash Player debug version (find it here).  Get the ActiveX one if you are using IE and the Netscape one if you are using Firefox.  This will allow you to connect to the Flash Builder tool and see exactly what’s going on in your apps.

Mosaic Flash Builder Plugin

The Mosaic team has put together a really useful plugin for the Flex Builder 4 IDE.  The LiveCycle Mosaic ES2 Plugin for Flash Builder 4 (FB4) plugin helps with tile creation as well as debugging.  Used in combination with the debug version of the mosaic.war file and the FlashPlayer debug version, you can do all of the standard debugging things that you could with FB4, but inside of the Mosaic framework.  This includes breakpoints, expression watchers, step through, etc.

The tool will even build a temporary Catalog and Application file so you can test Tiles in the framework strait away.  Having said that, however, I usually like to test with my own layouts and catalog setups.  Fortunately I can do this by deploying my Catalog and Application descriptors as per normal (using an ANT script), then I change the Debug Configuration in FB4 to use my setup.  By setting the Host Application and Catalog settings to point to my descriptors I can test the tiles as they would appear to the end user.  For example, in the shot below I have an Application called SalesDashboard and a Catalog called SalesDashboardCatalog.  This setup will launch the debugger using those descriptors:

image

You have 45 seconds to comply

If you are using Firefox as your web browser then you may have noticed that the Flash Player will crash after a short idle time.

image

This is especially annoying when you are debugging because the thing times out and crashes when you are in the middle of looking at variable properties.  This is because of a “feature” in Firefox that crashes the Flash Player after 45 seconds of inactivity.  Fortunately its fairly easy to override this by disabling the hang protection:

  • In Firefox type the following into the address bar (aka the “Location bar”) and hit enter:  about:config
  • Click the “I’ll be careful…” button
  • In the resulting Filter box type: dom.ipc.plugins.timeoutSecs
  • In the resluting dialog box enter -1 and click OK

Now the Flash Player won’t time out while you are trying to debug your application.

Lip Service – Using Mosaic 9.5 Services

LiveCycle Mosaic 9.5 has the concept of a service component.  This is a data store that defines objects that are used by multiple tiles. This allows the exchange of custom data objects between tiles as well as pre-configuring data before a tile is loaded.

In this post, I walk through a simple service example to explain how to put one together.  The source code for this project is located here

Why Services Matter

Why would you go through the effort of putting together a service? What does it give you that you can’t do with “normal” tiles? One good reason is that you need to exchange custom objects between tiles.

If you have multiple tiles that use the same custom object. If you were to compile each of those tiles with the object inside it and then deploy it, you would run into a problem due to the way Mosaic isolates tiles.

For example:

Say you have a complex object called Dog. A dog has several fields: name, color, breed and a Boolean called hungry. Now say you have two tiles that make use of the dog object – AddDog and ShowDogs. The AddDog tile creates a message that sends out a Dog object. The ShowDogs tile watches for a message with the Dog message in it.

When you package your tiles, each one will contain a copy of the Dog object.

clip_image002[4]

Since the Mosaic framework doesn’t know what a Dog object is and there are really two different Dog objects (one for each tile) you are going to get errors.

You could send each individual as a message (or an array of messages), but that gets cumbersome quickly. Also, if your custom object contains complex objects it becomes a problem.

Now let’s look at doing the same thing using Mosaic Services. This time you create an interface and service for the Dog object. The two tiles don’t include the Dog themselves, but they do refer to it.

clip_image002[6]

Now the framework is aware of what a Dog object is. When the AddDog tile sends a message with a Dog in it, the ShowDogs tile will be able to receive the message.

Using Mosaic Services

To create and use Mosaic services you need to do the following:

  • Create an ActionScript interface library. This allows you to separate the definition of the custom object from its implementation.
  • Create the ActionScript service, an implementation of the interface class.
  • Use the services in your tiles. You will be accessing the custom objects using the interface.
  • Add the services, interfaces and tiles to the catalog
  • Add the service reference to the application
  • Deploy the catalog and application to your Mosaic server.

When the user logs in, Mosaic will use the designated service implementation for the custom object based on the references in the application and catalog information. For those of you that are familiar with the Java Spring framework this is similar to Spring’s dependency injection.

Now let’s look at how to build the above mentioned example using services.

The Interface Library

Using FlashBuilder create a new Flex Library Project to hold the interface file(s). Ensure that you are using Flex 3.4 or greater. The interface spells out the fields and functions that must be in the service implementation.

In this case I only have one interface, IDogData. It defines the name, color, breed and hungry fields as well as a toString function that is useful for debugging.

package com.adobe.etech

{

public interface IDogData

{

function get name():String;

function set name(nm:String):void;

function get color():String;

function set color(clr:String):void;

function get breed():String;

function set breed(brd:String):void;

function get hungry():Boolean;

function set hungry(hngy:Boolean):void;

function toString():String;

}

}

The interface library needs to be compiled into a SWF file so it can be deployed to Mosaic. The easiest way to do this is to modify the sample ant scripts that are shipped as part of the stockDataService that comes with Mosaic.

The Service Implementation Library

Now that we have defined what the custom object will do, we need to actually do it. Create another Flex Library Project to hold the service classes. You will need to add a reference to the interface library you just created as well as a reference to the Mosaic tile API swc.

Next create ActionScript implementation classes that implement your interface library classes. The service class will need to have corresponding functions for each of the ones mentioned in the interface. This is where you put the code that carries out the operations that you named in the interface.

In this example, I have only one service class (DogData) that implements the IDogData interface. The service class fleshes out each of the functions from the interface. I have also added a constructor function that allows me to initialize the service at start up. I’ll talk more on that when I discuss the catalog descriptor.

package com.adobe.etech.impl

{

import com.adobe.etech.IDogData;

public class DogData implements IDogData{

private var _name:String;

private var _color:String;

private var _breed:String;

private var _hungry:Boolean;

public function DogData(nm:String=“not set”, clr:String=“not set”, brd:String=“not set”, hngry:Boolean=false){

_name = nm;

_color = clr;

_breed = brd;

_hungry = hngry;

trace (“*********** Dog: “ + this.toString());

}

public function toString():String{

return (_name + “, “ + _color + “, “ + _breed + “, “ + _hungry.toString());

}

public function get hungry():Boolean{

return _hungry;

}

public function set hungry(value:Boolean):void{

_hungry = value;

}

public function get breed():String{

return _breed;

}

public function set breed(value:String):void{

_breed = value;

}

public function get color():String{

return _color;

}

public function set color(value:String):void{

_color = value;

}

public function get name():String{

return _name;

}

public function set name(value:String):void{

_name = value;

}

}

}

Much like the interface library, the service library must be compiled into a SWF. Again, the stockDataService includes an ANT script that serves as a good starting point for how to do this.

Services in Tiles

Now that you have an interface library and a service implementation of that interface; its time to look at how the service is used in your tiles.

Start by creating a new Flex project to hold your tile source code. This project will be like any other Mosaic tile project except that you will include a reference to the service implementation library that you created earlier. I won’t get into creating general Mosaic tiles here, as you can find that information in other Adobe documents.

In your tile code, you will refer to the interfaces and not the service implementations. This is because we want Mosaic to select the correct services to ensure that we are using ones common to all tiles. We don’t want a copy of the service embedded in our tile code.

For example: I have a very simple tile called AddDogs. I want it to share the dog object, specified in the IDogData interface, that is loaded into the application. This will allow me to send IDogData objects as part of Mosaic messages. My Tile code looks like this:

<?xml version=”1.0″ encoding=”utf-8″?>

<mc:Tile xmlns:fx=”http://ns.adobe.com/mxml/2009

xmlns:s=”library://ns.adobe.com/flex/spark

xmlns:mx=”library://ns.adobe.com/flex/mx

xmlns:mc=”com.adobe.mosaic.core.*

layout=”absolute” width=”100%” height=”100%

>

<fx:Script>

<![CDATA[

import com.adobe.etech.IDogData;

import mx.controls.Alert;

[Bindable] public var dogData:IDogData;

protected function addBtn_clickHandler(event:MouseEvent):void{

dogData.name = dname.text;

dogData.color = dcolor.text;

dogData.breed = dbreed.text;

dogData.hungry = hungry.selected;

this.parentView.context.setAttribute(“changedDog”,dogData);

}

]]>

</fx:Script>

<fx:Declarations>

<s:RadioButtonGroup id=”hungryGroup/>

</fx:Declarations>

<mx:Form>

<mx:FormHeading label=”Add a new dog/>

<mx:FormItem label=”Dog’s Name>

<mx:TextInput id=”dname/>

</mx:FormItem>

<mx:FormItem label=”Color>

<mx:TextInput id=”dcolor/>

</mx:FormItem>

<mx:FormItem label=”Breed>

<mx:TextInput id=”dbreed/>

</mx:FormItem>

<mx:FormItem label=”Hungry” id=”dhungry>

<s:RadioButton x=”76” y=”20” id=”hungry” label=”Yes” value=”true” groupName=”hungryGroup/>

<s:RadioButton x=”76” y=”46” id=”nothungry” label=”No” value=”false” groupName=”hungryGroup/>

</mx:FormItem>

<mx:FormItem>

<mx:Button id=”addBtn

label=”Add

click=”addBtn_clickHandler(event)”/>

</mx:FormItem>

</mx:Form>

</mc:Tile>

I have another tile that also uses the IDogData object – ShowDogs. In this case the tile is listening for a message with the dog object and will show it in fields:

<?xml version=”1.0″ encoding=”utf-8″?>

<mc:Tile xmlns:fx=”http://ns.adobe.com/mxml/2009

xmlns:s=”library://ns.adobe.com/flex/spark

xmlns:mx=”library://ns.adobe.com/flex/mx

xmlns:mc=”com.adobe.mosaic.core.*

layout=”absolute” width=”100%” height=”100%

creationComplete=”init()”>

<fx:Declarations>

<!– Place non-visual elements (e.g., services, value objects) here –>

</fx:Declarations>

<fx:Script>

<![CDATA[

import com.adobe.etech.IDogData;

import mx.events.FlexEvent;

import mx.events.PropertyChangeEvent;

import mx.controls.Alert;

[Bindable] public var dogData:IDogData;

private function init():void{

getDogData();

this.parentView.context.addAttributeWatcher(“changedDog”,onDogChange);

}

private function onDogChange(e:PropertyChangeEvent):void{

getDogData();

}

private function getDogData():void{

dogname.text = dogData.name;

dogcolor.text = dogData.color;

dogbreed.text = dogData.breed;

doghungry.text = dogData.hungry.toString();

//Alert.show(dogData.toString(),”Change in Dog”);

}

]]>

</fx:Script>

<s:Label x=”51” y=”48” text=”Name/>

<s:Label x=”51” y=”68” text=”Color/>

<s:Label x=”50” y=”88” text=”Breed/>

<s:Label x=”50” y=”108” text=”Hungry/>

<s:Label x=”115” y=”49” id=”dogname/>

<s:Label x=”115” y=”69” id=”dogcolor/>

<s:Label x=”114” y=”89” id=”dogbreed/>

<s:Label x=”114” y=”109” id=”doghungry/>

</mc:Tile>

Adding Services to the Catalog

I mentioned earlier that the two tiles use the object interface and not any particular implementation of that interface. This is because we only want one implementation loaded, and that should not be in any particular tile. When the tile code runs, however; it will need an instantiated version of the object on which to work.

The Mosaic catalog is used to resolve this issue. In the catalog we will add the service implementation. In the tile descriptions we will create a reference to that service implementation. Mosaic will resolve the references so the running code has access to the instantiated object.

On the file system, in the catalogs folder, we need to add an Interfaces directory and Service directory. Into these we put the compiled interface and service SWF files (or you can have the ANT script do that for you).

image

The Interface Library Entry

In the catalog’s descriptor.xml file, there needs to be an entry for the interface library. The library has a name that will serve as a reference to other entries.

My advice is that you name the library the same as the interface library’s Flex project and SWF file name. There will be a lot of references in the catalog after we’re done and keeping the names strait will be messy enough.

For example, my IDogData interface is in a Flex project called MyDataInterfaceLibrary, so the interface library entry in the catalog looks like this:

<tile:InterfaceLibraryList>

<tile:InterfaceLibrary name=”MyDataInterfaceLibrary“>

<crx:Metadata>

<crx:Description>Dog Data Interfaces</crx:Description>

</crx:Metadata>

<tile:InterfaceList>

<tile:Interface interfaceName=”com.adobe.etech.IDogData“/>

</tile:InterfaceList>

</tile:InterfaceLibrary>

</tile:InterfaceLibraryList>

The Service Library Entry

The service implementation class needs an entry in the catalog as well. The service entry refers to the Interface Library Entry.

It also allows us to set some data when the service instance is first created. When I created the DogData class, I added a constructor that took four parameters. In the Service Library entry in the catalog, I use the ConstructorArgs tag to load some initial data into the instantiated object. That way I’ll have some dog data when the tiles first go to use the object.

<tile:ServiceLibrary name=”MyDataService“>

<crx:Metadata>

<crx:Description>Dog Data service</crx:Description>

</crx:Metadata>

<tile:ServiceClassList>

<tile:ServiceClass className=”com.adobe.etech.impl.DogData name=”SampleDogData scope=”singleton“>

<tile:Implements>

<tile:Interface library=”MyDataInterfaceLibrary interfaceName=”com.adobe.etech.IDogData“/>

</tile:Implements>

<tile:ConstructorArgs>

<tile:Argument value=”Rover“/>

<tile:Argument value=”Brown“/>

<tile:Argument value=”Doberman“/>

<tile:Argument value=”true“/>

</tile:ConstructorArgs>

</tile:ServiceClass>

</tile:ServiceClassList>

One other important thing to note is the scope attribute. By setting its value to singleton, I’m telling Mosaic to create only one instance of that object. All tiles will share that one instance. If anything changes it, it will be changed for ALL tiles.

The Tile Entries

The tile class entries need to be modified to tell Mosaic how to map the interface library types to variables in the tile. This is done by adding a Depends and Property element to the TileClass element.

<tile:TileClass name=”AddDogs label=”AddDogs catalog=”SimpleServiceCatalog initialWidth=”400 initialHeight=”400“>

<crx:Metadata>

<crx:Description><![CDATA[This tile has a description. It can me plain text or contain HTML.]]></crx:Description>

<crx:Category>Dogs</crx:Category>

</crx:Metadata>

<tile:Depends>

<tile:Interface library=”MyDataInterfaceLibrary interfaceName=”com.adobe.etech.IDogData“/>

</tile:Depends>

<tile:Properties>

<tile:Property type=”com.adobe.etech.IDogData name=”dogData“/>

</tile:Properties>

<tile:Content uri=”/mosaic/catalogs/SimpleServiceCatalog/tiles/AddDogs/content contentType=”application/x-shockwave-flash loadAs=”default“/>

</tile:TileClass>

The Depends clause tells us that this tile uses interfaces from the specified interface library. The Property element tells us that the interface maps to a specific variable in the tile. If you look at the tile’s MXML code you will see a variable that corresponds to the name attribute. In this case there is the line:
[Bindable] public var dogData:IDogData;

So this maps the interface library to the content of the tile, note that it does not mention the service library. This is done in the application’s descriptor.

Putting it all Together with the Application Descriptor

The final step is to use the application descriptor to identify the exact service that will carry out the work of the interface. A properties element will be added to each tile reference that uses the service. This will map the service to the property. When combined with the catalog entry for the tile, this will inject the service into the tile’s variable.

For example:

<tile:TileReference name=”AddDogs label=”Add Dogs catalog=”SimpleServiceCatalog“>

<tile:Properties>

<tile:Property name=”dogData“>

<tile:ServiceReference catalog=”SimpleServiceCatalog library=”MyDataService name=”SampleDogData“/>

</tile:Property>

</tile:Properties>

</tile:TileReference>

This entry tells Mosaic to map the SampleDogData service to the dogData property in the AddDogs tile. If you look at the catalog entry for that tile you can see that the dogData parameter is an IDogData object. You can also see that the SampleDogData service is a DogData class with some pre-named values supplied to the constructor.

What’s it look like

Assuming all of the code, packaging and mapping was done right; the application should be deployable now. Once that’s done you should be able to run it by going to the application’s URL. In the example the application is called SimpleService. It should look like the following:

image

You can see that there are two tiles: the AddDog on the left and the ShowDogs on the right. One thing to note is that the ShowDogs tile has data to start. This is because the initial data was listed in the catalog along with the service entry.

If I make changes to the dog’s data using the AddDog tile and hit the Add button then the data in the ShowDogs tile will change as well.

image

This is because the Add button click handler sets a Mosaic attribute that includes the IDogData object. The ShowDogs tile on the other hand is watching for a message that includes an IDogData object. If I loaded a new tile that used the same service I would see the new data, this is because service was marked as a singleton.

Drag and Droop

The database has been created and an interface class (DAO) has been created.  Now I need to get some data into the system. To do that I need some kind of data input screen that will take in text and multimedia files (images, videos, etc.)  In the interests of making things easy on the user I didn’t want to use an open file dialog.  I find that they are unpleasant to use, especially for non-technical users.  Drag and drop is much more intuitive.

According to the hype, it’s very easy to drag and drop files from a user’s desktop into an AIR application. I figured a five minute Google would give me a dozen or so examples of how to do it.  Well, yes and no.  There were many examples (875,000 returns on AIR drag and drop), but most were written for the Beta versions of AIR.  It seemed like every sample I found used the DragManager object which (I found out later) was transformed into the NativeDragManager object when AIR was released.  Other samples were so complicated that peeling back the layers to a simple sample took more time that it was worth.

What I need to do is really quite simple.  Have an image field onto which a user can drag and drop an image file.  Once the file was dropped onto the field I want the image to change to the new one and the file contents to be available in binary form.  The last requirement is so the file can be uploaded to the database.

Step 1 – Create a target for the operation – an image field:

<mx:Image x="10" y="10" id="displayImage" source="DropHere.jpg"/>

Step 2 – Add a listener for both the drag and the drop operations. This was done by adding a simple function that is called on the createComplete event of the canvas:

public function initDrop():void{
//Listeners to manage the drop of files on the first panel
 displayImage.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER,acceptDrag);
 displayImage.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP,dragDrop);
}

Step 3 – Add some code to allow the drag operation. Nothing too complicated here; its just a function that allows the drag operation into the image field:

//Accept drag and drop on the first panel
public function acceptDrag(event:NativeDragEvent):void{
        NativeDragManager.acceptDragDrop(displayImage);
}

Step 4 – Add some code to handle the drop operation.  Here I want to change the image to the new file and to put the contents of the file into a byte array.  To prove to myself that it was working, I echoed the file size to the screen.  This will be replaced later with a call to the DAO for a database update:

import flash.filesystem.File;
private var imageBytes:ByteArray = new ByteArray();  //the byte array can not be null!!!

…..

//On drop collect the files
public function dragDrop(event:NativeDragEvent):void{
        var imageList:Array = event.clipboard.getData(ClipboardFormats.FILE_LIST_FORMAT) as Array;
        var droppedFile:File = imageList[0];

         //load the file into the image
        displayImage.source= droppedFile.url;                             

        //get the byte array for the image
        var fileStream:FileStream = new FileStream();
        fileStream.open(droppedFile, FileMode.READ);                         

        fileStream.readBytes(imageBytes);
        fileSize.text = imageBytes.length.toString();             

   }

There are a couple of things to note about the dragDrop function.  First the result of the drag and drop is always an array of the files.  This is because the system allows you to drag more than one file at a time (very useful in some circumstances).  I took the cheap and cheesy route of just using the first element in the array.

Second I’m not using a Loader object to change the image contents as is suggested on many web pages.  I tried this at first and the image was not replaced, but instead the second image was overlaid on top.  I fiddled around a bit with the image layers but I couldn’t quite get it to work properly.  Simply re-setting the image source was a simple solution to the problem.

There you go.  Not much to it, but it works well and functions properly with the 1.0 release of AIR.

I split this bit of the application into a separate app so you can download it without all of the rest of the overhead.  You can find it here.

Feed the Beast

Working with the local database can be a bit of a challenge.  If the db is not well organized and if your application structures are not well thought out then you can end up with a real mess.  As I said before, sitting down and thinking the data layout all the way through and performing a few simple normalization exercises goes a long way to making the db construction a lot easier.

Knowing how AIR interacts with the local SQLite database helps as well. Essentially there are three parts to an AIR database setup:

  • The SQLite database
  • A class to interact with the database
  • Classes to hold the results of a database query

The SQLite database

AIR uses SQLite for a local/offline database.  SQLite is a very easy db to work with, but its pretty Mickey Mouse in a lot of respects.  For example there is no true auto-increment feature so the developer has to handle record id generation him/herself.  Also there are no foreign keys which means that most relationships between tables must be handled by the calling application.  How can you call yourself a relational database when the major function for controlling relationships does not exist?!

One tool that anyone working with SQLite databases must have is a database management tool. Sometimes you just need to have a look at what is going on inside the database itself.   There are a few out there for SQLite, but one of the most useful is a simple plugin for Firefox.  SQLite Manager has many useful features including a simple SQL execution tool, that will make your SQLite development much easier.  Go get it!

The nice thing about using the built in SQLite database is that you don’t need to mess around with a lot of connection properties.  You just point to a .db file (or create one) and away you go. You can concentrate on working with your data instead of trying to find the database.

Database Interaction Class

To make life easier I re-used a standard J2EE pattern for database connectivity – the Data Access Object (DAO).  A DAO is nothing more complicated than a class that acts as an interface between the AIR app and the database.  This way all db interactions (connection, queries, inserts, etc.) can be centralized and the rest of the AIR application doesn’t need to deal with the db trivialities.  The db structure can also be changed without having to rewrite the entire application.

The first thing that I needed to do was to connect to the database – actually before that I needed to check if the database exists and if not then create it.   I did this in the constructor for the DAO class as it only needs to be done once.

//private vars
private var _dbExists:Boolean;
private var _dbConnection:SQLConnection;
private var _createFlyTypeTableStmt:SQLStatement = new SQLStatement();

……..

public function LocalDAO(){
   _dbConnection = new SQLConnection();

  
// Add event listeners to react when database connection is opened.

  
_dbConnection.addEventListener(SQLEvent.OPEN, openSuccessHandler);
  
//_dbConnection.addEventListener(SQLErrorEvent.ERROR, openErrorHandler);

  // The database file is to be stored in the user’s application (UserData) folder.
  
var dbFile:File = File.applicationStorageDirectory.resolvePath("FlyData.db");
  
trace("Database file location is: " + dbFile.nativePath);

  
//Check if the database has already been created.

  
_dbExists = dbFile.exists;

  //Open the connection.
  
_dbConnection.open(dbFile);
}

The nice thing is that if the FlyData.db database file does not exist then one will be created for me. 

The openSuccessHandler listener will wait until the db is opened (or created and opened).  If it is newly created then I want to create the tables in the database as well.  The listener will then call a method called initDatabase that will fire a bunch of CREATE SQL statements. Most interactions with the database happen via a SQLStatement, through which you pass standard SQL queries as strings.  The SQLStatement is pointed to the correct database using a SQLConnection object.  

/**
* Success handler for _dbConnection
**/

private function openSuccessHandler(event:SQLEvent):void
{
    //We have an open handle to the database file.
    //Next, we need to create the tables if this is a new database

    if(_dbExists){
          trace("Application Database already exists.");
    }else{
         trace("New Application Database, must generate tables");
         initDatabase();
    }
}

private function initDatabase():void{
    _createFlyTypeTableStmt.sqlConnection = _dbConnection;

   
//Create the database tables

   
var sql:String = "CREATE TABLE `Material` (`MaterialID` INTEGER UNSIGNED NOT NULL, `MaterialName` VARCHAR(45) NOT NULL,`Color` VARCHAR(45) NOT NULL, `Size` VARCHAR(45) NOT NULL, PRIMARY KEY (`MaterialID`))";
   
_createFlyTypeTableStmt.text = sql;
   
_createFlyTypeTableStmt.execute();

//more create statements follow …..

Great, now the database exists, has been connected and I know for sure that I have a table structure.  Next I need to be able to select and insert records.

Query Results Classes

You need to have a place to put the results of a search.  In Java you have a result set that you can parse for specific db columns.  In AIR the execution of an SQLStatement returns an array of objects.  The objects must match the structure of the query exactly (and I do mean exactly).  Therefore, for all practical purposes, you need to have a class for each database search – one for each table (or one that matches multiple tables in the case of a join).

The next thing to do then is to create classes for each of the possible searches.  In my case this means that each table will need a corresponding class.  For example I have a table called Material that has a MaterialID, MaterialName, Color and Size.  I therefore need a class that has all of those elements:

package components.dataStructures
{
   public class Material    {
      public var MaterialID:int;
      public var MaterialName:String;
      public var Color:String;
      public var Size:String;

   public function Material(){
   }

  }
}

When I perform a query against the Material database the result will be mapped into an array of the class Material:

public function getMaterials():void{

    _dbMaterialStatement.sqlConnection = _dbConnection;
    var sqlQuery:String = "SELECT * FROM Material";
    _dbMaterialStatement.itemClass = Material;
    _dbMaterialStatement.text = sqlQuery;
   //SQL calls are asynchronous so a listener for the results is required
    _dbMaterialStatement.removeEventListener(SQLEvent.RESULT,onDBMaterialSelectResult);
    _dbMaterialStatement.addEventListener(SQLEvent.RESULT, onDBMaterialSelectResult);
    _dbMaterialStatement.execute();

}

In AIR, database calls are asynchronous.  This means most calls to the db will involve the setup of a listener.  This can be a bit of a pain because its difficult to just setup a method to make a call and return a value, you have to setup a listener to wait for the call to be executed.  This leads to many, many listeners floating around and , take my advice, you need to make sure you remove the listeners when they are not needed.

private function onDBMaterialSelectResult(event:SQLEvent):void{
   var result:SQLResult = _dbMaterialStatement.getResult();
   if (result != null){
       MaterialData = result.data;
   }
}

Guess What Class I Am

As I said earlier, because SQLite does not implement a proper auto-increment feature, you must do it yourself.  Shouldn’t be too hard – get the MAX of a column, add one and Bob’s your Uncle.   Something like:
"SELECT MAX(MaterialID) FROM Material"
should work fine. That works, but there is an important little trick.  AIR returns the result of all db queries as an array of objects, you must specify what those objects are, usually by setting the SQLStatement’s itemClass property.  Unfortunately this doesn’t seem to work too well for the MAX function.  I thought that MAX should return an integer, or at best a long – but it doesn’t. Setting the code to:
_dbMaxStatement.itemClass = int;
didn’t work.  With much use of the Flex Builder’s debugging tool, I determined that the MAX statement was returning an "id" class.  Okay, what the heck is an id class?  I can’t just set
_dbMaxStatement.itemClass = id;
because I don’t have the class "id" anywhere in my code – and I couldn’t figure out its package designation. I tried recasting the result to an integer and it didn’t work either. This all got very frustrating until a colleague pointed out that you can set the returned class type in the SQLStatement text itself. This lead to the following code:

_dbMaxStatement.sqlConnection = _dbConnection;
var sqlQuery:String = "SELECT MAX(MaterialID) AS id FROM Material";
_dbMaxStatement.text = sqlQuery;
_dbMaxStatement.execute();

var result:SQLResult = _dbMaxStatement.getResult();
var myint:int = result.data[0].id;
MaterialMax = myint;
trace ("***** MaterialMax: " + MaterialMax);

That little "AS id" makes all the difference in the world.

Prepared Statement

The one other thing that I wanted to mention is the use of variables inside a SQLStatement.  Since the statement is essentially a string, you could write it like:
var sqlQuery:String = "INSERT into Material (MaterialID, MaterialName, Color, Size) VALUES(‘" + material.MaterialID + "‘,’" + material.MaterialName + "‘,’" + material.Color + "‘,’" + material.Size + "‘)"; The problem is that keeping track of the quotes and commas quickly becomes a nightmare.  Fortunately there is an easier way.

In Java there is the concept of a "prepared statement" in which a question mark is substituted for a variable entry in an SQL statement.  ActionScript has the same concept although it uses an @ symbol and a variable name instead of a question mark.  Using this method the same SQL command can be written as:

var sqlQuery:String = "INSERT into Material (MaterialID, MaterialName, Color, Size) VALUES(@MatID, @MatName, @MatColor, @MatSize)";
_dbMaterialStatement.text = sqlQuery;
_dbMaterialStatement.parameters["@MatID"] = material.MaterialID;
_dbMaterialStatement.parameters["@MatName"] = material.MaterialName;
_dbMaterialStatement.parameters["@MatColor"] = material.Color;
_dbMaterialStatement.parameters["@MatSize"] = material.Size;

This makes the code much easier to read and maintain.

Conclusion

Using the above methods I created the other tables, classes for holding results and DAO methods for querying and updating the database.  The next step will be to wire the database stuff back into the display objects that I created earlier.  As you may recall I had setup some temporary data sources that were hard coded.  I now want to use the real objects as returned from the database.


My god…its full of stars

Its time to start making things look nice.

Last time I started with the central user focused object – the pattern – and started creating MXML components that corresponded to the object’s parts.  This, along with some temporary data structures gave me a rough working prototype of the pattern object.  It did look quite ugly however.

This time I spent moving objects around, changing layout and playing a bit with color schemes.  Since most of the pattern is data driven, allowances need to be made for different lengths of data.  This means that objects holding text and images needs to expand to fit the required screen area.  Other objects need to flow properly so text doesn’t end up running on top of other paragraphs

In AIR this means that things need to be placed in the appropriate UI containers. I had already used the Canvas container to hold sub parts of the pattern (such as the Instructions and the Recipe), but there are others that came into use such as the HBox, VBox and Tiles.  The Canvas is great, you can place any object where ever you want and it doesn’t care.  Unfortunately it doesn’t care about flow or object collisions either.  With dynamic data the Canvas can look like a real mess.  The HBox and VBox on the other hand, will only place one object in one place at a time.  Depending on which box you choose it will place objects side by side or stack them.  Either way it does take care of flow.  Tiles are another really useful container – depending on the direction, it will place things side by side (as with an HBox) until it reaches its maximum width, then it starts a new row.  By nesting the containers you can pretty much get the screen to flow any way you want.

To decide what objects went in what container, I started by drawing all the objects on a piece of paper.  I then drew an arrow next to each object to indicate the direction I wanted it to flow.

I then drew boxes around objects on the same plane.  By looking at the flow lines I can determine what container was required.  For example the entire Pattern screen needs to flow down – I want it to get longer if there is a lot of information (not wider).  This means that the Pattern Screen can be wrapped in a VBox. One of the consequences of this is that all of the objects would be stacked on top of one another as that is all that a VBox allows.  When I look at the Name, Tier and Species fields I want them to be on the same line. This means that they must be grouped in an HBox.  By continuing this process I eventually had a layout that would flow correctly as different lengths of data were added. Another thing I found was that by omitting the height and width attributes of certain HBox and VBox elements the boxes will inherit the height and width of the elements inside them (they would flow correctly).  I.e. the VBox that contains the picture and recipe will expand to fit all of the Recipe because it does not have a set height.

Another concern that I had was making the right part of the screen scroll.  Even though I have a few sections that I did want to scroll – such as the Instructions.  I had to tinker with things a log, but I eventually got just the Instructions section to scroll and not the entire page.  This was a bit of a PITA and took a lot of fiddling with the width and height values of the various containers.  At some points I had scroll bars on top of other scroll bars.  If anyone knows of an easier way to this (besides trial and error), please let me know.

Once I had things laid out nicely and flowing correctly, I started looking at themes and colors.  Eventually I will use a style sheet, but I’m still playing with things too much for that right now.  As far as colors go, I’m no Graphic Designer.  I can tell when something looks wrong, but what it takes to make it right is beyond my current skill level.  Fortunately there are some really nice tools out there to help. On of my new favorites is Kuler (yes I am Adobe biased).  There are tons of user defined color schemes available, some better than others.  My favorite feature is the Create From an Image feature.  I uploaded a picture (in this case a picture that included my parka and waders) that had the right "feeling" and it picked out the color theme from that picture – way cool.  With some minor adjustments I can use those five colors for all my objects and still have something that looks nice.

One other thing that I did was to add a new component for the Species field.  I decided that icons looked better than text and it was just a matter of doing a similar repeater setup that I used in the Recipe and Instructions components.

One complaint – As I have stated before, I am a coder by training.  I love the rich IDE environments that are provided in tools such as Eclipse.  Since Flex Builder is based on Eclipse, I believe that it should have the same rich development environment.  Missing features such as source formatting (pretty printing) and variable highlighting annoy me to no end. If someone on the Flex Builder team is listening – please add these feature in as they are in the Java developing mode.

Staring at a blank page

I launched Eclipse, created a new Flex project and got a shiny new application started. I then found myself staring at the clean grey page with no idea of where to start – call it programmer’s block. Okay, time to regroup.  In the past, with larger code based development projects (Java, .Net, etc.) I would have fallen back to the design document and started with either the main data structure/function, or some of the well defined supporting classes.  Really building with AIR is no different.  The only change is that instead of a data structure, the project is user/task focused. 

If I look back at the application structural diagram it gives me a place to start:

Structure1.jpg

The central object that the user interacts with is the Fly Pattern – seems as good a place to start as any.

If I look at a typical fly pattern it consists of some simple elements (the image, fly name, tier name, notes) and some more complex elements (recipe, instructions) that can repeat. To me this means that the pattern class has a couple of support classes that contain repeating elements.  Now we’re getting somewhere. 

I created a new MXML Component to hold the pattern and called it patternDetails.  I dropped on some objects to hold the simple elements – an mx:Image for the main picture, a mx:Text for the fly name and another mx:Text for the tier’s name.  I didn’t mess around with layout and design too much – I wanted to get all of the elements together before I worried about aesthetics. Not that aesthetics aren’t important, they are one of the keys to usability, but there is no use buying wallpaper until you know how big the room is.

Next I created two other MXML Components – one for the recipe data and one for the instructions.  Both of these have elements that repeat and are quite similar in function, although they are very different in presentation.  First the recipe: this consists of a list of elements consisting of parts, materials and notes.  The number of elements in the list is not fixed as one pattern may have three items and another may have forty.  I needed a MXML object that was capable of holding such a list.

In previous projects I have used the DataGrid in its various forms. Its a useful item that can be bound directly to a data source and has a lot of built in functionality (such as sort). Its really useful for lists of data items. I tried it, but it just didn’t look right, it was a bit too rigid in its layout.  I started to look for an alternative when I came across the mx:Repeater. Repeater also can be bound to a data source, but you specify all of the item layout yourself.  It doesn’t have the built in functionality, but it does have a lot of freedom in its layout – perfect.

Since I have to consider layout soon, I would like to see how the screens look with “real” data and image.  I picked a fly pattern called a Stimulator as a typical example, but how to get the data into the application?  Eventually this will be done by reading the information from a database, but this requires developing the database structure, supporting data structure and supporting classes.  It will have to be done eventually, but for right now I just want to see something quickly. I could go the other route and just hard code everything from images to text, but that doesn’t verify the functionality of the application – it means that I would need to rebuild everything when I built the functional code.

What I needed was a happy medium – something that I could put together quickly with out a lot of support classes, but still could be easily transitioned into functional code. Objects such as Repeater can use a dataProvider attribute to specify their data source. The dataProvider can be an array of Objects and can come from any data source.  In the long run the source will be the database, but right now I can create a local array and use it to test the Repeater. So I created a temporary folder for the images and set up an ArrayCollection to hold the data locally. For example, the following is the temporary array setup for the recipe component:

One other thing to note about the Repeater.  A consequence of complete freedom of look and feel is that you no longer have some of the basic functionality that you would expect.  For example when I first created the Repeater, connected the dataProvider and populated it with some text elements it repeated everything on the same line.  It did not lay new elements after the previous one.  What I wanted was something akin to the repeating subform functionality provided Acrobat.  I eventually found that wrapping the entire Repeater in a VBox did the layout as the VBox ensures that elements get placed one after the other.

So, now I have a rough fly pattern view with real (sort of) data.  I can now start making it look nice.  Here’s what it looked like before the beautification effort:

For those interested in seeing the early source code I’ve posted it in zip format here.

 

 


Thats no moon….

As you may recall, one of the main purposes of this project is to teach a traditional Java programmer (namely me) how to develop Rich Internet Applications (RIA).  This goes beyond just learning syntax and tooling, it means gaining a deeper understanding of the possibilities of a new environment.  To that end I have been doing quite a bit of reading about designing applications for RIA environments.  The more research that I do, the more I am convinced that this project requires a fundamental shift in thinking from a code based development mind set to a more visual based/user centric model. Developers/programmers (and I include myself in this category) can easily become too enamored with our own data structures and can loose track of the user that our applications should be designed for.  This is nothing new, but it becomes glaringly obvious as more tools become accessible to a wider range of development talent.  I guess what I’m saying is that crappy user design becomes less acceptable as tools make it easier for better design. Certainly, tools such as Flex Builder make it easier for a more diverse population to build better applications.  In no way does this excuse poor system design , it just means that good system design should support a good user experience.

Rob Adams, a Senior User Research Specialist with Adobe, has written a series of Developer Center articles that I really encourage anyone developing AIR/Flex applications to read. Rob’s articles will help get you into the RIA head space and help you to start designing better RIA applications.

I finally got around to putting together the system design document for the
Fly Tying Inventory tool that I described in earlier posts. I’ve tried to keep Rob’s suggestions (and others that I have read) in mind while working on the design for this project.  Specifically, I started with the consideration of the user’s task (finding a fly pattern and associated media) and worked the design from that.  Everything needs to be considered from the point of view of the user’s purpose. See the attached BC_Requirements document, which is a light weight combination of Requirements Document and Systems Analysis and Design document.  I can get away with something lighter because I will be building most of the application myself – I can wing it with some of the design.

One interesting outcome from this user/task oriented approach is the database layout.  When I started listing all of the things that a user needs in relation to the fly pattern (the central concept of the application) I found that I had created a horribly un-normal data record.  I went through an exercise to do some basic database normalization and got it at least to a 2NF state. Getting to 3NF can be more desirable, however I find that higher orders of normality can mean that many joins are necessary for searching, which is inherently inefficient.  Because I am using AIR and will be stuck with SQLite (which doesn’t allow foreign keys) 2NF should be good enough. I already know, from past projects, that you usually need one class for each of the database tables that you are loading into memory – so the database design has a direct correlation to the application’s data structures.

In previous posts I mentioned that I will be creating an Interface Design Document as well, but it is conspicuously absent.  I did try to write one, but I found out that I didn’t know enough of the capabilities of the AIR environment to write something useful.  I’m sure a Designer could have done it anyway, but I’m a coder – I need to learn what I don’t know first. I’ll probably write one up after I play with things a bit, some experimentation is necessary.

Ready, Fire, Aim

When last I posted, and it was a while ago, I was searching for an application to develop that would teach me some of the subtleties of AIR. I has laid out some possible options and I received a couple of suggestions from others.  Having thought about all of the possibilities, and made up a couple of new ones, I decided that I would go with a Fly Tying Inventory tool. 

I know, I know, there are many of you out there that are saying “well who the hell cares about something like that?”  Well, in this case the application is not the important thing, but rather its a vehicle for the learning of the software.  As such the application must have elements that lend themselves to multimedia (video, text, graphics, audio), offline capabilities (such as a local database), an engaging user interface, etc.  Lets look at how this tool would meet the criteria I set out before:

  • Must be user facing – back end software is incredibly important stuff, but AIR’s whole reason for being is to build an engaging interface
    Users will need to view patterns, search the database, enter new patterns and generally work with the pattern data that is available.  Users most likely are not developers and their patience with software may be limited.  Good interface design is very important.
  • Should make use of some of AIR’s special talents – otherwise why build it in AIR?

    • online/offline access – this implies that there is at least some kind of a back end system
      A central database of patterns that can be used to update the local software will be very useful
    • local database
      Patterns should be stored in a local database for easy searching and retrieval
    • have update capabilities for new versions of the software
      Updating the application as new features are added is a must
    • platform independence
      Being a public facing application we should not restrict users to a specific platform
    • encryption of data
      Admittedly this is not much of a requirement, fly patterns are not usually that much of a guarded secret (fishing locations on the other hand…..)
    • multimedia presentation
      Absolutely – patterns are listed as text with an accompanying picture. Often there are short videos demonstrating the tying as well audio explanations may be added

  • Should be fun, but it still should be somewhat useful.  Lets face it, I am in the high-tech industry and with that comes a bit of ADD.  The project has to be interesting enough to keep my attention.
    This is really a matter of opinion, but I find it very useful. There are thousands of patterns with many variations. Having a search tool would help a lot.
  • Must be well understood.  By this I mean that the project must be something concrete as opposed to an abstract thought experiment.  This is mainly so I can objectively assess the success of the project.
    No problem here, this will be a very concrete application.

Basically the application will be a storage and search tool for a specific type of multimedia. You really could substitute anything that has many parts and a recipe for putting them together and you would have the same thing (car parts inventory with installation instructions, stellar cartography with location data, etc.)

Okay, I’ve now got an application what are the next steps?  In this case I’m going to do two things:

1 – Put together a semi-formal set of requirements.  This will allow me to measure the success of the project against a fixed set of criteria.  It also will keep me from missing any key points in the software development.  Its a pretty common step in any software development.

2 – Create a visual design document.  This will let me keep the visual design consistant.  I believe that this is one of the most important documents when creating a user oriented application.  Its purpose is to make sure the visual integrety of the application is maintained.

I’m not going to create a 400 page manuscript, but a few of pages to keep things on track through-out the development process.  I’ll post the docs for discussion next time.


Of cabbages and kings

When I published my last blog entry I mentioned that I wanted to find a small but useful project on which to learn some of AIR’s nuances. The project has to be non-work related as that comes with some baggage (such as dead lines) that I don’t want to interfere with the learning process. 

At the risk of offending my old university Intro to Computer Programming professor (sorry Dr. Evans), I’m going to approach the problem bass ackwards. In this case I know what software I will use, I just need to find a project to develop.  My criteria are as follows:

  • Must be user facing – back end software is incredibly important stuff, but AIR’s whole reason for being is to build an engaging interface
  • Should make use of some of AIR’s special talents – otherwise why build it in AIR?
    • online/offline access – this implies that there is at least some kind of a back end system
    • local database
    • have update capabilities for new versions of the software
    • platform independence
    • encryption of data
    • multimedia presentation
    • etc……
  • Should be fun, but it still should be somewhat useful.  Lets face it, I am in the high-tech industry and with that comes a bit of ADD.  The project has to be interesting enough to keep my attention.
  • Must be well understood.  By this I mean that the project must be something concrete as opposed to an abstract thought experiment.  This is mainly so I can objectively assess the success of the project.

I also want to have something complex enough to be able to take advantage of many of AIR’s features while still being simple enough that one person can build it. I don’t want to need to learn an entire development architecture (such as
Cairngorm or Flight Framework). I think that adding a formal design pattern would muddy the waters right now.  Who knows, if this is a success maybe I’ll do another blog series on developing inside a framework.

So with this short list in mind I spent some time staring at the ceiling, thinking of a few applications.  Here’s what I have come up with so far:

  • Expense report tool - My job involves a fair amount of travel and that leads to filling in expense reports.  Most large scale expense reports software requires online access to impossibly huge back end systems such as SAP.  Unfortunately this means that I can’t enter my expense data while on the road (or on the plane).  It would be very helpful if I could create expense reports off line and then synchronize when I connect. Also, all of the user interfaces for expense systems that I have used seem to have been designed by accountants (no offence to accountants out there) with little regard for aesthetics.
    The drawback is that the connection to the back end system requires a pretty in depth knowledge of the systems APIs. I am worried that I could get bogged down in the back end connection.
  • Fly Tying Inventory – One of my favorite hobbies is fishing and recently I have taken up fly fishing and fly tying.  For those of you not familiar with this all encompassing waste of time and money – fly fishing consists of standing in the middle of a river waving a long stick around with a string tied to bits of feathers and fur on a hook.  The combination of feathers and fur are called a fly pattern and there are literally thousands of different fly patterns out there. As a software guy it would be nice to have a tool from which I could search those patterns. This sort of thing screams for a well organized database that could contain videos and images of each pattern.  The problem here is seeding the database with enough information to become useful
  • Tournament Tracker – I was recently involved in a kayak fishing tournament in support of the Ottawa Riverkeeper organization. A good friend and fellow Adobe employee had volunteered to help monitor the event and register each entrants catch as well as work out who won what prizes.  Being a “photo release” event, each angler was required to take a photo of their catch before returning it to the water.  The length of each catch determined the winners.  I couldn’t help but think that a tool for the organizers to track anglers would be useful.  At the same time a way for angler’s to see up to date tournament information would be pretty cool. As the “weigh-in” information is gathered on the beach, off-line use would be really useful.
  • Hockey Pool – I’m the first to admit that I’m not a hockey fan (which is blasphemy in this part of the world), but conceptually a hockey (or baseball, football, F1, cricket, whatever) pool may lend itself to this kind of application.  Data must be centrally stored, but user picks and some other operations may be able to happen offline (with a data synch happening later).  I wonder if the NHL would let me put in video clips :-)
  • Gas Price Monitor – A very timely idea.  It would be very interesting to have a tool that charted gas prices around the country (world?) and compared them to the price of crude oil.  There are a few web sites that do this, but their interfaces are not exactly the sexiest things in the world.  I can’t however think of an offline use for this as current data is key to the application.

Okay, so now I have a few ideas.  My next chore is to pick one of the above (or something else if it comes to me) and then start to put some requirements together. Remember my main goal is to learn AIR, the application I use to do that is secondary.