Cross-Application Interactions in UcompOS Implementations

One of my primary goals in the design of the UcompOS Rich Productivity Framework was to create a flexible environment for the deployment of multiple Rich Internet Applications that needed to work either separately, or discretely to create a singular overall rich experience.

UcompOS Applications launched by the UcompOS Portal are sandboxed in such a way that they exist as isolated entities that can operate independently from the environment they’ve been loaded into.

As I touched upon in my posting on Building Drag and Drop Experiences in the UcompOS RPF, UcompOS applications should not attempt to access other UcompOS entities directly through direct ActionScript or JavaScript class/method interaction.

This is not to say that you cannot do this, but if you were going to, say, load two separate UcompOS entities into a n0n-sandboxed configuration, and have each entity access the other’s classes, you would obviously be tightly coupling the entities in such a way that if two different developers were working on each entity, one could very easily break the implementation unintentionally.

If you have a very compelling reason to do this, I have not prevented you from doing this.

In this posting, I will touch upon some of the numerous models for achieving cross-application interactivity in the UcompOS RPF.

Proxy Components and Services Dictionaries

The model for interactivity across the boundaries of UcompOS entities and applications is to use the framework that’s built into the UcompOS SDK, mainly Services Dictionaries and Proxy Components.  These are two extremely important concepts to understand, which is why I touch upon them so frequently.

In review, a Services Dictionary publishes a list of the “Public API” methods that an entity exposes to other entities.  A Proxy Component is a client apparatus in one entity that is built to connect to the Public API method sponsored by some other entity.

As I’ve also explained numerous times, and particularly in this posting, the UcompOS SDK has a number of Proxy Components built into it that are client apparatus’ to the Public API methods of the UcompOS Portal.  After all, the UcompOS Portal is simply one entity in the overall context of the UcompOS Continuum.

Numerous postings and tutorials I’ve posted give examples of building Services Dictionaries and Proxy Components so I won’t go into the details of this in this posting, but again, understanding their mechanics is crucial.

UcompOS SDK Events

Another crucial concept to understand in the UcompOS RPF is its Event-based implementation.

By strict convention, all Proxy Components extend a class in the UcompOS SDK named AbstractProxyComponent.

As the name implies, it is an abstract class and should never be instantiated directly, only extended.  (Unfortunately, ActionScript 3 doesn’t give us a true foolproof way to enforce abstractness.)

AbstractProxyComponent extends EventDispatcher so thus all Proxy Components inherit an addEventListener(); method.

So essentially, in one UcompOS entity, you can build a Proxy Component that talks to the Public API method in another entity.  And you can register an event listener to receive event notices from that Proxy Component.

That’s why I call these things “Proxy” Components.

You are creating a virtual representation in one entity of a physical implementation in another entity, so essentially, your Proxy Component is a “Proxy” to the true implementation that is actually located in an entirely different entity.

So when you do something like the following:

var w:UcompOSWindowProxy = new UcompOSWindowProxy();
w.add("http://blog.ucompass.com","UcompOS",800,600);
w.addEventListener(UcompOSWindowProxy.CLOSE,closeHandler);

w in the above scenario is an instance of the Proxy Component UcompOSWindowProxy that is built into the UcompOS SDK.

w is not REALLY the window that gets created on the UcompOS Portal, but it is a virtual representation of it.

But when you add an event listener on the CLOSE event of the window, it is as if you are attaching an event listener to the actual window itself.

I am hopeful that the concept of Proxy Components is making more and more sense to the dedicated readers of this blog.

The event handlers implemented with any Proxy Component are exclusively dispatched an instance of the SDKEvent class.

The SDKEvent class is worth studying.

If you look at the AS3 docs for the SDKEvent class, you’ll notice it has a collection of read-only properties.  One of which is data and another is proxyComponent.

By strict convention, any Public API method in a UcompOS entity returns an item of type Object.  This data is carried back to the Proxy Component that invoked the Public API Method in the form of an SDKEvent instance.

So for instance, building to our simple example above, I could do something like this:

w.addEventListener(UcompOSWindowProxy.WINDOW_INFO,windowInfoHandler);
w.getWindowInfo();
 
private function windowInfoHandler(event:SDKEvent):void
{
 
 // this will be the Object returned by the Public API method I contacted
 var data:Object = event.data;
 
 trace("Window size is "+data.width+" x "+data.height);
 
}

Let’s talk a bit about the proxyComponent property of SDKEvent instances.

It is of type IProxyComponent.

The AbstractProxyComponent event implements the interface IProxyComponent from the SDK, so any Proxy Component can be cast as an item of type IProxyComponent.

Dispatching an Event to a Proxy Component from a Public API Method

So you have built your Public API method and you have set up a Proxy Component in another entity to connect to this Public API Method.

As I have explained, the Object your Public API Method MUST return will be returned to your Proxy Component packaged in an SDKEvent instance.

But what if you asynchronously want to dispatch an SDKEvent to a Proxy Component?

The key to this is the BroadcastEvent class in the SDK and its static dispatchEvent(); method.

As an example of when we might use this capability, suppose we have a UcompOS AIR application that sponsors a Public API Method that allows us to asynchronously return the list of files in a directory.

The implementation of this might look something like this:

private var _uuid:String;
 
public function getFilesInDirectory(data:Object):Object
{
 _uuid = data._dataPackage.uuid;
 var directory:File = new File(data.directory);
 directory.addEventListener(FileListEvent.DIRECTORY_LISTING,getFilesInDirectory_handler);
 directory.getDirectoryListingAsync();
 return SDKEvent.VOID;
}
 
private function getFilesInDirectory_handler(event:FileListEvent):void
{
 var files:Array = [];
 for(var i:uint = 0;i<event.files.length;i++)
 {
 files.push((event.files[i] as File).name);
 }
 BroadcastEvent.dispatchEvent(_uuid,"fileListing",{files:files});    
}

As a brief aside, notice in the Public API method, I am returning SDKEvent.VOID.  This actually suppresses the return altogether of any data to the calling Proxy Component.  This is the best practice when your Public API method doesn’t return anything of value to the calling Proxy Component.

When the UcompOS SDK passes the input Object to a Public API method, it adds a property to the Object called _dataPackage which itself is an Object.

This Object has a property, uuid, which is the same value of the uuid property on the Proxy Component that originated the transaction with the Public API method.

The BroadcastEvent.dispatchEvent(); method expects this uuid value as the first input parameter.

A Proxy Component that invoked this Public API method could have an event listener listening for events of type “fileListing”.  In this scenario, the event handler would be dispatched an SDKEvent with the data dispatched to the BroadcastEvent.dispatchEvent(); method.

Dispatching Continuum Events

The UcompOSGlobalManagerProxy class in the UcompOS SDK has a method named dispatchContinuumEvent();.

It accepts two parameters – the type of event, and a data Object.

When you call this method, you are dispatching an event to every entity in the UcompOS Continuum and the event can be listened for on the UcompOSGlobalManagerProxy Singleton.

So for instance, you could say something like:

UcompOSGlobalManagerProxy.getInstance().dispatchContinuumEvent("myEvent",{name:Ed});

And in an entirely different entity, you could do something like this:

UcompOSGlobalManagerProxy.getInstance().addEventListener("myEvent",eventHandler);

and your event handler could look something like:

private function eventHandler(event:SDKEvent):void 
{
 
 trace("Your name is "+event.data.name);
 
}

The dispatchContinuumEvent(); method doesn’t need to target a specific entity as the BroadcastEvent.dispatchEvent(); method does – it simply dispatches an event to EVERY entity.

Launching one UcompOS Application from Another Entity

The UcompOSGlobalManagerProxy class has a launchApplication(); method which accepts as input an Application Manifest URL as a String.

This enables you to launch a UcompOS Application from another entity.  Similarly, there is a quitApplication(); method as well as a getConnectionId(); method which can be used to find the connectionId of an already running UcompOS Application.

UcompOS Shared Objects

As I posted here, the UcompOS RPF now has a Shared Object implementation where an entity can house Objects of specific names on an Object on the UcompOS Portal that is available to all other entities.

When any Shared Object is created or its properties are updated, a Continuum Event is dispatched.

This creates an environment where the Shared Object itself can become a useful medium for achieving inter-application and inter-entity communication.

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:

PreloaderWindow

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.

New Proxy Component in UcompOS SDK: UcompOSAlertProxy

This morning, I have just added a new Proxy Component to the UcompOS SDK: UcompOSAlertProxy that is available in version 0.2.2 and later of the UcompOS Developers Package.

UcompOSAlertProxy provides a way to create a modal dialogue on the UcompOS Portal via an implementation of the Flex Alert control.

Previously, I had made this possible with the createAlert(); method of the SDK class UcompOSGlobalManagerProxy.

The inefficiency with this is that UcompOSGlobalManagerProxy is a Singleton class (along with UcompOSDockProxy, UcompOSMenuBarProxy, and UcompOSStyleProxy).  So one instance of UcompOSGlobalManagerProxy would handle the event dispatching for all Alert dialogues created with its createAlert(); method.

So handling events dispatched by Alerts created with UcompOSGlobalManagerProxy‘s createAlert(); method required extra handling and actually provided risks of gaps in data integrity and continuity.

I have removed the createAlert(); from UcompOSGlobalManagerProxy and replaced the functionality with the simpleAlert(); and confirmAlert(); methods of the UcompOSAlertProxy class.

The simpleAlert(); method launches a simple Alert with a single control button that will default with a label of “OK”.

The signature of simpleAlert(); is as follows:

public function simpleAlert(title:String, message:String, okLabel:String="OK"):void

You have the ability to customize the label on the single button presented on the Alert control.

The signature of confirm(); is as follows:

public function confirm(title:String, message:String, okLabel:String = null,
cancelLabel:String = null, defaultButton:String="OK"):void

You can specify the labels to be used on both the OK and Cancal buttons and you can articulate which should be the default button.

When the user takes action and clicks a button on the Alert dialogue, an instance of SDKEvent is dispatched by the UcompOSAlertProxy instance of type UcompOSAlertProxy.ALERT_SUBMIT.

The SDKEvent instance’s data property will have a detail property of type String which will have a value of UcompOSAlertProxy.OK or UcompOSAlertProxy.CANCEL.

Let’s look at a very simple example with the aid of some code:

private function fileExists(file:String):void
{
 var a:UcompOSAlertProxy = new UcompOSAlertProxy();
 a.data = file;
 a.addEventListener(UcompOSAlertProxy.ALERT_SUBMIT,fileExists_handler);
 a.confirm("A file named "+file+" exists in the target location. 
 Do you wish to overwrite it?","Yes","No",UcompOSAlertProxy.CANCEL);
}
 
private function fileExists_handler(event:SDKEvent):void
{
 var a:UcompOSAlertProxy = event.proxyComponent as UcompOSAlertProxy;
 if(event.data.detail==UcompOSAlertProxy.OK)
 {
 trace("Overwrite file "+a.data);
 }
 
}

Building Full Screen Mode UcompOS Implementations

Since the release of Adobe Flash Player 9 in 2006, the Flash Player is capable of entering into what is known as “Full Screen Mode“.

In Full Screen Mode, the content playing in the Flash Player takes over the entire viewport of the display device such that the only content in view and capable of being interacted with is the content currently being executed in the Flash Player.

Depending on the nature of your application, this can in some circumstances create a much more focused and positive experience for your users.

The UcompOS Portal is configured to operate in Full Screen Mode and the UcompOS SDK provides a means for sending the UcompOS Portal into Full Screen Mode from one of your UcompOS applications or sub-applications.

Of course there are some rules and principles to observe when leveraging the power of Full Screen Mode in your UcompOS Applications.

In my blog posting on the Mechanics of UcompOS Desktop (AIR 2.0) Applications, we learned that the launching of an AIR application by the Flash Player must be preceded by a user-triggered mouse event.

The same rule applies to entering into Full Screen Mode.

The UcompOSGlobalManagerProxy class has two pertinent methods:  enterFullScreenMode(); and exitFullScreenMode();.

Both of these methods accept an optional single input parameter, message, of type String.

When you call either of these methods from one of your UcompOS applications or sub-applications, in order to generate the compulsory user mouse click, the UcompOS Portal throws up an Alert dialogue asking the user if they want to allow the request to go into or out of full screen mode.

Of course having the dialogue appear for exitFullScreenMode(); calls is somewhat dubious as all the user needs to do is hit the Escape key on their computer to break out of Full Screen Mode but for continuity, I have decided to keep the mechanics of throwing the Alert in calls to both enterFullScreenMode(); and exitFullScreenMode();.

The optional message parameter lets you customize the text that is presented on the Alert dialogue.

When the UcompOS Portal breaks into or out of Full Screen Mode, a Continuum Event (an instance of the SDKEvent class) of type UcompOSGlobalManagerProxy.SCREEN_MODE_CHANGE is broadcast.

The data Object of the event will have a state property with a literal string value of “normal” or “fullScreen” so that your applications and sub-applications can behave accordingly when the user enters/exits Full Screen Mode.

In the scenario we discussed in my AIR 2.0 UcompOS Applications posting mentioned above, we saw we can leverage the UcompOSArtifactProxy class to create a clickable UcompOS Artifact that immediately launches a particular AIR application (with the aid of the UcompOSAIRProxy class).

This same concept is also available with Full Screen Mode implementations.  You can create a UcompOS Artifact and assign its data Object special properties that tell the UcompOS Portal to launch or exit Full Screen Mode when the artifact is clicked on.

The property names will be enterFullScreenMode and exitFullScreenMode and both should be of type Boolean with a value of  true.

They should be mutually exclusive and you should not have the enterFullScreenMode and exitFullScreenMode properties set on the artifact’s data Object simultaneously.  If you do, the exitFullScreenMode property takes precedent.

Let’s look at a simple code-driven example:

private var artifact:UcompOSArtifactProxy;
 
private function start():void
{
 
 SDKClient.getInstance(this);
 UcompOSGlobalManagerProxy.getInstance().addEventListener
(UcompOSGlobalManagerProxy.SCREEN_MODE_CHANGE,screenModeChangeHandler);
 artifact = new UcompOSArtifactProxy();
 artifact.add(null,null,"http://www.server.com/enterFullScreenModeImage.png");
 artifact.data = {enterFullScreenMode:true};
 
}
 
private function screenModeChangeHandler(event:SDKEvent):void
{
 
 switch(event.data.state)
 {
 
 case "fullScreen":
 artifact.data = {exitFullScreenMode:true};
 artifact.setImage(null,"http://www.server.com/exitFullScreenModeImage.png");
 break;
 case "normal":
 artifact.data = {enterFullScreenMode:true};
 artifact.setImage(null,"http://www.server.com/enterFullScreenModeImage.png");
 
 }
 
}

An important point to make about Full Screen Mode implementations, at this time, UcompOS HTML content cannot be displayed while in full-screen mode.  UcompOS HTML applications and sub-applications rely on an iframe implementation which is outside the visual scope of the Flash Player.

UcompOS HTML entities can still be interacted with via Proxy Components while in Full Screen Mode however.

The same applies to UcompOS AIR 2.0 content.  It cannot be in view while the in Full Screen Mode but it can be interacted with by Proxy Component-driven interactions.

Building Drag and Drop Experiences in the UcompOS RPF

The UcompOS RPF couldn’t rightfully be called a “Rich Productivity Framework” if it didn’t have a model to support drag and drop interactivity.  Drag and Drop behavior could perhaps be considered one of the key characteristics of an application that helped to define the term “Rich Internet Application” in the early 2000s.

This discussion doesn’t focus on building drag and drop interactivity within a single UcompOS application.  Obviously, you can implement whatever sort of drag and drop interactivity within a singular UcompOS HTML, Flash, Flex, or AIR application without restriction or consideration of UcompOS mechanics.

The real power in a UcompOS Rich Portal Application is in implementing inter-application drag and drop interactivity.

Dragging names from an address book application to an e-mail application, dragging images from a file manager to a text editor, highlighting multiple resources and dragging them to a widget to perform some operation on the items simultaneously – these are some of the experiences that can be created.

The mechanics of drag and drop UcompOS implementations are worthy of discussion.  There are some important considerations that need to be made in order for the UcompOS Portal to enable your applications to be configured appropriately for inter-application dragging and dropping.

Sandboxing in the UcompOS RPF

The philosophy of the UcompOS RPF is to achieve the highest degree of sandboxing possible while still achieving our interaction objectives.

Our sandboxing is less for security purposes and more to create an isolated, self-contained environment for a UcompOS entity that is free from unintentional disruption by other UcompOS entities.

Communication between applications in the UcompOS RPF should exclusively occur using the classes built into the SDK.

In no way should you plan to, or should you need to, attempt to directly access classes, properties, or methods of other UcompOS entities within an individual UcompOS entity. You should exclusively rely on the powerful implementation of a UcompOS entity’s public API methods, Services Dictionary, and Proxy Components.

The one exception to this is the implementation of dragging and dropping across two different UcompOS entities.  But the UcompOS Portal handles all the logic for you so that you don’t need to worry about anything other than what should be dragged and what should be allowed to be a drop target and how it should behave when receiving a drop.

The topic of sandboxing in the Flash Player and loading SWF content into other SWF content is a necessarily thorough and rigorous conversation.

In the UcompOS RPF, any SWF content – be it a UcompOS application or sub-application – is loaded into the UcompOS Portal with an implementation of the SWFLoader class.

The SWFLoader has some very important properties that essentially articulate the permissions the child SWF content has in terms of accessing information (classes, properties, methods) of the parent SWF content it was loaded into.

One of the chief goals of the UcompOS RPF is complete disaggregation.  The framework wants you to be able to place resources at any network address without consequence.

Therefore, all SWF content is loaded into the UcompOS Portal with the trustContent property set with a true value.

There are three different ways SWF content is loaded into the UcompOS Portal as listed below:

  • SWF-based UcompOS application loaded invisibly into the UcompOS run-time
  • SWF-based sub-application loaded into a UcompOS Window
  • SWF-based sub-application loaded into a UcompOS Artifact

When you load SWF content into the UcompOS Portal, you can articulate how tightly it is to be sandboxed.

As far as SWF-based UcompOS applications loading invisibly into the UcompOS run-time, you can set a sandbox=”true” or sandbox=”false” attribute to the root tag in that application’s manifest file.  The default condition is sandbox=”true”.

Also, if you look at the signatures of the add(); methods of both UcompOSWindowProxy and UcompOSArtifactProxy, you’ll see they both have sandbox parameters that default to a true value.

By default, a SWF loaded into a UcompOS Window or UcompOS Artifact will take on the sandbox configuration of its root UcompOS Application.

From an implementation point of view, the only difference between the two configurations is SWF content loaded with the sandbox=”true” configuration has the following property set to the SWFLoader used to load the content:

swfLoader.loadForCompatibility = true;

According to the Adobe’s ActionScript 3 documentation:

“Set this property to
true

to indicate that the loaded application might be compiled with a different version of the Flex framework and you want your application to be able to interact with it. Setting the value of the
loadForCompatibility

property to
true

also causes the LoaderContext of the Loader to load the sub-application into a sibling application domain of the main application, rather than a child application domain.”

A full discussion of the LoaderContext and ApplicationDomain classes is outside the scope of this blog posting, but essentially, setting the loadForCompatibility property to true is a form of sandboxing in that you are able to load Flex content created with a different version of the SDK than the parent SWF content.

Unless you have very specific reasons, in most all cases you’ll leave the sandbox property in its default configuration (sandbox=true).

Our plan is that we will always keep the UcompOS Portal running on the latest public release of Flex 4 so leaving sandbox=true will insure you won’t have to continue to update your Flex-based UcompOS content.

Scenarios for when you would want to set sandbox=false are complex and sophisticated and will be covered in an upcoming posting.

The Marshall Plan

The implications laid out above are somewhat important to elaborate upon.

We want to be able to load SWF content created with any version of the Flex SDK into the UcompOS Portal, and then we still want to be able to enable it to participate in drag and drop interactivity with other SWF content, that may have been produced with still other versions of the Flex SDK.

How is this possible?  After all, classes such as the DragManager are involved and certainly it seems we’ll run into compatibility issues.

The answer is taking advantage of a concept the people at Adobe have created that they call the Marshall Plan.  I recommend you explore this important topic.  It deals with how data is marshalled across the boundaries between two different SWFs.

As of the Beta 2 release of Flex 4, the classes that enable this marshalling to automatically occur are no longer automatically compiled into the SWF.  You have to explicitly add them at compilation time.

This must be added as an argument to the Flex compiler for your application:

-includes=mx.managers.systemClasses.MarshallingSupport

Failure to add this to a Flex 4 Beta 2 UcompOS application that is set up for dragging and dropping will yield unpredictable results, and even run-time errors.

Building a Simple Drag and Drop Example

If you have two different UcompOS entities – one the drag source, the other the drag destination – you would set each of them up as if they were both in the same overall application.

You’d set up the drag source to specify the behavior of the drag initiation, and your drag destination to implement your desired behavior of when items are dragged and dropped onto the target.

There is another scenario however and it involves dragging and dropping to a UcompOS Artifact.

In our example, we want to build a very simple example that shows a UcompOS Flex application that employs a DataGrid.  We then want to implement an artifact and enable the user to drag and drop an item from the DataGrid to the artifact.

When the item is dropped, we want to throw a simple HTML alert that outputs some information about the item that was just dropped.

Here are the three resources used to accomplish this task:

The Dock Manifest

<applications>
 <application background="true" default="true">
   http://applications.ucompass.com/SampleApps/UcompOSExamples/assets/manifests/DragAndDropExample.xml
 </application>
</applications>

The Application Manifest

<application selfLoading=”true” width=”206″ height=”250″ preloaderTitle=”Drag and Drop Example”>
 
 <source>
  <base>http://applications.ucompass.com/SampleApps/UcompOSExamples/bin-debug/DragAndDropExample.swf</base>
 </source>
 
 <titles>
  <title default=”true” locale=”en_US”>UcompOS Drag and Drop Example</title>
 </titles>
 
 <icons>
  <icon default=”true” locale=”en_US”>
   http://applications.ucompass.com/SampleApps/UcompOSExamples/assets/png/nature/Cloud-48×48.png
  </icon>
 </icons>
 
</application>

The Application Code

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
 xmlns:s="library://ns.adobe.com/flex/spark"
 xmlns:mx="library://ns.adobe.com/flex/halo"
 applicationComplete="start();" height="200" width="200">
 <fx:Script>
 <![CDATA[
 import com.ucompass.ucompos.sdk.SDKClient;
 import com.ucompass.ucompos.sdk.events.SDKEvent;
 import com.ucompass.ucompos.sdk.proxycomponents.UcompOSArtifactProxy;
 import com.ucompass.ucompos.sdk.proxycomponents.UcompOSHTMLProxy;
 
 import mx.collections.ArrayCollection;
 [Bindable]
 private var _people:ArrayCollection = new ArrayCollection([
 {Name:"Ed",Age:36},
 {Name:"Max",Age:2},
 {Name:"Mason",Age:1},
 {Name:"Abigail",Age:1}]);
 
 private function start():void
 {
 SDKClient.getInstance(this);
 var artifact:UcompOSArtifactProxy = new UcompOSArtifactProxy();
 artifact.add("Drag Here!",null,
 "http://applications.ucompass.com/SampleApps/UcompOSExamples/assets/png/nature/Cloud-48x48.png",
 300,300);
 artifact.dragDropManager(true,"items",["Name","Age"]);
 artifact.addEventListener(UcompOSArtifactProxy.DRAG_DROP,dragDropHandler);
 }
 
 private function dragDropHandler(event:SDKEvent):void
 {
 var droppedItems:Array = event.data.dragDropData;
 var droppedItem:Object = event.data.dragDropData[0];
 var h:UcompOSHTMLProxy = new UcompOSHTMLProxy();
 h.alert("Your name is "+droppedItem.Name+" and you are "+droppedItem.Age+".");
 }
 
 ]]>
 </fx:Script>
 <mx:DataGrid width="200" height="200" dataProvider="{_people}" dragEnabled="true">
 <mx:columns>
 <mx:DataGridColumn dataField="Name"/>
 <mx:DataGridColumn dataField="Age"/>
 </mx:columns>
 </mx:DataGrid>
</s:Application>

Video Demo

Drag and Drop Example

The Mechanics of UcompOS Desktop (AIR 2.0) Applications

One of the more important and high-impact features of the UcompOS Rich Productivity Framework is the ability for UcompOS Rich Portal Applications to extend to the desktop.

The technology leveraged to accomplish this is Adobe AIR 2.0.

When you consider the presence of AIR 2.0′s new NativeProcess class, which enables an AIR application to target the native Operating System’s base functionality, the prospect of building industrial-strength applications that place very few limits on the developer and allow their imaginations to extend to new heights becomes very attainable.

In this post I want to talk a bit about the mechanics of AIR applications in the UcompOS RPF.  Understanding these mechanics can help you conceptualize and then deploy the most compelling rich experiences for your UcompOS Rich Portal Application.

The first thing to understand is that in the context of the UcompOS RPF, any AIR application will simply be another UcompOS entity.

A UcompOS entity represents any application or sub-application running in a UcompOS implementation.  The UcompOS Portal itself is a UcompOS entity.  All the UcompOS entities running at one time in the UcompOS RPF comprise what I refer to as the UcompOS Continuum as you know if you’ve been reading my blog postings.

There are a few different ways to launch a UcompOS AIR entity – as an application from the UcompOS Application Dock, using the UcompOSAIRProxy class, or by creating a UcompOS Artifact the user can click on to launch the AIR application.

First, let’s understand the mechanics at work here.  The UcompOS Portal is obviously a Flash-based browser entity.  In order for a browser-based Flash application to launch an AIR application, a couple things need to happen.

First, the author of the AIR application has to articulate that the application is allowed to be launched by the browser.

This is very easy to accomplish.

You simply need to have the following element as a direct child of the root element in the application’s manifest descriptor file.

<allowBrowserInvocation>true</allowBrowserInvocation>

The manifest descriptor file is NOT the UcompOS application manifest for the application.  It is the XML file that articulates the base-level behaviors and properties of an AIR application.

If you try to compile an AIR application that instantiates the UcompOS AIR SDK that does not have the above provision made, you’ll get a compilation error.

Does this mean that the only way your UcompOS AIR applications should be launched is by the browser via the UcompOS Portal?

Not necessarily.  There are ways you can set it up so that when your UcompOS AIR application is launched directly by the end-user, it opens the UcompOS Portal to a precise configuration.

It can even be configured so that your AIR application can save custom file types on the user’s desktop that when double-clicked upon can launch the AIR application which in turn launches the UcompOS Portal and then feeds one or more applications with the data contained in the desktop file.

These are a bit more advanced topics that I’ll definitely devote coverage to these capabilities sometime in the next month or so.

The next thing we need to understand is that the browser can only launch an AIR application following a user-triggered mouse click event.

When you configure an AIR application as the base source for a UcompOS Application in that application’s UcompOS Application Manifest and have the application show up on the UcompOS Application Dock, the UcompOS Portal is configured such that the clicking of the application icon on the dock is the click that is used to immediately launch the AIR application.

When you configure the Application manifest that will use an AIR application as its base source code, there are some parameters you need to incorporate into the manifest.

Below is a sample application manifest from a UcompOS AIR application that provides an example of this:

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

Note that you must at least include the appId and publisherId parameters.

There are a couple other ways to launch a UcompOS AIR application as a sub-application.

The first is with the UcompOSAIRProxy class which is built into the UcompOS SDK.

The UcompOSAIRProxy Class

The UcompOSAIRProxy class deserves some attention in this posting.  It is a Proxy Component that is used to create a virtual representation of a UcompOS AIR application in a UcompOS entity.  Inspecting the class is a good way to learn more about the mechanics of UcompOS AIR Applications.

Let’s look at the signature of the UcompOSAIRProxy‘s constructor:

public function UcompOSAIRProxy(appSource:String, appId:String,
      publisherId:String, airVersion:String, host:String="127.0.0.1", 
      operationPort:uint=0, validationPort:uint=0)

Let me touch on each of the parameters of the UcompOSAIRProxy‘s constructor:

  • appSource: This is the URL of the .air package file associated with the AIR application
  • appId: This is the application id of the AIR file as it exists in the AIR application’s descriptor file
  • publisherId: This is the value of the publisherid that is found in the file at $APP_INSTALL_DIR/Contents/Resources/META-INF/AIR/publisherid - WARNING: the publisherid will be deprecated in AIR 2.0 soon and I will then remove it as a required parameter in the UcompOSAIRProxy’s constructor at that time
  • airVersion: This is the minimum version of AIR to run the application.  AIR 2.0 is the UcompOS minimum requirement.
  • host: UcompOS AIR Applications rely on the ServerSocket class to communicate with other UcompOS entities.  By default, the UcompOS AIR application binds to 127.0.0.1 on the local machine and other UcompOS entities target that destination in socket transmissions.  However, you can override this and bind to some public facing IP address.  (NOTE: The implications here are remotely deployed UcompOS AIR Applications)
  • operationPort and validationPort: UcompOS AIR Applications bind to two ports – one for validation purposes and the other for actual UcompOS data transmissions.  By default, the UcompOS SDK will start with port number 1024, and climb upward using the first two openly available ports for operation and validation.  These port numbers can be explicitly articulated.

A UcompOSAIRProxy class has three specific types of SDKEvent instances associated with it:

  • UcompOSAIRProxy.AIR_APPLICATION_LAUNCHED: Dispatched when the AIR application has launched
  • UcompOSAIRProxy.AIR_APPLICATION_QUIT: Dispatched when the AIR application has quit
  • UcompOSAIRProxy.AIR_APPLICATION_STATUS: Dispatched following a call to the UcompOSAIRProxy instance’s getStatus(); method

An instance of UcompOSAIRProxy also has a getter/setter on the Boolean property status which returns true when the AIR application is running.

The UcompOSAIRProxy class has two methods key to our discussion: launchApplication(); and quitApplication();

You could use launchApplication(); to launch a UcompOS AIR Application as a sub-application.

When you do, however, the UcompOS Portal must find a way to meet the compulsory user-triggered mouse-click requirement mentioned above.

It does this by throwing an Alert onto the interface of the UcompOS Portal asking the user if they agree to allow the AIR application to launch and then by clicking “OK”, the user is providing the necessary mouse-click.

The full signature of the launchApplication(); method is as follows:

public function launchApplication(message:String=null):void

The message parameter allows you to display a custom message on the Alert used in this scenario.

There is an alternative way to launch a UcompOS AIR sub-application that I’ll cover next.

Using a UcompOS Artifact to launch an AIR Application

By now, you know that the UcompOSArtifactProxy class in the UcompOS SDK provides a way to implement the equivalent of “desktop widgets” on the UcompOS Portal interface.  I prefer the term “Artifact” over “Widget” however as I feel widget has functionality implied, whereas a UcompOS Artifact can be functional and can be a full-fledged UcompOS sub-application or it can be strictly presentational.

As is the case with all Proxy Components (i.e. any class that extends AbstractProxyComponent), UcompOSArtifactProxy instances have a data property.

When you set the data property of a UcompOSArtifactProxy instance, this data property is housed on the actual artifact itself on the UcompOS Portal.

There are two special properties that can be added to this data Object: launchAIRApplication and quitAIRApplication

The values of these properties would be the uuid property of the UcompOSAIRProxy instance that was to be launched upon a mouse click of the artifact involved.

So the following code may be something you’d see in practical operation:

var myAIRAppProxy:UcompOSAIRProxy = new UcompOSAIRProxy("http://apps.mysite.com/myApp.air","My-App","My-Publisher-Id","2.0");
var myArtifactProxy:UcompOSArtifactProxy = new UcompOSArtifactProxy();
myArtifactProxy.add("Label for my Artifact",imageContent);
myArtifactProxy.data = {launchAIRApplication:myAIRAppProxy.uuid};

In the above example, as soon as the artifact is clicked on, the AIR application will launch.

You could also do something like the following too:

private function launchHandler(event:SDKEvent):void
{
 switch(event.type)
 {
 case UcompOSAIRProxy.AIR_APPLICATION_LAUNCHED:
  myArtifactProxy.setImage(imageContentAppOn);
  myArtifactProxy.data = {quitAIRApplication:myAIRAppProxy.uuid};
 break;
 case UcompOSAIRProxy.AIR_APPLICATION_QUIT:
  myArtifactProxy.setImage(imageContentAppOff);
  myArtifactProxy.data = {quitAIRApplication:myAIRAppProxy.uuid};
 break;
 }
}

In the above example, we are listening to see when the AIR application is launched as well as when it quits.  We then replace the artifact image with a status indicator using the UcompOSArtifactProxy‘s setImage(); method so it easily shows the user if the associated AIR application is running or not.

As an example of how I am using this concept myself, in the Educator 2 Rich Portal Application I am building entirely on top of the UcompOS RPF, on the taskbar (an artifact container with a number of artifacts housed in it), there is an icon to launch the “Educator 2 Desktop”, which is obviously an AIR application.

When they click this icon, the Educator 2 Desktop immediately launches and the icon turns green indicating the feature is connected.  When they click it again, the Educator 2 Desktop quits and the icon turns red indicating the feature is disconnected.

A Simple UcompOS Shared Object Impementation Example

The new Shared Object implementation in the UcompOS Rich Productivity Framework provides an additional model for achieving inter-application and sub-application communication and data exchange in a manner that is grounded in the MVC (Model-View-Controller) Design Pattern.

The term Shared Object in the context of the UcompOS RPF is similar in principle to the concept of the Local Shared Object infrastructure that has been in the Flash Player since the days of Flash 4 but in our case, the UcompOS RPF Shared Object is a run-time Object that does not get saved locally (unless you of course configure your own implementation to do so).

In the UcompOS Portal is an Object that gets instantiated into the application’s run-time memory upon authentication.

This Object is then freely available for all UcompOS entities to access using the GlobalManager.setSharedObject, GlobalManager.getSharedObject, and GlobalManager.getSharedObjects public API methods sponsored by the UcompOS Portal.

The UcompOSGlobalManagerProxy class in the UcompOS SDK has an API to these methods in the public methods of the class as indicated  below:

public function setSharedObject(key:String, value:Object):void
public function getSharedObject(key:String):void
public function getSharedObjects(keys:Array):void

One of the more important aspects of the Shared Object implementation is that the UcompOS Portal dispatches a Continuum Event of type SDKEvent.SHARED_OBJECT whenever a property of the Shared Object changes.

A Continuum Event is an event dispatched to every entity in the UcompOS Continuum.

An entity can attach a listener to the UcompOSGlobalManagerProxy Singleton listening for SDKEvent.SHARED_OBJECT events.

The event Object will have a key and a value property representing the property on the Shared Object that was just modified and its associated value.

As a very simple example of what we can do with this, I set up a basic UcompOS implementation.

The implementation involves a UcompOS background application that launches a UcompOS Window with a text area.  Upon initialization, this application launches another UcompOS Window with a sub-application in it next to the initial window  This sub-application also has a text area.

When text is entered in the primary application window’s text area, the text is instantly displayed in the sub-application window’s text area.

The mechanics for this employ the UcompOS Shared Object.

I’ll share the code below so you can get an understanding of how it was configured:

The Dock Manifest

<applications>
 <application background="true">
  http://desktop.ucompass.com/Shared_Object/manifest.xml
 </application>
</applications>

The Application Manifest

<application selfLoading="true" width="500" height="500" x="0" y="100">
 <source>
 <base>http://desktop.ucompass.com/Shared_Object/windowA.html</base>
 </source>
 <titles>
 <title locale="en_US">Shared Object Example</title>
 </titles>
</application>

The Application Code

<html>
<head>
<title>Window A</title>
<script type="text/javascript" src="/UcompOSSDK.js"></script>
<script type="text/javascript">
 
 var g;
 var w;
 
 function start()
 {
  g = new UcompOSGlobalManagerProxy();
  w = new UcompOSWindowProxy();
  w.add("http://desktop.ucompass.com/Shared_Object/windowB.html","Echo",500,500,510,100);
 }
 
 function echo()
 {
  // pause for 1/100th of a second as the onkeypress event is dispatched before the textarea is updated
  setTimeout(function() { g.setSharedObject("echo",document.getElementById('text').value); },10);
 }
 
</script>
</head>
<body>
<u>Enter some text</u><br/>
<textarea id="text" style="width: 400px; height: 400px;" onkeypress="echo();"></textarea>
</body>
</html>

The Sub-Application Code

 
<html>
<head>
<title>Window B</title>
<script type="text/javascript" src="/UcompOSSDK.js"></script>
<script type="text/javascript">
 
 var g;
 var w;
 
 function start()
 {
  g = new UcompOSGlobalManagerProxy();
  g.addEventListener(g.SHARED_OBJECT,echo);
 }
 
 function echo(data)
 {
  switch(data.key)
  {
   case "echo":
    document.getElementById('text').value = data.value;
   break;
  }
 }
 
</script>
</head>
<body>
<u>What you're saying in the other window</u><br/>
<textarea id="text" style="width: 400px; height: 400px;"></textarea>
</body>
</html>

UcompOS SDK Communication Mechanics

I want to spend some time looking a bit deeper under the hood of the UcompOS SDK particularly in terms of how the SDK is configured to enable entities to talk to one another.

Understanding these mechanics can help you understand how to build the best Proxy Components that will promote the richest experiences for your UcompOS Rich Portal Application.

As you’ve read about in my previous blog postings, the Flash Player’s LocalConnection infrastructure is the medium used for inter-entity communication in the UcompOS Rich Productivity Framework.

A slight exception to this is with UcompOS AIR 2.0 applications that take advantage of AIR 2.0′s new ServerSocket class such that messages are sent to the AIR application over a socket versus LocalConnection.

My reasons for using LocalConnection despite its known limitations have to do with the fact that LocalConnection is such a practical lowest common denominator solution and is equally available to all versions of ActionScript.

Limitations of the LocalConnection include the inability to send data packets in greater than 40K chunks. The SDK elegantly works around this limitation by implementing its own incremental transmission and aggregation logic.

For the most part, the messages sent between entities don’t ever come anywhere near 40K but are closer to 1K. In some use cases, larger data chunks may need to be sent over the UcompOS “SDK Tunnel”. For instance, in Educator 2, I am building a whiteboard application and content drawn in the application often needs to be transmitted elsewhere as a ByteArray encoded PNG.

Messages sent back and forth in the UcompOS Continuum via the SDK are placed into an instance of a class called DataPackage. The DataPackage itself will receive a polymorphic construct of type IDataPackageObject as one of its input parameters.

(REMINDER: There is AS3 documentation for the SDK)

Currently, the classes in the SDK that implement IDataPackageObject are CommandObject and EventObject.

With this knowledge we can talk a bit more about the mechanics of data transmission.

As I’ve explained, a UcompOS entity serves both as a server and a client in the context of the UcompOS Continuum.

The “touch points” of a UcompOS entity are the classes SDKClient and SDKServer. SDKClient is of course also the class that is instantiated when a UcompOS application starts up.

The SDKClient‘s send(); method receives a DataPackage as input. In addition to the DataPackage having the polymorphic IDataPackageObject instance embedded, it also has information about the destination entity as well as the origin entity. The send(); method then uses the LocalConnection infrastructure, or an extended Socket class, to send the DataPackage to the remote entity (encoded into a compressed ByteArray).

The SDKServer then serves as the server infrastructure in a UcompOS entity.

The SDKServer‘s server(); method is the method targeted by the SDKClient‘s send(); method.

This method decodes the compressed ByteArray, serializes it into a DataPackage, and then determines what type of IDataPackageObject instance is in it.

It then passes the DataPackage to a polymorphic construct of type IServerHandler.

Right now, as you’d expect, there are two classes in the SDK that implement IServerHandler: CommandObjectHandler and EventObjectHandler.

Communication in the UcompOS RPF is both synchronous and asynchronous.

The transmission of a CommandObject from one entity to another results in the receiving entity generating an EventObject and passing it back to the sending entity in real-time. In that regard it is a synchronous communication process.

From an implementation point of view, the EventObjectHandler class causes the Proxy Component that made the original call in the calling entity to dispatch an event of type SDKEvent and this event object will have a data property that represents the data returned by the method contacted in the target entity.

The API method in the target entity must by strict convention return an item of type Object. This Object can have an eventType property which will be the event type of the SDKEvent dispatched by the calling Proxy Component via the EventObjectHandler class.

Typically in a UcompOS application, one would attach an event listener to a Proxy Component instance for the event type they knew would be returned by the target remote API method.

Thus the model takes on more of an asynchronous flavor even though you are able to take action on the return of data from a target entity in real-time.

There is a way to implement more of a callback type synchronous implementation.

The AbstractProxyComponent‘s call(); method returns a String that is used as the Universally Unique ID of the Transmission. All Proxy Components by strict convention must extend AbstractProxyComponent.

The EventObject that is returned from the target entity has a transactionId property that is the value of the String returned by the call(); method of AbstractProxyComponent.

One can easily facilitate a true callback strategy armed with this knowledge.

Develop Against the Latest UcompOS Build Without Needing to Download the Build

Each time I make a change of substance to the UcompOS Rich Productivity 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 Productivity 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.

Some Tips and Tricks for Working with the UCompOS Framework

As I’ve experimented with some various aspects of the UCompOS framework, I have picked up on a couple of subtle nuances that I thought I’d share.

First, if you haven’t yet switched to the Flash Builder 4 Beta from Flex Builder 3, doing so is probably a good idea.  One key reason (beyond enhanced functionality) is that I have noticed a significant reduction in the sizes of exported Flash Builder 4 swf files.  For example, one artifact I had created in Flex Builder 3 was about 300 k when I exported it, but this was reduced to about 70 k once I ported it to Flash Builder.  I was surprised at the huge difference.

Speaking of Artifacts, after talking with Edward Mansouri at some length about some of the file size issues I mentioned above, and his recommendation is to use Flash CS 4 or 5 (if you’re lucky enough to be in the private beta program) for artifact development wherever possible.  Flash can get your file sizes down to roughly a tenth of what I can get them down to in Flash Builder, which is a big deal if you have a lot of artifacts in your implementation.

I want to move on a little bit and talk about developing applications using the AIR 2.0 SDK, which is the only way to take advantage of some of the AIR functionality in the UcompOS framework.  The AIR 2.0 SDK and runtime can both be downloaded from http://labs.adobe.com/downloads/air2.html (you will need both).  Once you have downloaded it, go ahead and install the runtime (which installs automatically).  However, you will need to need to do some tweaking to Flash Builder in order to install the SDK properly (I will only cover Flash Builder in this blog post).  What you need to do is find the “sdks” folder in your Flash Builder folder and open it.  It should look something like this:

pic1

What you are going to want to do next is make a copy of the 4.0.0 folder.  In my case I copied it and named it “4.0.0 AIR 2.0″

pic2

This will be the folder where we copy the files from the AIR 2.0 SDK into.  Simply open the ZIP file containing the AIR 2.0 SDK and copy and paste the files into the 4.0.0 AIR 2.0 folder.  If you are prompted, be sure to overlay any old files with the new ones from the ZIP file, or you will run into problems.

pic3

Now, you will need to configure Flash Builder to use the new SDK for applications.  To do this, open up Flash Builder, and create a new Flex project.  On the dialog that comes up, select the “Configure Flex SDKs” on the middle right of the dialog:

pic4

On the dialog that comes up, click the “Add” button, and browse to the 4.0.0 AIR 2.0 folder you created earlier:

pic5

pic6

Then, select the “Ok” button, and your dialog should look like this:

pic7

Once you’re done, click the “Apply” button, and then click “Ok”.  Then, under the “Flex SDK version” part of the dialog, select the “Use a Specific SDK” radio button, and select your new SDK from the select box.  From here on out, setting up your project is normal.  Note that unless you made your new SDK the default, you will have to manually select your SDK every time you create a new application that leverages the AIR 2.0 SDK (including UcompOS applications).

pic8

The final topic I wanted to touch on was getting your publisherID that you need to add to your application descriptor file in order to make it work with UCompOS.  The easiest way to do this is to export a release build of your AIR application and install it.  Once that’s done, you will need to browse to the location where you installed the AIR application, and browse to the “META-INF/AIR” folder.  In that folder, you will see a file called “publisherid” that contains the value that you need to set for publisherID in your AIR application descriptor file (don’t forget to set allowBrowserInvocation to true!):

pic9

I hope some of these tips help you out as you work with UcompOS.