Auto-Updating the UcompOS Portal
As you know, at the Downloads portion of the UcompOS Project site, you can download the UcompOS Developers Package which contains the UcompOS Portal run-time, as well as the UcompOS SDK for JavaScript, Flash/Flex, and AIR 2.0.
This enables you to host a full UcompOS implementation on your local workstation for testing and development, or to host a public UcompOS implementation from your own webservers.
With the UcompOS project being updated frequently, the physical process of updating the UcompOS RXF and its associated components can be tedious.
There are ways you can automate the process, but it requires some simple scripting that can be accomplished in a variety of languages.
There are some static URLs to know about first of all.
The latest release of the UcompOS Developers package is always found at a URL of http://ucompos.ucompass.com/UcompOS.zip. That zip package expands into a folder named bin-release and within that folder are the contents of the UcompOS Portal. Also inside that folder is a file UcompOSSDK.zip which expands into an sdk folder, inside of which are an html, air, and flashOrFlex folder with each of the three respective SDKs for those different platforms.
In my current large scale UcompOS implementation, what I am doing is setting it up so that I have one and only one copy of the different SDK files – UcompOSSDK.swc (Flash/Flex), UcompOSSDKAIR.swc (AIR), and UcompOSSDK.js and UcompOSSDK.swf (JavaScript).
I then establish relative symlinks to the various locations on my local file system where those files need to be placed – libs folders in Flex projects and root webserver paths for JavaScript applications. For Flash applications I simply embed the UcompOSSDK.swc file from the singular location where the file exists on my workstation.
As far as updating the UcompOS Portal, the developers package bin-release folder contains a simple XML file named release.xml.
This is what the current one looks like as of this morning:
<release> <portal> <version>0.4.5</version> <packaged>Tue Mar 23 08:47:44 2010</packaged> </portal> <sdk> <version>0.4.5</version> <packaged>Tue Mar 23 02:02:53 2010</packaged> </sdk> </release>
Also, a file exists at the URL http://ucompos.ucompass.com/release.xml of the same format which you can test against to determine if you are running the same version of the UcompOS Portal or if there is an update available.
Note there IS an SVN Repository for the UcompOS Project but at this time it is just meant as a means to distribute the UcompOS SDK source code. Eventually, I plan to make all of the UcompOS RXF project open source and there will be a model for accessing the full source code of all the associated UcompOS technologies and you’d be able to structure your updates according to check-outs from SVN. (My timeline for this by the way is to coincide the open-sourcing of the UcompOS Portal with the production 1.0 release of the UcompOS RXF which I am trying to coincide with Adobe MAX 2010 in October).
In the meantime, there are still a number of simple ways you can go about coordinating an auto-update.
Below is a very simple PERL script I wrote that auto-updates the UcompOS Portal in my Educator 2 project:
#!/usr/local/bin/perl use strict; use LWP::Simple; use XML::LibXML; my $myVersion; if(!-e "../../frontend/release.xml") { $myVersion = 0; } else { open(FILE,"../../frontend/release.xml"); my @line = <FILE>; close(FILE); my $myReleaseInfo = join("",@line); $myVersion = getVersion($myReleaseInfo); } my $latestVersion = getVersion(get("http://ucompos.ucompass.com/release.xml")); if($myVersion ne $latestVersion) { update(); } sub getTextNode { my $thisNode = $_[0]; my @thisChild = $thisNode->getChildNodes; my $textNode; eval { $textNode = $thisChild[0]->getNodeValue; }; if(!$textNode) { eval { $textNode = $thisChild[0]->nodeValue; }; } return $textNode; } sub getVersion { my $releaseInfo = $_[0]; my $xml = XML::LibXML->new; my $string = $xml->parse_string($releaseInfo); my $xp = XML::LibXML::XPathContext->new($string); my @nodeSet = $xp->findnodes('//portal/version'); my $version = getTextNode($nodeSet[0]); return $version; } sub update { chdir('cache'); my $content = get("http://ucompos.ucompass.com/UcompOS.zip"); open(FILE,">UcompOS.zip"); print FILE $content; close(FILE); system("unzip -qq 'UcompOS.zip'"); unlink("UcompOS.zip"); system("rsync -a --delete --exclude=\"LocalLib.js\" --exclude=\"styles.css\" bin-release ../../../frontend"); system("\\rm -rf bin-release"); }
In my example above, I basically have my UcompOS Portal code housed in a bin-release folder. I check the latest release version and see if it matches my local version, and if it doesn’t, I retrieve the latest package, unzip it, and overwrite my existing copy with rsync excluding the LocalLib.js and styles.css files – which are the only files in the UcompOS Developers package you would typically need to update with your own implementation specific information.
Instantiating UcompOS Global (Continuum) Variables at Startup
In my post about UcompOS Shared Objects, I described how the UcompOS Portal furnishes a Shared Object Implementation that can be written to and read by any UcompOS entity in the UcompOS Continuum.
In a large-scale UcompOS Rich Portal Application, you very likely may find a need to share complex data across multiple UcompOS entities.
One effective approach to this challenge is to use the UcompOSGlobalManagerProxy class’s setSharedObject(), getSharedObject(), and getSharedObjects() methods.
These methods get/set shared objects on the UcompOS Portal at run-time.
But what if you want to populate the UcompOS Portal’s shared Object at start-up even before any UcompOS applications are launched?
As of the version 0.4.1 Milestone release of UcompOS RXF, there is a way to do this.
The key is a new <globalVariables/> element that can be added as a child element to the root <applications/> element in a dock manifest and as a child element to the root <application/> element in a UcompOS application manifest.
Below is a simple example of this in action in the dock manifest of my Educator 2 software application being built entirely on top of the UcompOS RXF:
<?xml version="1.0" encoding="utf-8"?> <applications> <globalVariables> <globalVariable> <key>_DOMAIN_</key> <value>applications.ucompass.com</value> </globalVariable> <globalVariable> <key>_PROTOCOL_</key> <value>http</value> </globalVariable> <globalVariable> <key>_GATEWAY_</key> <value>http://pilotfish2.ucompass.com/apps/gateway</value> </globalVariable> </globalVariables> <application background="true" default="true"> _PROTOCOL_://_DOMAIN_/Educator2PortalFoundation/manifest.xml </application> </applications>
Each global variable is wrapped in a <globalVariable/> element with child <key/> and <value/> elements.
All global variables in the dock manifest will automatically be added to the UcompOS Portal’s Shared Object implementation and will immediately be available to all UcompOS entities.
String Substitutions in Manifest Files
In the above example, notice I am instantiating global variable named _PROTOCOL and _DOMAIN_. And then notice that those strings appear in my <application/> element.
A string substitution will automatically take place here such that the <application/> value will be http://applications.ucompass.com/Educator2PortalFoundation/manifest.xml after the substitution.
As mentioned earlier, the <globalVariables> element is supported in both application manifests and dock manifests so you can also establish global variables in this fashion when launching a UcompOS application.
Typing Shared Objects
The <value/> element supports an optional type attribute such that you can articulate the data type of a Shared Object.
The best way to show how this is accomplished is with examples:
Array
<globalVariable> <key>groceryList</key> <value type="Array"> <item>Bananas</item> <item>Cereal</item> <item>Diapers</item> </value> </globalVariable>
Object
<globalVariable> <key>contacts</key> <value type="Object"> <item> <key>Joe</key> <value>555-1212</value> </item> <item> <key>Bob</key> <value>555-2121</value> </item> </value> </globalVariable>
The type attribute can also be of type uint, int, Boolean, Number and String. The default data type is String. Also, the <item/> element supports the type attribute for Arrays and Objects.
Also, nesting of complex data structures is supported, so you can implement an Array of Arrays, or an Array of Objects, etc.
Preloaders in UcompOS Implementations
In an enterprise-level UcompOS Rich Portal Application, many disaggregated applications may come together to create an overall rich experience.
Some of these applications may be relatively small HTML/JavaScript type applications that take virtually no time at all to load. Other applications may be large and complex Flex applications that easily approach several megabytes in file size.
Providing the user with feedback and information about an application while it is loading is a critical part of creating a positive rich experience.
The UcompOS Portal has a built-in pre-loader dialogue that is a stylized progress bar implementation.
By default, the label on the pre-loader will read “Ucompass.com, Inc.”.
You can however override this by passing in a parameter named preloaderTitle to the UcompOS Portal implementation.
Click here for a simple example of this.
If you don’t want to append a query string to the UcompOS Portal URL, you can also set the value of the preloaderTitle global variable in your LocalLib.js JavaScript package that is in the ucompos/local/LocalLib.js file in the UcompOS Developers Package. This resource is used to build custom authentication into your UcompOS Portal implementation.
Also, when you load Flash or Flex content into a UcompOS Window instance, you can customize the preloader that the user sees while the content is loading into the window.
By default, a generic progress bar dialogue appears (the same one used for the loading of the UcompOS Portal) with the title “Loading…” while the SWF content is loading into the UcompOS Window instance.
However, this text can be customized so that you could create messages that say things ranging from “Getting Sent E-Mails” to “Searching Flickr”.
Let’s take a look at the full signature of the add(); method of the UcompOSWindowProxy class:
public function add(source:String, title:String = Untitled Window, width:uint = 400, height:uint = 300, x:uint = 0, y:uint = 0, suppressParameters:Boolean = false, sandbox:Boolean = true, preloaderTitle:String = null, format:String = null):void
Notice the second to last parameter, preloaderTitle.
The value of this will be the value on the text field on the preloader that appears in the UcompOS Window while the SWF content is loading.
Below is a simple visual example of this:
The preloader dialogue does not show up on HTML type applications loaded into a UcompOS Window.
Also, you can specify the pre-loader to be used on a “self-loading” applications. A self-loading application is loaded immediately into a UcompOS Window versus simply being loaded invisibly into the UcompOS run-time. This is accomplished by setting a selfLoading attribute in the root element of the application manifest for the UcompOS application and giving it a value of true.
To do this, simply set a preloaderTitle attribute in the root element of the application manifest.
If you are loading a Flex application into a UcompOS Window and that application furnishes its own preloader, then that preloader will be used versus the UcompOS Window’s default preloader.
At this time, I have not created a way to overload the default preloader used in the loading of the UcompOS Portal so you can only customize its text field for now using the technique illustrated above.
Develop Against the Latest UcompOS Build Without Needing to Download the Build
Each time I make a change of substance to the UcompOS Rich Experience Framework’s core components, i.e. the UcompOS Portal or the UcompOS SDK, I roll out an incremental version update.
At the Downloads link, the most up to date releases of the UcompOS Developers Packages (which contains the UcompOS Portal client-side files and the UcompOS SDK) is available.
If you are actively developing and helping me find bugs and make improvements, the process of continuously downloading the developers package may get tedious especially as I am rolling out incremental updates fairly continuously.
I have devised a streamlined implementation model.
The way my development configuration is set up, the UcompOS implementation at http://ucompos.ucompass.com represents an implementation running on the very latest, most current build of the UcompOS Rich Experience Framework.
Now with your UcompId account, you can associate your UcompId with your own Dock Manifest URL.
When you log into the UcompOS Portal at http://ucompos.ucompass.com with your UcompId, the authentication process will use the dock manifest URL that you have registered.
In this configuration, you may still have to make frequent updates to your UcompOS SDK files (I’ll be posting about best practices for that soon) but you would at least not have to go through the process of downloading updates to the UcompOS Portal.
How UcompOS Flash/Flex Applications are Sandboxed in the UcompOS Portal
There are numerous paradigms for launching UcompOS Applications in the UcompOS Portal.
In many instances, the UcompOS Application itself will be a non-visual entity thought of as perhaps the Model and Controller in an MVC style implementation and then the UcompOS Application would launch individual sub-applications in UcompOS Window, UcompOS Browser Window, or UcompOS Artifact instances which could be considered the View in the MVC implementation.
A UcompOS Application can also be an AIR application running on the desktop launched by the UcompOS Portal and also, a UcompOS Application can be launched immediately into a UcompOS Window or UcompOS Browser Window instance by implementing specific attributes in the root of the UcompOS Application’s application manifest.
The specific implementation details of SWF-based UcompOS content being loaded as a UcompOS application or sub-application into the UcompOS Portal warrants discussion.
If you’re an experienced Flash/Flex developer, you may come to the UcompOS RXF with a pre-conceived idea about how things are implemented and how you would go about building rich experiences.
There are 3 different scenarios where SWF content will be loaded directly into the UcompOS Portal:
- The SWF content represents a UcompOS Application that is loaded into the UcompOS Portal run-time. In this case the SWF content is a non-visual entity. It gets loaded into a SWFLoader with its visible property set to false that is attached directly to the UcompOS Portal as a child. This application then likely is involved with launching sub-applications into UcompOS Window, UcompOS Browser Window, or UcompOS Artifact instances.
- The SWF content represents a UcompOS sub-application that is loaded into a UcompOS Window instance. In this case, the SWF content is a visual entity. It gets loaded into a SWFLoader and the SWFLoader is attached as a child to a UcompOS Window instance. A UcompOS Window instance is derived from a class that extends the MDIWindow class from the mdi package in the FlexLib project. And of course MDIWindow extends Panel.
- The SWF content represents a UcompOS sub-application that is loaded into a UcompOS Artifact instance. In this case, the SWF content is a visual entity. A UcompOS Artifact is actually a VBox implementation that gets attached to the UcompOS Portal and then the SWFLoader is attached as a child to the VBox instance. The UcompOS Portal is a Flex 4 application with the Halo theme compiled so that is how I am using VBox here.
Here’s the important thing to understand about the SWFLoader instances implemented in all 3 of the above scenarios. They are implemented in one of two “sandboxed” configurations.
If you are creating a Rich Portal Application and your intentions are to load two separate UcompOS Applications into the UcompOS Portal and then do some sort of coding where your accessing something like FlexGlobals.topLevelApplication, this will not work.
All communication with other entities in the UcompOS RXF should be accomplished exclusively via the communication protocols in the UcompOS SDK using implementations such as Proxy Components and public API methods exposed in Services Dictionaries.
The goal is to keep the UcompOS Portal as “dumb” as possible and enable it to focus exclusively on being really good at its base core objectives which are:
- An MDI (Multiple Document Interface)
- Style and aesthetic presentation management
- Artifact model for widget-like implementation
- UcompOS Application run-time
- Event Dispatchment architecture
The classes UcompOSArtifactProxy and UcompOSWindowProxy each have Boolean sandbox parameters. Also, the root <application/> element in an application manifest has a sandbox attribute which accepts values of true or false.
The default condition if you don’t set the sandbox parameter is true.
From an implementation point of view, all SWFLoader instances attached to the UcompOS Portal have the trustContent set to true. This may be something I rethink when moving the UcompOS RXF project to Beta but at least in my main UcompOS RXF application I am building, the domain that I serve the UcompOS Portal from will be the exclusive thing I serve from that domain as I am interested in creating the a strong degree of disaggregation. (Of course you should have a solid understanding of policy files (such as crossdomain.xml)).
In the case where the sandbox property is set to true (the default condition), the SWFLoader’s loadForCompatibility property is also set to true.
This creates a situation where the loaded SWF application is loaded into a sibling ApplicationDomain relative to the UcompOS Portal SWF application.
The loadForCompatibility property addresses the need for a SWF to be able to load child SWFs created with different versions of Flex.
For instance, if you tried to load a Flex 3 UcompOS application or sub-application into the UcompOS Portal with its sandbox set to false, you’ll get run-time errors.
So then when or why would you want to load a UcompOS SWF application or sub-application with its sandbox property set to false?
Primarily only in instances when you wanted to implement drag and drop functionality across the boundaries of a UcompOS sub-application.
If all the drag and drop functionality is within the boundaries of the SWF, then the sandbox setting is irrelevant.
However, suppose you have two UcompOS SWF sub-applications loaded into UcompOS Window instances and you want to enable items to be dragged and dropped back and forth between them. This would require you to set the sandbox property to false. Also, this would require you to build your applications with Flex 4 (although I admittedly have yet to try implementing drag and drop across two different non-sandboxed Flash CS5-created applications).
In an upcoming tutorial, I’ll show a demo of building a UcompOS-based file management system like the one I have built for Educator 2 that enables drag and drop between multiple UcompOS Window instances as well as drag and drop from a UcompOS Window instance to a UcompOS Artifact instance.
The key fact to ascertain here is that strong command of the UcompOS SDK and how it is utilized to send information back and forth between entities in the UcompOS Continuum is compulsory to your developing the most powerful and effective Rich Experiences.
A Framework for Easily Managing UcompOS Applications
As I’ve worked with the UcompOS platform since its Public Alpha release, I have come to be impressed with its flexibility in bringing a lot of different applications together into one portal system. However, as I built and tested various applications, it became very apparent that I needed to take a few steps back and think about a framework for deploying applications or risk becoming engaged in a never-ending battle with the following tasks:
- Editing static dock manifest files
- Keeping application manifest files between applications straight
- As the number of applications increases, easily finding the files I need to change without worrying about breaking another application becomes a challenge
- Deploying updated versions of the UcompOS runtime without accidentally deleting or overwriting application content
- Managing permissions to applications for different users
These issues were tackled in a fairly systematic manner. My first logical step was to get rid of the static dock manifest and move that to some application code that generates the dock manifest from a database. For my UcompOS implementation, information for applications that go on the dock menu is pulled from a very simple table that looks like this:

The Application_ID field is just a numerical value indicating the identifier of the application. It is the primary key for the database and auto increments as new applications are installed. The Application_Descriptor field is very simple, in function to the “title” attribute in your application manifest. The Background and Default_State fields contain the values each application should have when they are added to the dock manifest file dynamically. The Directory_Path field requires a bit of explanation. What I’ve done is create a folder called “applications” in my UcompOS implementation where all applications reside:

The Directory_Path field is the name of the folder where each of my UcompOS applications is launched from. The final field, Is_Global, is a field that indicates whether or not the application should be globally accessible to all users of my UcompOS Portal or not. Note that if this field is set to false, I have another database table that contains the list of users who have access to the application.
This database is managed through a front-end that grants/denies access to applications based on the selection of a checkbox (Checking a box calls an AJAX function that automatically adds/removes permissions to various applications) like in the screenshot below:

With all this in mind, the script that generates the UcompOS dock manifest file performs the following actions:
- Add all of the global applications in my applications table to the dock manifest
- Retrieve the list of which applications the user has access to, and add those applications to the dock manifest
- Return the entire dock manifest file to the UcompOS main container
You will notice above that I did not provide a filename for each application’s manifest file (just a directory path). This was an intentional decision, as I chose to have a convention where the name of the application manifest file would be in a sub-folder called “manifest” under each application’s folder, and that the application manifest filename would be the same for each application. This makes it very easy for me to easily identify the application manifest for each application, and helps maintain consistency across my UcompOS applications.

In my case, this application manifest for each of my UcompOS applications is named “getAppManifest.php”, and it is a simple script that returns the XML application manifest for the application. In it, the file does a security check to make sure that the user is logged in and that they have permission to access the application (if the application isn’t global). If everything checks out, then gatAppManifest.php spits out the application manifest for the application.
I should note that, for very practical reasons, I chose not to place the information for each application manifest file into a database table. While this was certainly a consideration, the realization I came to was that this was going to be just too cumbersome, especially if you factor in the possibility of multiple languages. Also, I considered the fact that this file is fairly static in nature, and wouldn’t need to be managed all that often. Ultimately, I felt that there wasn’t going to be much of a difference time-wise between managing this manually or through a database interface. For my framework that stresses ease of use, managing the application manifest via straight XML presented a far more straightforward path.
Before I close, I wanted to briefly touch on the file structure I have in place for my implementation. By having each application compartmentalized in its own subfolder, it is much easier to troubleshoot and find issues with each application without worrying about breaking anything else. Further, it allows me to do a simple drag and drop operation to deploy new applications in my UcompOS framework (I have a simple function in my application manifest file that adds the new application to the Application database if it doesn’t exist, and sets up any auxiliary tables needed for the application. I also have been able to create several reusable templates I can leverage or when I begin work on a new UcompOS application). Ultimately, the framework I’ve put into place solves all of the issues I mentioned above and greatly simplifies the application deployment process, allowing me to focus on application functionality instead of the mechanics of application deployment, which for me, is what it’s all about.
A Simple HTML Digital Camera Browser
In this tutorial, we will look at a variety of core UcompOS Rich Experience Framework concepts, and in particular we will explore the mechanics of adding a desktop component to your UcompOS Rich Portal Application.
It is recommended you download the source of the application we’ll build in this tutorial at the link below so you can follow along and there is also a video demonstration of the application we are going to build in this tutorial.
Download the Simple HTML Digital Camera Browser Source Code
This tutorial assumes you have at least a basic working knowledge of:
- Adobe AIR 2.0
- Adobe Flash Builder
- ActionScript 3.0
- HTML
- JavaScript
- You should have read my blog posts or watched my video tutorials about UcompOS Proxy Components and Services Dictionaries
The goals for our application are as follows:
We want to build a simple digital camera browser that lets the user browse through and view images from their digital camera in a Rich Portal Application implementation.
We are going to keep the application as deliberately simple as possible and we are not going to address its cosmetics or aesthetics so that we can focus on providing instruction on specific UcompOS concepts and principles.
To further define the specifications for our application, we want to build a UcompOS Application that prompts the user to connect their digital camera to their computer. We want our application to be able to know when their digital camera has been connected. Then when the digital camera has been connected, we want to display the contents of their camera to them. The user should be able to easily browse through their camera’s contents and they should be able to click on a file on the camera to view it. Also, the file should be opened in the native photo-viewing application on their computer versus simply displayed in the browser.
Again, we are going to focus on a very simple implementation and will not focus on aesthetics or presentation so that we can focus more on the core UcompOS mechanics we are leveraging to build our application.
Our UcompOS application is comprised of the following components:
- An AIR 2.0 UcompOS Application built with Adobe Flash Builder 4
- An HTML UcompOS Sub Application
We’ll walk through the process of setting up and building the different pieces of the application fairly linearly and then tie it all together at the end with a screenshot of our application.
Implementation Details
The way I want to design our program, I want a UcompOS Application to load on the UcompOS Application Dock entitled “My Camera”. When this application is opened, we want it to launch our UcompOS AIR 2.0 application. I want that application to prompt the user to connect their digital camera.
When the user connects their digital camera, I want to instantly launch a UcompOS Window instance in the UcompOS Portal that displays the contents of the camera to the end user and allows them to browse through any folder structures housed on the camera and then I want to allow them to select a file to be viewed in their default photo viewing application on their computer.
When the camera is disconnected, I want to shut down the application.
Setting up the AIR 2.0 Application
While I could use a number of different technologies to build our AIR application, I am going to use Adobe Flash Builder 4. The minimum required version for a UcompOS AIR application is AIR 2.0. You can learn more about AIR 2.0 and access its run-time and SDK at http://labs.adobe.com/technologies/air2/.
The first step is to set up a Flash Builder project for my AIR application.
My project is called Camera_Example. Pictured at left is the fully expanded project in Flash Builder with all its files. 
Our main class in our AIR application is Camera_Example.mxml.
Notice in my libs folder is the file UcompOSAIRSDK.swc. This is the UcompOS SDK file for AIR applications. This file is found in the UcompOS Developers Package in the sdk/air folder that is created when you unzip the UcompOSSDK.zip file contained in the package.
Simply drag and drop that file into the libs folder of any Flash Builder (or Flex) based UcompOS AIR application.
You can also incorporate the UcompOS AIR SDK into Flash-based and HTML-based AIR applications (the SDK has no Flex dependencies) but the techniques for doing so are outside the scope of this tutorial.
Ideally, my goal is for the end user to not even have any knowledge that an AIR application is involved other than the initial install process. I want the user to operate entirely within the web browser here and my rationale for this in this tutorial is with the goal in mind of showing how multiple technologies are fusing together to create a seamless rich experience.
Of course, AIR needs to be involved because AIR is what we use to do most of the heavy lifting in our application including detecting the camera attachment/detachment, browsing through the camera’s contents, and opening individual pictures on the desktop.
From an implementation point of view, an AIR application can only be launched from the web browser following a user-initiated event such as a mouse click.
When an AIR application is configured as the base source code for a UcompOS Application, and this application appears on the UcompOS Application Dock, when the user clicks the icon in the application dock, that user event is what triggers the launching of the AIR application.
I’ll add that it is possible to implement UcompOS AIR sub-applications and the best practice for doing this is to leverage the UcompOSArtifactProxy class. This topic will be covered in a future tutorial in the near future.
Our AIR Application’s Descriptor File
Our Camera_Example-app.xml file needs a very crucial adjustment.
By default, you’ll see this XML element commented out:
<!-- <allowBrowserInvocation></allowBrowserInvocation> -->This needs to be uncommented and issued a true value:
<allowBrowserInvocation>true</allowBrowserInvocation>
This tells the AIR runtime that your application is allowed to be launched from the web browser.
If you try to instantiate the UcompOS AIR SDK in an AIR application that does not have its descriptor set up in this manner, you’ll get a compile-time error and you won’t be able to package your application.
Even though we do want our AIR application to be as innocuous as possible, if a user does stumble upon it on their main OS’ dock or in the folder on their computer where it’s been installed, I want them to see the custom icon at left and this icon and other varieties in different sizes are in the assets_embed/png folder.
Therefore in my app-descriptor file, I have implemented the following:
<icon> <image16x16>assets_embed/png/image16x16.png</image16x16> <image32x32>assets_embed/png/image32x32.png</image32x32> <image48x48>assets_embed/png/image48x48.png</image48x48> <image128x128>assets_embed/png/image128x128.png</image128x128> </icon>
AIR Application Code
Since I want the user to know as little as possible, if anything, about the presence of the AIR application, I want it to be invisible. Therefore, I’ll give the visible property in the root WindowedApplication tag a value of false. This will suppress any windows from being displayed.
Next, I want to instantiate the UcompOS SDK.
This should happen once the main application dispatches its applicationComplete event.
My root MXML tag looks like this:
<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" applicationComplete="start();" visible="false">
Now let’s take a look at the private variables I am declaring in my main class. We’ll cover the purpose of each of these variables further in the tutorial:
private static var _cameraRoot:String; private var _h:UcompOSHTMLProxy; private var _d:UcompOSDockProxy;
Now let’s take a look at my start(); method:
private function start():void { AIRSDKClient.getInstance(this,new ServicesDictionary()); AIRSDKClient.getInstance().addEventListener(SDKClient.SDK_READY,ucompos_init); implementStorageVolumeListeners(); }
The instantiation of the UcompOS AIR SDK is very similar to the instantiation of the UcompOS Flex/Flash SDK that targets browser-based content with a few key exceptions:
- The Singleton class AIRSDKClient is leveraged versus the SDKClient class
- We pass this as the first parameter to the getInstance(); method and an optional Services Dictionary as the second parameter. We’ll look at the Services Dictionary for this application below.
- Internal to the AIRSDKClient class, the SDKClient class is instantiated. Once it is instantiated, it dispatches an Event of type SDKClient.SDK_READY and only then can you safely instantiate and use any of the Proxy Components built into the SDK so you must listen for this event and implement any Proxy Component-related startup code in the event handler for this event
In our start(); method, we have a call to implementStorageVolumeListeners();
Let’s take a look at the implementStorageVolumeListeners(); method:
private function implementStorageVolumeListeners():void { StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT,mountHandler); StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT,unmountHandler); }
This method leverages AIR 2.0 capabilities. StorageVolumeInfo is a Singleton class in AIR 2.0 that can have an event listener attached to it to handle StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT and StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT events. These events are dispatched whenever a new mount point is introduced to the base Operating System or whenever a mount point is removed. Our handlers for these events are mountHandler(); and unmountHandler(); respectively.
Before we take a look at mountHandler(); and unmountHandler();, let’s take a look at the ucompos_init(); method that is invoked once our UcompOS AIR SDK has been fully initialized and we are ready to interact with it fully:
private function ucompos_init(event:Event):void { _h = new UcompOSHTMLProxy(); _h.alert("Please connect your digital camera to your computer"); _d = UcompOSDockProxy.getInstance(); }
In this method, we are creating an instance of UcompOSHTMLProxy. This class has a number of methods that let us execute common JavaScript methods such as alert();, prompt();, and confirm(); in the UcompOS Portal’s HTML wrapper file.
I am choosing to use a regular JavaScript alert to prompt the user to connect their digital camera to their computer.
I am also going to create a reference to the UcompOSDockProxy Singleton and my reasons for doing this will become clear below.
Now, let’s take a look at the mountHandler(); method.
private function mountHandler(event:StorageVolumeChangeEvent):void { _d.setAlert(true); _cameraRoot = event.rootDirectory.nativePath; w = new UcompOSWindowProxy(); w.add("http://desktop.ucompass.com/Camera_Example/Camera_Browser.html",event.rootDirectory.name,400,400); var object:Object = API.getFiles({}); }
This method is invoked when the user attaches a new storage volume to their computer. It is worth mentioning at this time that the simple example being developed here could be used to browse any type of removable storage. We just happen to be focusing on a scenario that would involve a digital camera.
The implementation details of mountHandler(); are straightforward. First, we want to call the setAlert(); method of the UcompOSDockProxy instance and pass a value of true to it. This makes the icon associated with this application on the UcompOS Portal Application Dock “Chirp” and glow drawing the user’s attention to it.
Then, we want to set the _cameraRoot property to event.rootDirectory.nativePath. The StorageVolumeChangeEvent contains a rootDirectory property which is of type File and represents the file location on the file system where the base of the mount point is located.
Next we create an instance of UcompOSWindowProxy of 400 x 400 and load our HTML sub-application into it. Our HTML sub-application will be the actual camera browser that the end user interacts with and we’ll review that later.
Our unmountHandler(); method is extremely simple:
private function unmountHandler(event:StorageVolumeChangeEvent):void { w.close(); UcompOSGlobalManagerProxy.getInstance().quitApplication(); }
This calls the close(); method on our UcompOSWindowProxy instance and then quits out of the application once the camera is removed.
Way back when we instantiated the UcompOS AIR SDK, we passed a new instance of ServicesDictionary to the instantiation method.
There are two public API methods we need our AIR application to sponsor and we are calling them Camera.getFiles and Camera.openFile.
Camera.getFiles will take the path to a given folder on the file system and return a list of the contents of that folder.
Camera.openFile will take the path to a given file on the file system and open it up with the application on the computer that the file is associated with.
First, let’s take a look at our ServicesDictionary:
package cameraexample { import com.ucompass.ucompos.sdk.server.AbstractServicesDictionary; public class ServicesDictionary extends AbstractServicesDictionary { public function ServicesDictionary() { _map = { 'Camera.getFiles': { static:true, classRef:API, method:'getFiles', description:'Lists files in a folder' }, 'Camera.openFile': { static:true, classRef:API, method:'openFile', description:'Opens a file in its native application' } } } } }
As you can see, both of our public API methods are housed as static methods in an API class.
Here is the method that corresponds to the Camera.getFiles public API method:
public static function getFiles(data:Object):Object { var folder:String = data.folder; if(!folder) { folder = Camera_Example.cameraRoot; } var file:File = new File(folder); var files:Array = []; for(var i:uint = 0;i<file.getDirectoryListing().length;i++) { var _file:File = file.getDirectoryListing()[i] as File; files.push({name:_file.name,isDirectory:_file.isDirectory}); } return {eventType:"files",files:files,folder:folder}; }
The Camera.getFiles public API method expects a folder property to be on the Object parameter passed to the method. If it’s not, it retrieves the contents at the base of the camera.
Back in our base application, we have a static getter function that retrieves the value of cameraRoot (which is why we established the value of _cameraRoot in the mountHandler(); method).
Our method simply builds an Array of Objects each having a name property and a Boolean to indicate if the item is a directory.
In our return Object, we return the eventType property set to files as well as our Array of files and a reference to the folder whose contents were retrieved. We’ll learn more about the purpose of this eventType property when we look at our HTML sub-application.
Our public API method Camera.openFile is extremely simple:
public static function openFile(data:Object):Object { var file:File = new File(data.file); file.openWithDefaultApplication(); return {}; }
That’s it for our AIR application. We are ready to package it with adt or the compiler built into Flash Builder.
I am packaging it into a file named Camera_Example.air and it will be reachable at a network URL of http://desktop.ucompass.com/Camera_Example/Camera_Example.air
Our HTML Sub-Application
Now we are ready to build our HTML sub-application which will be the interface the end-user actually interacts with.
The URL of our application will be at http://desktop.ucompass.com/Camera_Example/Camera_Browser.html. This is the URL passed to the add(); method of our UcompOSWindowProxy instance of our mountHandler(); method in our AIR application.
We want this application to be extremely simple.
We just want it to list out the contents of our digital camera and present them as files or folders.
When the user clicks on a folder resource, we want to display the items in that folder. When they click on a file resource, we want to open that file in the application that the file is associated with on their computer.
From an implementation point of view, when we click a folder, we are going to call our AIR application’s public API method Camera.getFiles and when we click a file we are going to call Camera.openFile.
The first thing we are going to do in our HTML sub-application is implement the UcompOS JavaScript SDK:
<script type="text/javascript" src="/UcompOSSDK.js"></script>
While it is not a requirement, best practice recommends you place the UcompOS JavaScript SDK and SWF files in the root directory of your webserver.
Here are two variables we initialize:
var camera; var d;
When the UcompOS JavaScript SDK has initialized, it looks for a start(); method in the application its implemented into.
Our start(); method is as follows:
function start() { d = new UcompOSDockProxy(); camera = new Camera(); camera.addEventListener("files",filesHandler); camera.getFiles(); }
We are creating an instance of UcompOSDockProxy which we’ll use to suspend the Dock alert we set in our AIR application.
More importantly, we are creating an instance of Camera, and adding an event listener to it and calling its getFiles(); method.
Camera is a Proxy Component we have built in our HTML sub-application. A Proxy Component is an interface to the public API methods located in other entities.
In our case, the Proxy Component Camera in our sub-application is the interface to the Camera.getFiles and Camera.openFile public API methods sponsored by our AIR application.
Let’s take a look at our Proxy Component Camera and walk through it as the mechanics of Proxy Components are very important to understand:
function Camera() { this.setDestination(parentConnectionId); this.getFiles = function(folder) { this.call("Camera.getFiles",{folder:folder}); } this.openFile = function(file) { this.call("Camera.openFile",{file:file}); } } Camera.prototype = new AbstractProxyComponent(); Camera.prototype.constructor = Camera;
The last two lines of the class would be analogous to saying Camera extends AbstractProxyComponent in ActionScript 3.0. Any Proxy Component must extend AbstractProxyComponent (in ActionScript as well as JavaScript).
In our class implementation, we pass the parentConnectionId property to the setDestination method of our class (which is a method inherited from AbstractProxyComponent).
Since our sub-application was launched by our AIR application, in the context of the UcompOS Continuum, we know that our AIR application is the parent of the sub-application in scope and we can safely use the UcompOS JavaScript SDK global variable parentConnectionId (this is analogous to the public property SDKModel.getInstance().parent in the UcompOS AIR/Flash/Flex SDK).
Our Camera class also implements two methods: getFiles(); and openFile();. As you can see by referring to the class code, both of these call the public API methods in our AIR application Camera.getFiles and Camera.openFile by using the call(); method in our class that is inherited from AbstractProxyComponent.
Another very important point, in our start(); method, refer again to this command:
camera.addEventListener("files",filesHandler);
This tells our instance of our Camera class to pass any SDKEvent’s of type “files” to the method filesHandler.
If you refer to our AIR application public API method Camera.getFiles, you’ll recall its return Object sets an eventType property to “files“.
The return Object of the public API method Camera.getFiles is passed to our filesHandler(); method.
Here is the code of our filesHandler(); method:
function filesHandler(data) { var e = document.getElementById('files'); e.innerHTML = '<p><a href="javascript:void(0);" onclick="getFiles();">Camera Root</a><p/><hr/><p/><u>Current folder: '+data.folder+'</u>'; for(var i = 0;i<data.files.length;i++) { if(data.files[i].isDirectory) { e.innerHTML+='<p/><img src="icons/folder.gif"/> <a href="javascript:void(0);" onclick="getFiles(\''+data.folder+'/'+data.files[i].name+'\');">'+data.files[i].name+'</a>'; } else { e.innerHTML+='<p/><img src="icons/file.gif"/> <a href="javascript:void(0);" onclick="openFile(\''+data.folder+'/'+data.files[i].name+'\');">'+data.files[i].name+'</a>'; } } }
Notice we are referencing the files and folder properties of the Object passed to filesHandler();. We iterate on the files property which we know from our inspection of our AIR application’s public API method Camera.getFiles is an Array and we further know that each Object in this Array has a name:String and isDirectory:Boolean property.
We create simple HTML that displays the name of the files and folders with the appropriate icons and calls the methods getFiles(); for folders and openFile(); for files.
These methods appear below:
function getFiles(folder) { d.setAlert(false); camera.getFiles(folder); } function openFile(file) { camera.openFile(file); }
In getFiles(); as well as openFile();, notice we are calling the setAlert(); method of the UcompOSDockProxy and passing it a value of false. This is to cancel the Dock alert we set on the UcompOS Portal’s Application Dock that we set in the AIR application the first time the user clicks on a resource.
That’s all there is to our HTML sub-application.
Configuring Everything as a UcompOS Application
Now we need to set up our application manifest for our simple Digital Camera browser application.
This should be very straightforward if you’ve reviewed some of my other UcompOS tutorials but for AIR applications, there are some special configurations you need to make:
<application> <source> <base>http://desktop.ucompass.com/Camera_Example/Camera_Example.air</base> <params> <param> <name>appId</name> <value>Camera-Example</value> </param> <param> <name>publisherId</name> <value>0E5CA255707A7E3F70F12D38B16B8D2A4C17413C.1</value> </param> </params> </source> <titles> <title locale="en_US" default="true">My Camera</title> </titles> <icons> <icon locale="en_US" default="true">http://desktop.ucompass.com/Camera_Example/icons/camera.png</icon> </icons> </application>
Notice the appId and publisherId parameters you must include in the <params/> element of the manifest.
IMPORTANT: At the time I am authoring this tutorial, the evening of December 27, 2009, the publisherId field faces an uncertain future in AIR 2.0 and may be deprecated. At present, you can find your publisherId by looking in the $APP/Contents/Resources/META-INF/AIR/publisherid file in the application installation directory for your installed application. The appId and publisherId parameters must be included otherwise, the UcompOS Portal will not be able to successfully launch your UcompOS AIR application. Any changes to the AIR 2.0 implementation specifics for publisherId will be blogged about here and updates will immediately be made to the UcompOS RXF accordingly.
Next we’ll take a very quick peak at my Dock Manifest:
<applications> <application> http://desktop.ucompass.com/Camera_Example/manifest.xml </application> </applications>
In this case, I obviously just have a single application I am loading into my UcompOS Portal implementation that is our simple Digital Camera browser example.
Screenshot of the Application
Conclusion
In this tutorial, we created a deliberately simple application to demonstrate a number of core UcompOS RXF concepts and principles – particularly integrating the desktop into a UcompOS Rich Portal Application implementation.
A Services Dictionary Publishes a UcompOS Entity’s Public API Methods
As I discussed in my posting yesterday, each application, sub-application, and the UcompOS Portal itself is considered an entity in the context of the UcompOS Continuum.
For an entity to play a meaningful role in the Continuum, it must have the UcompOS SDK installed and there is an SDK for Flash/Flex, AIR, and JavaScript UcompOS applications.
The SDK enables each entity to serve as both a client as well as a server as far as its ability to send requests to and receive requests from other entities.
The implementation details of the UcompOS SDK are well hidden from the developer, however, to build the most engaging and interactive experiences with the UcompOS RXF, a command of the core mechanics of the UcompOS SDK will serve you very well.
Before I continue, I recommend you take a look at the Video Tutorials entitled Services Dictionaries and Proxy Components that will provide a foundation for the client-server paradigm of a UcompOS entity.
Messages are sent back and forth between entities in the UcompOS Continuum in a format called a DataPackage. DataPackage is a class in the UcompOS SDK. Full SDK documentation is online for ActionScript and JavaScript developers.
The SDK class SDKClient is the interface to the UcompOS Continuum for outbound DataPackage instances and the class SDKServer is the interface to the UcompOS Continuum for inbound DataPackage instances.
The functionality an entity makes publicly available to other entities are considered that entities Public API Methods. The list of Public API Methods that an entity sponsors are published in an instance of a class known as a Services Dictonary.
A Services Dictionary by strict convention will extend the abstract class AbstractServicesDictionary which is built into the SDK.
AbstractServicesDictionary furnishes a protected variable _map, which will be overridden in the extending Services Dictionary class instance.
_map is a simple Object where each property is an Object whose key is the name of a public API method and whose value is an Object that points to the internal implementation location of the functionality.
For instance, in the UcompOS Portal Services Dictionary, you’ll see the following property in the _map Object:
'MDI.addWindow':{ static:false, classRef:com.ucompass.ucompos.controller.api.commands.MDI, method:'add', description:'Forms a new MDI window on the UcompOS Main Container' }
MDI.addWindow is then considered a Public API Method sponsored by the UcompOS Portal.
You can sort of think of a Services Dictionary as being analogous to a web server configuration file where virtual hosts are analogous to public API method names and the local directory they resolve to are analogous to the mapping to the internal functionality that is leveraged when the public API method is contacted.
When the UcompOS SDK is instantiated, the static getInstance() method of the SDKClient class is passed a reference to the main application class (an extension of MovieClip or Sprite in a Flash application or an implementation of Application or WindowedApplication in a Flex application).
The syntax to instantiate the UcompOS SDK client is as follows:
SDKClient.getInstance(this);
and this command is generally going to be in the application’s main class either in the constructor of the main Document class in a Flash application, or in a private event handler in the main MXML document of a Flex application being called AFTER the applicationComplete event is fired.
The instantiation of the UcompOS SDK Client can accept a second parameter as well which would be a new instance of a Services Dictionary. So suppose in my entity my Services Dictionary was a class named ServicesDictionary. I would instantiate the UcompOS SDK as follows:
SDKClient.getInstance(this,new ServicesDictionary());
Once I do this, any other entity can easily target my entity’s public API methods.
I will add at this point that by strict convention, any public API method must be configured to accept exactly one parameter of type Object and return a value of type Object. Any data that needs to be sent to a particular public API method must be encapsulated within the single Object parameter passed to it.
Also, the UcompOS SDK has been kept deliberately as simple as possible. There are no Flex dependencies in it which is why you can use the SDK interchangeably in Flash as well as Flex applications so keep this in mind when packaging data for transmission across the UcompOS Continuum. For instance, sending an ArrayCollection from a Flex application to the public API method of a Flash application will obviously not work.
An entity can target functionality in another entity even if that functionality is not exposed in the target entity’s Services Dictionary. However in this case, obviously, the calling entity requires intimate knowledge of the internal implementation details of the target entity.
In my blog posting tomorrow, I’ll focus on the other side of a UcompOS transaction between two entities and I’ll introduce Proxy Components. A Proxy Component is a client to the public API method in another entity. As a brief foreshadowing, the UcompOSWindowProxy’s add(); method in the UcompOS SDK can be considered a client to the example UcompOS Portal public API method above, MDI.addWindow.
Further, packaged into the UcompOS SDK are a host of Proxy Components that represent the client interface to all the public API methods that are exposed by the UcompOS Portal.
More on this crucial topic tomorrow.



