Meet the UcompOS Portal Proxy Components

If you’ve read my previous postings, you should now have a basic understanding of some of the core mechanics of the UcompOS Rich Productivity Framework including Proxy Components and Services Dictionaries.

The concept of the UcompOS Continuum should also be somewhat clearer now as well as the roles of the UcompOS Portal and the UcompOS SDK in the context of the UcompOS Continuum.

Despite the obvious significance of the UcompOS Portal, in the context of the UcompOS Continuum, it is just another entity with the UcompOS SDK implemented that publishes a Services Dictionary of public API methods exposed for other entities.

To take advantage of this fact, the UcompOS SDK has a host of Proxy Components built into it that can be thought of as client interfaces to the public API methods exposed by the UcompOS Portal.

Understanding the mechanics of building your own UcompOS public API methods and Proxy Components to those public API methods is the key to unleashing the true power of the UcompOS RPF and creating the most compelling rich experiences for users of your Rich Portal Application.

But even a basic exploration of the SDK’s built-in UcompOS Portal Proxy Components is a fun way to explore and learn.

In this post, I’ll introduce you briefly to the UcompOS Portal Proxy Components.  I won’t spend a lot of time detailing them as you’ll likely see some dedicated blog postings from me covering the individual Proxy Components and their nuances in the coming weeks.

Each Proxy Component covered is built into all 3 UcompOS SDKs – AIR, Flex/Flash, and JavaScript.

The implementation details of using the AIR and Flash/Flex (ActionScript 3.0) Proxy Components versus the JavaScript Proxy Components are very subtle and of course full SDK documentation is online for the ActionScript 3.0 SDK as well as the JavaScript SDK.

Since the SDK documentation is online, I won’t spend a lot of time covering the methods, properties, and events of the Proxy Components (as you can get all this information in the docs), rather I’ll just give a first-person synopsis of what you can do with each of the Proxy Components.

UcompOSAIRProxy

The UcompOSAIRProxy class allows you to create a virtual representation of a UcompOS AIR application in an entity.  Once you create this virtual representation, you then use this class’s launchApplication(); and quitApplication(); method to control the start and stop of the AIR application.  Then any communication with the AIR application is handled via the traditional means of Proxy Components and public API methods.  The UcompOSAIRProxy class would be used when you wanted to launch a UcompOS AIR application as a sub-application.  If the UcompOS AIR application was to be a base application, you would just have the network URL to the air package in the application manifest and the AIR application would be launchable from the UcompOS application dock.  See my tutorial entitled A Simple HTML Digital Camera Browser for an example of this.

UcompOSArtifactContainerProxy

The UcompOSArtifactContainerProxy class lets you create a container for UcompOS Artifacts on the UcompOS Portal.  A UcompOS Artifact is essentially a “widget” that can be an image or SWF application that can have functionality attached to it (drag and drop, double click, click, rollover, context menu, etc.).

A UcompOS Artifact Container then is a visual layout container for these artifacts.  I gave an example of this in my tutorial entitled Building a UcompOS Weather Channel Widget.

An example may be a taskbar.  Suppose I want to layout a number of widgets each with its own specific functionality attached to it along the bottom right corner of my UcompOS Portal interface.

To do this I could create a UcompOS Artifact Container via the UcompOSArtifactContainerProxy class.  Then when I create my UcompOS Artifact, I would pass a reference to my UcompOSArtifactContainerProxy instance and the artifact would be placed into the relevant container.

There are 3 layout options at present with the UcompOS Artifact Container: HBox, VBox, and Tile

These are parallel to the classes of the same name in the mx.containers package from the Flex 3 framework (FYI the UcompOS Portal is built on Flex 4).

UcompOSArtifactProxy

The UcompOSArtifactProxy is a class used to create UcompOS Artifacts on the UcompOS Portal.

A UcompOS Artifact can be thought of as a widget.  The widget can be purely presentational, or it can have functionality attached to it as my Weather Channel Widget example demonstrated.

The visual content of a UcompOS Artifact can articulated as one of three forms:

  1. The network URL to an image (PNG, GIF, JPG)
  2. The network URL to a SWF appication
  3. A ByteArray representation of an image

You then attach functionality to a UcompOS Artifact by calling the UcompOSArtifactProxy‘s many public methods and attaching various event listeners to it to handle click, double click, rollover, rollout, drag and drop, and other various events.

If the visual content of your artifact is a SWF, this SWF can be a UcompOS sub-application for even more exciting interactive possibilities.

At this point, HTML-type applications cannot be used as artifacts

As described above in my discussion of the UcompOSArtifactContainerProxy class, you can add a UcompOS Artifact to a container so that groups of similar artifacts can be arranged together in a logical presentation.

One cool thing you can do, if you don’t add an artifact to a container and just position it at specified coordinates on the UcompOS Portal, you can make it draggable.  And because you can configure artifacts to accept drops, the ability to drag and drop one artifact to another to create some desired behavior or functionality exists.

I’ll likely devote a tutorial to this interesting capability in the near future.

UcompOSBrowserWindowProxy

The UcompOSBrowserWindowProxy class lets you launch a satellite browser window from the UcompOS Portal.  You can add generic content to the browser window, or you can load a UcompOS sub-application.

The ability to communicate with a UcompOS sub-application loaded in a satellite browser window is the exact same as if the sub-application was loaded in the UcompOS Portal run-time, in a UcompOS Window, or in a UcompOS Artifact.

UcompOSDockProxy

The UcompOSDockProxy class lets you exert control over the UcompOS Portal’s Application Dock.

You can do things such as set context menus to the application icon an entity is associated with.  You can also change the size of the icons on the dock as well as control the “genie” effect (the effect where the dock icons expand and then contract as the mouse passes over them).

You can also hide the dock completely using the showDock(visible:Boolean):void; method.

And one of the more important capabilities is you can set an alert for the icon on the dock an entity is associated with using the setAlert(); method.  This would be useful perhaps in e-mail or instant messaging applications to alert the user of receipt of a new e-mail or instant message.

UcompOSGlobalManagerProxy

The UcompOSGlobalManagerProxy class provides an interface to a number of important visual and data features  of the UcompOS Portal.

For instance, if you wanted the UcompOS Portal to throw a generic Flex-style Alert, you could use the createAlert(); method.

If you wanted to dispatch an event to all entities in the UcompOS Continuum, you could use the dispatchContinuumEvent(); method.

Suppose you wanted to launch another UcompOS application from within an entity.  You could call the launchApplication(); method and pass the manifest URL of the target application’s application manifest.

These are only a few of the important duties the UcompOSGlobalManagerProxy facilitates.

UcompOSHTMLProxy

The UcompOS Portal is a Flex 4 application embedded in an HTML wrapper.

The UcompOSHTMLProxy class lets you call JavaScript functions in the HTML wrapper.

The class provides the alert();, confirm(); and prompt(); methods which invoke the JavaScript functionality of the same name.

You can then add event listeners such as UcompOSHTMLProxy.CONFIRM_SUBMIT and UcompOSHTMLProxy.PROMPT_SUBMIT to capture user-input.

You can also use the javascript(); method to pass pure JavaScript code.

So one possibility here, in the ucompos/local/LocalLib.js JavaScript file in the UcompOS Developers Package that you customize to create your own authentication, you can implement your own JavaScript methods that perform specific tasks and then access these methods from the UcompOSHTMLProxy.

UcompOSMenuBarProxy

The UcompOSMenuBarProxy lets you set the dataProvider for the UcompOS Menu Bar and your particular menu bar implementation comes into view when the UcompOS application associated with the entity that set the menu bar model comes into focus.

You can even associated a menu bar model with an individual UcompOS Window instance.

You then attach event listeners to capture user menu selections.

Two cool random quick points is that you can pass a baseMenu=true value to the second parameter of the setMenuBar(); method and it will remove the default UcompOS menu bar item so you can 100% customize the menu.  Also, in your dataProvider to the menu you can attach an icon property with the network URL of images you want to use for the individual menu bar nodes.

UcompOSStyleProxy

The UcompOSStyleProxy class lets you take control over the portal’s visual presentation including the background image (wallpaper) that appears on the UcompOS portal.

With the setTheme(); method, you can set the MDI window theme to one of 3 different pre-existing window themes.  Right now, I just have the 3 that come packaged with the flexlib.mdi package implemented (Windows XP, Mac OS 9, and the default style) but it is my intention to expand this.

There is also a setEffects(); method that let you control the visual effects that occur when UcompOS Windows are closed or minimized/maximized.  The options are Default, Linear, and Vista which are the effects built into the flexlib.mdi package.

There is also a generic setStyle(); method that lets you set a generic CSS style property to the UcompOS Portal.

One important point about this method.  When you call setStyle();, the UcompOS Portal dispatches a UcompOS Continuum Event – meaning that all entities get notified about the style change.

So if you did something like this:

UcompOSStyleProxy.getInstance().setStyle("Button","color",0xFF0000);

Every entity would be notified that the Button selector’s color property was just given a red value on the UcompOS Portal.

You could then listen for this UcompOSStyleProxy.STYLE_CHANGE event and change the visual presentation of an entity’s internal controls accordingly.

UcompOSWindowProxy

The UcompOSWindowProxy class is used to create MDI window instances on the UcompOS Portal and then load generic web content or a UcompOS sub-application into them.

The class documentation is definitely worth a look as there are a lot of really cool features you can employ with UcompOS Window instances.

So that’s it.  A brief look at all the UcompOS Portal Proxy Components built into the UcompOS SDK.

I invite you to view the source code of the the JavaScript SDK and you can check the UcompOS Flash/Flex SDK written in ActionScript 3 out of the UcompOS SVN Repository.  Viewing this code will give you some better ideas of how to construct Proxy Components.

I have not yet made the source code of the UcompOS Portal publicly viewable and do not have a timeline for doing so but if you have questions about how I have built the public API methods on the UcompOS Portal, please bring questions to me in the UcompOS Forum and I’ll share relevant strategies and code examples.

The UcompOS Event Propagation Model

The other day, I wrote a blog posting describing how the UcompOS RPF implements the MVC design pattern.

This implies that there is an event-driven architecture that has been facilitated in the UcompOS Continuum and that is indeed the case.

In this blog posting, we have to roll our sleeves up and really dig into the UcompOS SDK so that I can help you understand how the UcompOS RPF Event Architecture works.  If you have full command of the UcompOS RPF’s event architecture, it will help you build the most engaging and interactive rich experiences for your Rich Portal Applications.

The architecture at first glance is necessarily complex and involved and this will probably likely be just one of many blog postings I’ll make in reference to this topic and as time passes, I am going to work hard to make the event architecture simpler, more transparent, and as close as possible to best practice.  Of course developer feedback will be a crucial part of that mission.

CommandObject Transactions

If you’ve read my other postings, you have a very basic understanding of Proxy Components and Services Dictionaries.

Simply put, a UcompOS entity exposes its internal functionality to other UcompOS entities by publishing a Services Dictionary and a UcompOS entity contacts functionality in another entity by using a Proxy Component.

By strict convention, ALL communication between entities in the UcompOS Continuum should occur via the SDK class SDKClient and its send(); method.  (NOTE: There is a very esoteric exception to this that is handled behind the scenes by the UcompOS RPF that would involve two UcompOS entities participating in drag and drop interactions but that is beyond the scope of my article here and will be covered in an upcoming tutorial.)

The UcompOS SDK has both client and server infrastructure implemented.  Therefore, each UcompOS entity is capable of serving both as a server capable of receiving commands from other entities as well as a client capable of sending commands to other entities simultaneously.

The UcompOS RPF implements an asynchronous event dispatchment model.

When an entity needs to access a public API command in another entity, it (usually with the assistance of a Proxy Component) creates a CommandObject instance that is packaged into a DataPackage instance and sends it to the target entity via the SDKClient send(); method.  All these details are masked by the call(); method of AbstractProxyComponent which all Proxy Components must inherit from.

The server(); method of the SDKServer class is the first spot in the target entity to come in contact with the DataPackage.

DataPackages sent across the SDK Tunnel are encoded into compressed ByteArrays.

The server implementation in the SDK decodes the DataPackage and passes it to one of a couple different polymorphic constructs in the SDK:  CommandObjectHandler or EventObjectHandler

In the scenario where one entity is contacting the public API method of another entity, the DataPackage will contain a CommandObject and thus the DataPackage will be passed to the CommandObjectHandler in the SDK.

This will pass the data Object to the internal implementation code in the target entity.

By strict convention, any method that is capable of being targeted by another entity must accept exactly one parameter of type Object and return an item of type Object.

In order to understand the full event cycle in the UcompOS RPF, we must study what happens when this public API method returns this Object.  Where does this Object go from here?  How does it get back to the calling entity?

CommandObjectHandler passed the DataPackage to the internal implementation method and is returned the Object return from the method.

At this point, CommandObjectHandler turns from a server implementation to a client implementation and it creates a new DataPackage, puts an EventObject instance into it, and then sends the DataPackage back to the original entity.  So the original entity then becomes a server.

When the DataPackage with the EventObject packaged into it gets back to the SDKServer implementation in the calling entity, it is passed to the EventObjectHandler class.

The EventObjectHandler class then passes it to any event handlers that have been attached to the Proxy Component that made the initial public API call in the first place.

This is in fact why AbstractProxyComponent, which all Proxy Components extend from, inherits from EventDispatcher – so we can implement addEventListener(); thus implementing a paradigm many Flex and Flash coders are much more accustomed to.

When a public API method returns its required Object, an eventType property can be set to it.  If you do not explicitly set this, then CommandObjectHandler will set this for you as the constant SDKEvent.COMPLETE.

This eventType property is important as it has implications as far as how events get passed to a Proxy Component’s event handlers.

Suppose I have the following Services Dictionary class in a UcompOS application:

package
{
 
  import com.ucompass.ucompos.sdk.server.AbstractServicesDictionary;
 
  public class ServicesDictionary extends AbstractServicesDictionary
  {
    _map =
    {
      'Greeting.sayHello':
      {
 
        static:true,
        classRef:api,
        method:'sayHello',
        description:'Returns a greeting'
      }
 
    };
 
  }
 
}

Let’s suppose I have the following public API method that maps to Greeting.sayHello in my Services Dictionary example above:

public static function sayHello(data:Object):Object
{
  return {eventType:"Hello",greeting:"Hello there, "+data.name+"!"};
}

Now let’s suppose my Proxy Component located in another entity that will contact the Greeting.sayHello method above looks something like this:

package
{
 
  import com.ucompass.ucompos.sdk.client.AbstractProxyComponent;
  import com.ucompass.ucompos.sdk.model.SDKModel;
 
  public class Greeting extends AbstractProxyComponent
  {
 
    public function Greeting()
    {
      super._destination = SDKModel.getInstance().parent;
    }
 
    public function getGreeting(name:String):void
    {
      call("Greeting.sayHello",{name:name});
    }
 
  }
 
}
}

In my entity that will call Greeting.sayHello, I may see the following:

var g:Greeting = new Greeting();
g.getGreeting("Edward");

Now how do I get the greeting back from the entity that is exposing the public API method Greeting.sayHello?

Because AbstractProxyComponent extends EventDispatcher, and because my Greeting Proxy Component extends AbstractProxyComponent (a strict requirement of all Proxy Components), I can add an event listener to it.

g.addEventListener("Hello",greetingHandler);
 
private function greetingHandler(event:SDKEvent):void
{
  var greeting:String = event.data.greeting;
}

Notice my event handler is passed an instance of SDKEvent.  This is universal for all Proxy Components.

The SDKEvent instance will have a data property which will be the data Object returned by the remote public API method in the contacted entity.

Therefore, the eventType property in the return Object of a public API method determines which Proxy Component event handlers are passed an SDKEvent instance.

UcompOS Continuum Events

The above example shows a very simple example of an asynchronous request-response phase of a Proxy Component – public API method transaction.

There is another type of event in the UcompOS RPF called a Continuum Event.

The UcompOS Portal sponsors a public API method named GlobalManager.dispatchEvent.  This is accessed through the UcompOSGlobalManagerProxy Singleton class in the UcompOS SDK and its dispatchContinuumEvent(); method which accepts as input two parameters: type:String, and data:Object

When this method is accessed, every entity in the entire UcompOS Continuum is dispatched the event.

The implications of this is that you can monitor activity across the boundaries of different applications.  For instance, maybe you have a UcompOS Chat Application and when you receive an instant message from an individual user, you automatically want a separate UcompOS Address Book Application to open to that user’s Address Book entry.

Targeted EventObject Instances

The BroadcastEvent Singleton class in the UcompOS SDK has a dispatchEvent(); method.  This lets you send an EventObject on the fly to a single target entity.

For instance, the UcompOS SDK has a Proxy Component, that is the interface to various public API methods in the UcompOS Portal, called UcompOSWindowProxy.  The UcompOSWindowProxy‘s add(); method attaches an MDI window to the UcompOS Portal.

Internal to the UcompOS Portal implementation, it wants to notify the entity that created the UcompOS Window instance when certain user-driven activities occur such as when the window is moved, closed, minimized, maximized, etc.

Let’s look at a very simple example with some code.

Let’s suppose I have a UcompOS Application and I want to create a UcompOS Window, then I want to know when the user has closed the window.

In my application, the code I’d use to form the UcompOS Window could look like this:

var w:UcompOSWindowProxy = new UcompOSWindowProxy();
w.add("http://desktop.ucompass.com/MySubApp.swf","My Sub Application",500,500,100,100);

This will load the content at http://desktop.ucompass.com/MySubApp.swf into a UcompOS Window of dimensions 500 x 500 positioned at (100,100) from the upper-left origin point of the UcompOS Portal.

Now suppose I want to know when that window has closed.

No problem:

w.addEventListener(UcompOSWindowProxy.CLOSE,closeHandler);
private function closeHandler(event:SDKEvent):void
{
 // the window has closed
}

The reason why this works this way is that internal to the UcompOS Portal, there is a call to the UcompOS SDK static method BroadcastEvent.dispatchEvent(); that dispatches an EventObject (encapsulated in a DataPackage of course) to the entity that created the UcompOS Window.

As another example, consider the UcompOSMenuBarProxy class.  I haven’t covered the UcompOS Menu Bar in any tutorials quite yet, but the UcompOSMenuBarProxy class has a setMenuBar(); method that lets you attach an XML model that will be used as the dataProvider for the Menu Bar on the UcompOS Portal while the application in scope is in focus.

You could do something like this:

var menuModel:XML = new XML("<menu><menuitem label='File'><menuitem label='Open' data='1'/><menuitem label='Close' data='2'/></menuitem></menu>");
var m:UcompOSMenuBarProxy = UcompOSMenuBarProxy.getInstance();
m.setMenuBar(menuModel);
m.addEventListener(UcompOSMenuBarProxy.CHANGE,menuBarHandler);
 
private function menuBarHandler(event:SDKEvent):void
{
 var chooseString:String = "User chose "+event.data.label+" whose data property is "+event.data.data;
}

Summary

The event architecture in the UcompOS RPF is indeed complex at first glance.  There is no getting around that.  I am going to continue to work to simplify it, improve the documentation surrounding it, and get the implementation details as close as possible to a true best practice MVC implementation.

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:

database_table

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:

project

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:

roles

With all this in mind, the script that generates the UcompOS dock manifest file performs the following actions:

  1. Add all of the global applications in my applications table to the dock manifest
  2. Retrieve the list of which applications the user has access to, and add those applications to the dock manifest
  3. 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.

manifest

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 Productivity 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. flash_builder_project

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.

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

demo

Conclusion

In this tutorial, we created a deliberately simple application to demonstrate a number of core UcompOS RPF concepts and principles – particularly integrating the desktop into a UcompOS Rich Portal Application implementation.

The UcompOS RPF Implements the MVC Design Pattern

The Model-View-Controller (MVC) design pattern is a classic design pattern often used by applications that need the ability to maintain multiple views of the same data.

The internals of the UcompOS Portal application (which is a Flex 4 application) were created strictly following the MVC Design Pattern.

In addition, the overall UcompOS Continuum (I refer to the UcompOS Continuum as the UcompOS Portal and all the UcompOS applications and sub-applications it launches that work together collectively to create a Rich Experience) takes on an implementation style that closely resembles MVC.

When a UcompOS application is launched by the UcompOS Portal, the application is invisibly loaded into the UcompOS run-time.  It has no visual presentation.  You can launch a UcompOS application immediately into a UcompOS Window or UcompOS Browser Window by adding the selfLoading=’true’ attribute to the application’s root Application Manifest element (which will launch the content in a UcompOS Window) and the newWindow=’true’ attribute (which will launch the content in a UcompOS Browser Window).  Even in these scenarios though, a “helper” UcompOS Application is loaded into the UcompOS run-time which then is given the task of launching your application’s base content into a UcompOS Window or UcompOS Browser Window instance.  So technically, in these scenarios, your UcompOS base application code is actually a sub-application.

In a best practice implementation, you can think of the base source code of your overall UcompOS Application (that gets loaded invisibly into the UcompOS run-time) as serving as the Model and Controller for your UcompOS Application and the sub-applications that are launched into UcompOS Window, UcompOS Browser Window, or UcompOS Artifact instances as the View from an MVC implementation point of view.

From a terminology point of view, I have been internally referring to the content that is loaded invisibly into the run-time as the Portlet Manager, and the visual code launched into UcompOS Browser Windows, UcompOS Windows, or UcompOS Artifacts as the Portlets.  I have refrained from making this the official terminology however.  This project, necessarily, has a lot of jargon associated with it and I want to be very judicious and careful with the terms used to describe the various aspects and features of the framework.

In the implementations I have been building, I delegate any network responsibilities as far as retrieving data from a remote data source or updating a remote model to the Portlet Manager.  The Portlet Manager also implements listeners to the artifacts, windows, and browser window instances it spawns and launches and manages other Portlets based on user interactivity with said artifacts, windows, and browser window instances.

In a Portlet instance, there will obviously be functionality that involves manipulating or working with data presentations that correspond to  the Model in the overall application.  My standard practice is to dispatch any model changes to the Portlet Manager by using a Proxy Component class housed in the Portlet instance and a public API method exposed in the Portlet Manager’s Services Dictionary.

One thing I haven’t talked about in my postings yet is the Event Dispatchment architecture in the UcompOS RPF.  This topic is worthy of and will receive thorough coverage in the near future.

As a very generic foreshadowing to this, the AbstractProxyComponent class is in the UcompOS SDK and it is the class all Proxy Components must extend.

AbstractProxyComponent itself extends EventDispatcher and therefore, any Proxy Component has the addEventListener(); method available to it.

The event type you can register with a Proxy Component is of type SDKEvent which is also a class built into the UcompOS SDK.

As a very simple example, consider the following example code implemented in a UcompOS Application:

var w:UcompOSWindowProxy = new UcompOSWindowProxy();
w.add("http://www.google.com","Google",800,600,100,100);

This will open a UcompOS Window in the UcompOS Portal of dimensions 800 x 600 at 100,100 relative to the upper-left origin point.

Now, consider the following:

w.addEventListener(UcompOSWindowProxy.CLOSE,closeHandler);
private function closeHandler(event:SDKEvent):void
{
  // the window was closed!
}

I’ll devote my entire next posting to the SDKEvent class which is extremely important in terms of building highly interactive Rich Portal Applications with the UcompOS RPF and I’ll walk through the data flow and event propagation model involved with the simple example above.

A UcompOS Entity’s UUID

In my last two blog postings, I described Services Dictionaries and Proxy Components which together describe the implementation model of two disparate UcompOS RPF entities engaging in asynchronous communication with each other.  A Proxy Component is a client interface to the functionality in another entity exposed in that entity’s Services Dictionary.

As I described in my blog postings about UcompOS HTML Applications and UcompOS AIR 2.0 Applications, the Flash Player’s LocalConnection infrastructure is the basis of the medium used in the UcompOS Continuum for messages to pass from one entity to another while the AIR 2.0 ServerSocket class is the basis for inbound messages received by a UcompOS AIR 2.0 Application.

Each entity in the UcompOS Continuum that is capable of participating in transactions with other entities will have the UcompOS SDK implemented.

The UcompOS SDK has a Singleton class called SDKModel which has a public connectionId property.

This connectionId is a UUID (Universally Unique Identifier) in the context of the UcompOS Continuum.

Connection Ids are hierarchically structured, so by looking at the connectionId property of one entity, you can decipher the connectionIds of all the ancestors of that entity up to the root entity which is the UcompOS Portal.

Each time an entity launches another entity as  a UcompOS Application, UcompOS Window, UcompOS Artifact, or UcompOS Browser Window, that launched entity is given its connectionId by the launching entity and the mechanics of creating the connectionId is handled seamlessly by the UcompOS SDK.

Suppose the UcompOS Portal had a connectionId of A, then it launched an application.  This application may have a connectionId of A/B. Then suppose that application launched a sub-application in a UcompOS Window.  That sub-application may have a connectionId of A/B/C.  Now suppose that sub-application launched a sub-application in a UcompOS Artifact.  That sub-application may have a connectionId of A/B/C/D.  And so on….

In the SDKModel class, there is also a public property parent that is the connectionId of the entity that launched the entity in scope.

In addition, there is a root property which would be the connectionId of the root entity in the UcompOS Continuum which is the UcompOS Portal.

From an implementation details point of view, the connectionId is used by an entity as the connectionName property passed to the connect method of the LocalConnection instance that the entity uses to send and receive messages from other UcompOS entities.

The SDKClient Singleton class in the SDK has a public send(); method which handles the task of dispatching a DataPackage instance (also a class in the SDK) to a target entity via a LocalConnection target connectionId.

What we’ll learn soon when I start blogging about the UcompOSAIRProxy class is that UcompOS AIR applications have a string appended to their connectionId in the form of:

socket-host-validation port-operation port

For instance this may be a simplified example of a UcompOS AIR application’s connectionId:

A/B/C/D-socket-127-0-0-1-1024-1025

I’ll talk about the semantics of this in the near future and shed more light on the implementation details of an AIR 2.0 UcompOS entity.

The hyphens are used in the host address as periods are not permissible characters in the connectionName parameter passed to a LocalConnection instance’s connect method.

When the SDKClient‘s send(); method encounters the string “socket” followed by the host, validation port, and operation port in the syntax shown here, instead of sending the DataPackage to the AIR UcompOS entity via LocalConnection, it establishes a socket connection to the server socket endpoint broadcasting in the AIR application and sends the DataPackage over a socket connection.

Understanding the concept of the connectionId of a UcompOS entity is an important part of understanding how to build Proxy Components.

For instance, if you were to check the SDK source out of the UcompOS Project SDK SVN Repository, and inspect the code in the com.ucompass.ucompos.sdk.proxycomponents package, these are the Proxy Components built into the SDK that are client apparatus’ to the public API methods sponsored by the UcompOS Portal.

What you would see in the constructor for each of these Proxy Components is the following:

super._destination = _sdkModel.root;

All Proxy Components must extend the SDK abstract class AbstractProxyComponent which has a protected property _destination.

This _destination property indicates the UUID of the entity that contains the functionality being targeted by the Proxy Component.

This property can be set on the fly dynamically but in the case of the Proxy Components built into the SDK, they all target the UcompOS Portal so the _destination in these Proxy Components is always the connectionId of the root entity in the UcompOS Continuum – i.e. the UcompOS Portal.  The connectionId of the root entity in the UcompOS Continuum is always found on the public property root on the SDKModel Singleton in the SDK.

When you build your own Proxy Components, you’ll need a strategy that sets the _destination property either at the time of instantiation of the Proxy Component, or dynamically as it is possible for a single Proxy Component class to target numerous different entities simultaneously.

Proxy Components are Client Interfaces to Public API Methods

In my blog posting yesterday entitled A Services Dictionary Publishes a UcompOS Entity’s Public API Methods we learned that a UcompOS entity exposes functionality to other entities by publishing a Services Dictionary at the time the UcompOS SDK is instantiated.

The Services Dictionary can be thought of as a map that maps the name of a public API method to its internal implementation within that entity so as to not have to divulge the internal implementation details of the entity and its class structure.

This is the same concept as in a webserver configuration file.  You generally do not expose the internal absolute path of a virtual host, rather translate a domain or subdomain to resolve to an internal path on the local file system.

In the entity that originates a call to the public API method of another entity, the UcompOS SDK’s client infrastructure is leveraged.

Any data that is to be sent is packaged in an instance of an SDK class called a CommandObject which is then packaged into an instance of an SDK class called DataPackage which is the exclusive format of data that travels between entities in the UcompOS Continuum.

While it is certainly permissible to manually construct CommandObjects and DataPackages and then send them to the send(); method of the SDKClient Singleton (AS3 SDK documentation is available to help with this), it is far more convenient to set up a Proxy Component to handle this.

In advanced cases, you may find the need to call up functionality in a remote entity that is NOT exposed in the ServicesDictionary.  You can still do this but of course it requires knowledge of the remote entity’s internal implementation details and you have to specify the class path and associated method in the target entity.  This scenario does require the manual construction of a CommandObject and DataPackage and then a call to the SDKClient Singleton send(); method.

Proxy Components allow you to add more practical interfaces to the functionality exposed by a remote entity.

For instance, by strict convention, all methods that participate in UcompOS Continuum activity must receive one parameter of type Object.

You could create a Proxy Component however that masks this implementation requirement.

As I explained in my posting yesterday, the UcompOS SDK is packaged with a number of Proxy Components that represent the client interfaces to the public API methods exposed by the UcompOS Portal.

The UcompOS Portal is just another entity with the SDK installed in the context of the UcompOS Continuum.

As an example, we looked at the UcompOSWindowProxy class and specifically, its add(); method.

Let’s look at a simple example here to help explain this.

Suppose we have two UcompOS entities loaded into the UcompOS Portal.  One will expose a public API method, the other will call that public API method.

In the Services Dictionary of the entity being contacted, we may see a property that looks something like this:

'API.someMethod':{ static:true, classRef:APIClass,method:'testMethod',description:'A test API method'}

The classRef is the internal class path that houses the target implementation method, testMethod.  Notice there is also a requirement to articulate whether the method is static or not.

Let’s suppose our internal testMethod(); implementation looks like this:

public static function testMethod(data:Object):Object
{
 return {value:data.value*data.value};
}

Again, by strict convention, any public API method must receive exactly one parameter of type Object as input and return a value of type Object.  In this extremely simple method, we return the square of the value property of the Object passed to the API method.

In the entity that will call this public API method, we could create a Proxy Component.

All Proxy Components by strict convention MUST extend the SDK class AbstractProxyComponent.

Our Proxy Component class may look something like this:

package
{
 
 import com.ucompass.ucompos.sdk.client.AbstractProxyComponent;
 
 public class TestProxyComponent extends AbstractProxyComponent
 {
 
   public function TextProxyComponent()
   {
     super();
     super._destination = _sdkModel.parent;
   }
 
   public function squareNumber(value:Number):void
   {
     call("API.someMethod",{value:value});
   }
 
 }
 
}

Some things to point out:

  • The call method in our Proxy Component requires exactly two parameters – the public API method name in the remote entity and the Object instance to be passed to the method – this is the strict convention to be employed in Proxy Components
  • In the constructor of our Proxy Component, we are setting the value of the protected property _destination – this concept will be covered in a follow-up posting in the near future but for now I’ll point out that all entities have a universally unique identifier associated with them (UUID) that can easily be determined dynamically
  • In this case, since the _destination property is being sent to _sdkModel.parent, the implementation scenario would involve the entity that is publishing the public API method ‘API.testMethod‘ has launched the sub-application that will call the public API method – thus the target entity is the parent in this context and its UUID is accessible on the public property parent on the _sdkModel instance that is inherited from AbstractProxyComponent

And then to invoke the squareNumber(); method, you’d see something like this:

var p:TextProxyComponent = new TestProxyComponent();
p.squareNumber(5);

The call(); method of the AbstractProxyComponent class that all Proxy Components extend masks the implementation details of CommandObjects and DataPackages from the developer.  call(); provides a direct interface to the SDKClient Singleton send(); method that sends a DataPackage instance into the UcompOS Continuum bound for the target entity.

In tomorrow’s posting, I’ll talk about how you actually get data back from the public API method.  In other words, how we actually get the result from the squareNumber(); method in our Proxy Component.

This has to do with the asynchronous event handling implementation model in the UcompOS RPF that is modeled after the MVC (Model-View-Controller) design pattern.  (Is MVC really a Design Pattern?  Lott and Patterson seem to think so in their book Advanced ActionScript 3 with Design Patterns).

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 RPF, 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 DataPackageDataPackage 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.

The Mechanics of HTML Applications in the UcompOS RPF

When I set out to build the application (the Educator 2 E-Learning Enterprise Management System) that led me to build the UcompOS RPF, I was operating under some very simple premises.

One such premise is, to the maximum extent possible, I didn’t want to be handcuffed exclusively to any one particular technology.

While I did make the decision to make the UcompOS Portal (i.e. the “Main Container”) a Flex 4 application (and I am very comfortable with this decision), as far as the sub-applications that would be launched inside or alongside the UcompOS Portal, I simply felt I absolutely needed to be able to work with as wide of a canvas as possible and for me this includes Flex, Flash, HTML/JavaScript/CSS, Adobe AIR for desktop UcompOS applications and even Microsoft Silverlight.

But any of these technologies need be able to interact with each other seamlessly such that what particular technology a UcompOS application or sub-application was constructed with was irrelevant in the context of the overall UcompOS Continuum.

In a full-fledged UcompOS implementation that has multiple UcompOS Applications all working in conjunction with each other, each application, sub application, and the UcompOS Portal itself is a player I refer to as an entity.

For an entity to be able to talk to other entities or execute API commands on other entities, it must have the UcompOS SDK installed.  The download for the UcompOS SDK contains 3 resources for Flash/Flex, AIR, and JavaScript developers.

The Flash/Flex SDK is a SWC that has no Flex dependencies so you can use it interchangeably in Flash as well as Flex applications, however, the Flash/Flex SDK is based on ActionScript 3 and at this point I don’t yet have any intention to release an ActionScript 2 SDK (though there still are ways to involve ActionScript 2 applications in UcompOS implementations and that will be covered in a future posting).

The AIR SDK is a SWC file that has some AIR 2.0 dependencies so that will only compile in an application that has the AIR 2.0 SDK at its disposal.

The JavaScript SDK is comprised of 2 files: UcompOSSDK.js and UcompOSSDK.swfUcompOSSDK.js needs to be included in a UcompOS HTML Application <SCRIPT/> element and the file can be placed on any webserver – even on a domain different than the domain hosting the UcompOS Application.

However, the UcompOSSDK.swf file MUST be served on the SAME domain as the domain that is serving the UcompOS HTML Application.

By default, the UcompOS SDK will look for the UcompOSSDK.swf file in the root of the webserver – i.e. at an absolute path of /UcompOSSDK.swf.  This however can be changed by setting the basePath variable in your HTML Application’s JavaScript code to a relative or absolute path – for instance:

basePath = ‘./’;

would tell the UcompOS SDK that the UcompOSSDK.swf file can be found in the same directory as the HTML Application.

In my post yesterday I discussed the fact that for browser based content, UcompOS applications use the LocalConnection infrastructure of the Flash Player as their communication medium (UcompOS AIR Applications use the new AIR 2.0 ServerSocket implementation).

So if the UcompOS Continuum is based on the LocalConnection which is a Flash Player implementation, how then do UcompOS HTML Applications participate in UcompOS transactions?

The answer is using the UcompOSSDK.swf as a Proxy to the UcompOS Continuum.

When the UcompOSSDK.js file is implemented into a UcompOS HTML Application, the UcompOSSDK.swf file is loaded as an invisible element into the page via DHTML.

The UcompOSSDK.swf file is actually a UcompOS Application with the UcompOS SDK incorporated into it.

The UcompOSSDK.swf application utilizes a Proxy Component (more on Proxy Components in a future post) called JavaScriptCommand.  The UcompOS JavaScript SDK serves as a traffic manager between a UcompOS Application’s internal implementation and the UcompOSSDK.swf application.  Then the UcompOSSDK.swf application and its JavaScriptCommand Proxy Component serves as a proxy to the UcompOS Continuum.

If all this sounds complicated, well, it is.  It took a lot of time to think through the implementation details and to develop, test, and perfect them.  But once you get your head around it, it is very easy to implement.

For the most part, the mechanics involved with leveraging the UcompOS JavaScript SDK are very similar to those involved with the UcompOS AIR and Flash/Flex SDK.

ActionScript 3 and JavaScript are different languages though.  For starters, ActionScript 3 is a strongly typed language and JavaScript is not.  Also, implementing Object Oriented design patterns in JavaScript is more convention than strict procedure so there are subtle differences I’ll cover in this blog as the project evolves.

To get started building simple UcompOS HTML applications, you really only need to know a very basic amount of JavaScript.  To build full fledged powerful UcompOS HTML applications however that implement Services Dictionaries and Proxy Components (I’ll devote separate postings to each of these exciting topics in the next week), you do need to invest some time into learning Object Oriented Programming conventions in the JavaScript programming language.

The UcompOS RPF and Adobe AIR 2.0

One of the more exciting features of the UcompOS Rich Productivity Framework is the ability for UcompOS Applications to exist as Adobe AIR 2.0 applications.

Therefore, the browser-based UcompOS Portal can launch and control these applications which are running on the desktop.  The implications of this are very exciting to me as we can implement a Rich Portal Application that gives us the best of a web application with basically full range of the Desktop to create whatever experience we want or need to.

Further, using the UcompOS SDK, a UcompOS Application can launch a UcompOS AIR application as a sub-application.

Over the next week or so, I’ll publish some tutorials that highlight some of the really cool things you can do with this capability, but clearly the key value-added proposition here is a portal continuum implementation that transcends the browser and extends to the Desktop.

I thought I’d start my AIR-related blogs with a technical discussion of why I have chosen to require any UcompOS AIR Application to be based on AIR 2.0 or greater versus the AIR 1.x versions that are now in production.

The UcompOS AIR SDK in fact will only work when embedded in an AIR Application created with AIR 2.0 or greater.  If you try to instantiate it, for instance, in an AIR 1.5.3 application you’ll get a compile error.

AIR 2.0 is in a state of public beta right now. You can download the runtime or the SDK at the following link:

http://labs.adobe.com/technologies/air2/

There are many advancements and new features that have been made in AIR 2.0 but I only really wish to focus on one of them here and that is the one that led me to make the decision I made which is to require at least AIR 2.0.

The capability I am speaking of is the new ServerSocket class.

Perhaps one of the most powerful advancements in Flash technology (in my opinion anyway) came way back in Flash 5 near the turn of the century with the ability for Flash content to make socket connections to a socket endpoint thus ushering in an era of bi-directional socket applications.  This has paved the way for Flash applications to extend to the realm of chat and IM clients, telephony-based applications, etc.

With AIR 2.0, in addition to the application being able to connect to a socket endpoint, it can itself serve as a socket endpoint.

The UcompOS SDK is what makes communication between entities in the UcompOS Continuum possible – specifically the SDKServer and SDKClient classes in the SDK.

Each entity that has the UcompOS SDK implemented – Flash, Flex, AIR, or JavaScript – is equipped to operate both as a server and a client in terms of its ability to receive as well as send commands from / to other entities in a particular UcompOS implementation.

The transmission framework in the SDK is based on the LocalConnection infrastructure of the Flash Player.  I chose to go this route because I was looking for something Lowest Common Denominator and LocalConnection has been around in the Flash Player for a very long time.

This would simplify the ability to use the same SDK in content ranging from ActionScript 1.0 through Flex 4.

When I went to implement my SDK in AIR Applications, I was confronted with some harsh limitations of the LocalConnection.

And I am not even talking about the limitation of being able to send no more than a 40 K chunks of data at a time – that limitation was easily side-stepped.

What I would find is that if you tried to send more than one SDK transmission at a time to an AIR application via the original LocalConnection strategy, the AIR application would cease being able to participate in any further LocalConnection activity until that application would quit and be restarted.  The ability for an AIR application to “queue” inbound LocalConnection requests seems broken and it seems that if a synchronous operation is processing in an AIR application and another LocalConnection request arrives during that time period, there is a total breakdown in the ability for the AIR application to participate in any further LocalConnection activity.

This apparently is a known issue and is said to be the case with any and all Flash-based content – running in the AIR runtime or in the Flash Player in the browser – however I have primarily only been able to observe this for myself with AIR-based content.

Obviously, this is unacceptable.  In an ambitious UcompOS implementation, you may have one AIR application that is being bombarded with requests from dozens of UcompOS Applications simultaneously.

With the limitations of LocalConnection, this would break down the Continuum and the ability for AIR applications to be a part of the Continuum very quickly.

I have engineered an alternative to LocalConnection to use in AIR applications at least for in-bound traffic.

When the UcompOS SDK is instantiated in an AIR 2.0 application, the application binds and listens on two ports – the first two available ports on the computer above port 1024 (though you can explicitly set these port numbers but more on that in another blog entry).

One port is used for validation purposes (as Flash Player socket activity is governed by very strict protocols these days) and the other is used for transmission of actual SDK messages.  Whether it is intentional or not I don’t know, but I never was able to get validation and SDK data transmission activity working on the same port so by strict convention I implemented the need for two ports.

In the SDK, if the destination of SDK activity is an AIR application, it automatically sends the message via a class in the SDK that extends Socket such that any in-bound SDK activity arriving to a UcompOS AIR Application arrives via the new ServerSocket capabilities in AIR 2.0.

Any other activity in the UcompOS Continuum leverages the legacy LocalConnection architecture of the Flash Player.

I have been very impressed by the durability of the ServerSocket architecture I have implemented here.  I have successfully transmitted very large files as ByteArrays from a browser-based UcompOS Flex application to a UcompOS AIR 2.0 application without any issues whatsoever.

LocalConnection is not without its issues however, but the UcompOS SDK has managed to sidestep most all of these issues.

So in summary, the reason I have chosen to require AIR 2.0 for UcompOS AIR Applications exclusively has to do with the introduction of the great new ServerSocket class.  However, make no mistake about it.  The great other new features of AIR 2.0 are definitely game-changers and I encourage you to check them out.

In the near future, I’ll post about UcompOS JavaScript applications and how I have implemented them into the UcompOS RPF.