Developing multi-versioned applications

In most applications, even very large applications, the entire base of source code is compiled before release. This recompilation ensures that all pieces of the applications are using the same APIs. This model works well for desktop software and software whose releases are tightly controlled. But it does not always work well in the Internet model of application development, where applications are continually updated.

Internet applications are often developed and released gradually, with new pieces of functionality being added to the application all the time. To ease application development of this kind, Flex lets you create multi-versioned applications. A single application can load multiple sub-applications even if those sub-applications are compiled with different versions of the Flex framework. Sub-applications must, however, be compiled with the same or older version of the compiler that was used to compile the main application.

Using multi-versioned applications is common for sandboxed applications where more than one group of developers works on different pieces of functionality. It is also common on very large applications that are continually improved on and released.

When compiling multi-versioned applications, you should include the MarshallingSupport class into the main application and sub-applications. You do this with the includes compiler argument, as the following example shows:
-includes=mx.managers.systemClasses.MarshallingSupport

Using RPC and DataServices-related functionality in your multi-versioned applications has some limitations. These limitations depend on the type of remote data access you use: RPC classes with a proxy server, RPC classes without a proxy server, and DataServices and RemoteObject functionality. In many of these cases, you should develop sandboxed applications rather than multi-versioned applications. For more information, see Using RPC and DataServices classes with multi-versioned applications.

Flex supports multi-versioned applications that were compiled with version 3.2 of the Flex framework and later. For example, your main application can be a Flex 4.6 application, and the loaded applications can be compiled with the following versions of the framework:
  • 3.2

  • 3.4

  • 3.6

  • 4

  • 4.5

  • 4.6

If you try to load an sub-application that was compiled with a more recent version of the framework than the main application, you will get run time errors if the two applications use a shared resource whose API changed between versions of the Flex framework.

In general, a main application that is compiled for Flash Player 11 can load sub-applications that were compiled for Flash Player 11 or Flash Player 10. However, main applications that were compiled for Flash Player 10 cannot load sub-applications that were compiled for Flash Player 11.

Sub-applications that are compiled with an older version of the Flex framework can be loaded either locally (from the same domain or subdomain, in which case they are trusted) or cross-domain (from a different domain, in which case they are untrusted by default). A trusted multi-versioned application will have nearly the same level of interoperability with the main application as a single-versioned trusted application. Styles and resource bundles are not shared, but otherwise they have the same level of interoperability. An untrusted multi-versioned application has slightly more interoperability than a single-versioned untrusted application has when loaded. With untrusted multi-versioned applications, focus management and mouse events work with the main application.

When you load a multi-versioned application, you set the loadForCompatibility property of the SWFLoader control to true. Setting this property instructs the loader to load the application into a sibling application domain. Applications that are in sibling application domains each maintain their own class definitions. They communicate through event passing, if necessary. For example, if a sub-application must do something that only the top-level application’s manager can do, then it communicates with the main application by dispatching an event.

The following sections describe how to develop multi-versioned applications. They assume that all applications are trusted (loaded into the same security domain), and loaded into sibling application domains of the main application. For information about building applications that are in separate security domains, see Developing sandboxed applications.

Using RPC and DataServices classes with multi-versioned applications

RPC and DataServices classes are a special case when used in multi-versioned sub-applications. All applications in the same security domain must share the same definitions of the RPC and DataServices classes for messaging to work. In addition, they might also need to share the same definitions for custom value object classes. This can be a problem because the only way to ensure that a main application and a sub-application use the same set of definitions is to load the sub-application into a child application domain of the main application, which would prevent multi-versioned applications from working. As a result, to use RPC classes in multi-versioned applications, you can do one of the following:
Load the applications as sandboxed applications
If you do this, you must call the Security.allowDomain() method from the main application on the sub-application’s domain in the SWFLoader control’s complete event handler. You must also call the Security.allowDomain() method from the sub-application on the main application’s domain. This provides the same level of application interoperability as multi-versioned applications. In the sub-application, you must call the allowDomain() method early on in the initialization cycle. Use the application’s preinitialize event.

Use a bootstrap loader
You define the messaging classes in a bootstrap loader. You then load the main application into the bootstrap loader. The main application and its sub-applications can then share those class definitions.

When you use RPC classes (such as WebService and HTTPService) without a proxy server, you do not need to load the sub-application as a sandboxed application or externalize the classes in a bootstrap class loader. Applications that use these classes without a proxy server should work as well as any multi-versioned application. Be sure to that either the URLs for the target services are on the same server as the main application, or that the target server has a crossdomain.xml file on it that allows access.

Pop-up controls in multi-versioned applications

When a sub-application launches a pop-up control, it floats over the entire application, and can be dragged anywhere on the stage. Pop-up controls are created in the sub-application’s application domain and passed to the main application's PopUpManager. Pop-up controls cannot be strongly-typed as IUIComponent, so they are marshaled from the sub-application to the main application for display.

Pop-up windows behave as follows in a multi-versioned sub-application (these behaviors are the same for single-versioned sub-applications):

  • Launching a modal dialog box dims the entire application, not just the sub-application that launched it

  • Centering a pop-up centers it over the entire application, not just the sub-application

  • Dragging pop-up controls works over the entire application, not just the sub-application

  • Focus shifts to the pop-up when you first launch a pop-up from the sub-application

The PopUpManager must be linked in to all applications that are sandbox roots. When using the PopUpManager in a child application, there must also be an instance of the PopUpManager in the main application. To link the PopUpManager to a main application that doesn’t use it, you can add the following code:

import mx.managers.PopUpManager;   
private var popupManager1:PopUpManager;
If you do not link a PopUpManager into an application, and a sub-application in its sandbox requests that it display a modal dialog box, you might get the following error:
No class registered for interface 'mx.managers::IPopUpManager'.
List controls require some additional code when used in sub-applications. If you have a List control in a sub-application’s pop-up and no List control in your main application, you must also link the List class into your main application. In addition, the List control must have a data provider. You can link an invisible List into your main application and set its data provider to an empty array, as the following example shows:
<mx:List visible="false" includeInLayout="false" dataProvider="[]"/>

Embedded fonts in multi-versioned applications

Applications that are in the same application domain are able to use the same embedded fonts by specifying the font name. However, if the sub-application is loaded into a different application domain (as is the case with multi-versioned applications), then the sub-application must embed the font to use it.

Styles and style modules in multi-versioned applications

Using styles and style modules in multi-versioned sub-applications is the same as using them in sandboxed sub-applications. For more information on using styles and style modules in sub-applications, see Styles and style modules in sandboxed applications.

Focus in multi-versioned applications

Using the FocusManager in multi-versioned sub-applications is the same as using the FocusManager in sandboxed sub-applications. (See Focus in sandboxed applications.)

Cursors in multi-versioned applications

The CursorManager class in the main application and sub-application communicate much like the PopupManager class. If the sub-application changes the cursor to a custom cursor, this custom cursor continues to appear when the cursor is moved outside the sub-application and over the main application. Calling a method or setting a property on the CursorManager in the sub-application bubbles up to the main application, and vice versa.

If a busy cursor is visible while the mouse is over the sub-application, and you move the cursor to the main application, the cursor remains as a busy cursor.

Localizing multi-versioned applications

Using resource bundles in multi-versioned sub-applications is the same as using them in sandboxed sub-applications. For more information on using resource bundles in sub-applications, see Localizing sandboxed applications.

ToolTip objects in multi-versioned applications

ToolTip objects are created in the sub-application’s application domain and are passed to the main application’s ToolTipManager. This is an example of marshaling a class across application boundaries.

This behavior applies to error tips and data tips on List objects in multi-versioned applications as well.

Layouts in multi-versioned applications

The main application dictates the area of the screen that is available to the sub-application for screen layout.

LayoutManagers in different applications run independent of one another. The applications are not clipped.

Deep linking in multi-versioned applications

You cannot access the main application’s BrowserManager directly from a sub-application that is in a different application domain. The sub-application’s BrowserManager is disabled. This is the same for sandboxed and multi-versioned sub-applications. For more information, see Deep linking in sandboxed applications.

Dragging and dropping in multi-versioned applications

When the sub-application is in the same security domain as the main application, but in a different application domain, you can drag list items from a sub-application to a list in the main application, and vice versa. For this to work across applications, the class that defines the drop target must be public.

The DragProxy maintains the item’s appearance during the entire event, regardless of which application the mouse is over. Drag events are triggered for all source and destination controls, as if they are in the same application.

In the background, the DragManager generates the DragProxy in the sub-application's application domain then passes that DragProxy to the main application's DragManager during the drag operation.

Strongly-typed objects cannot be passed from the main application to the sub-application or vice versa. As a result, the DragManager accepts a DragProxy that is not strongly typed.

All properties and methods defined by the IDragManager interface are handled through delegation to the main application’s DragManager.

If you drag a generic type object, the object will be of generic type Object in Flash Player’s application domain. Flex marshals the properties so that the drag and drop operation works without having to write any custom code.

If you use a custom class as part of the drop data, that class cannot be shared as strongly-typed. In that case, override the drop events and add logic to marshall the data from the drag source to the drop target.

Listening for mouse events in multi-versioned applications

Mouse interaction between a main application and a sub-application can be confusing, especially when those events occur in different application domains. For example, when you click and drag an object in a sub-application, and then release the button over the main application, typical Flex mouse events such as MOUSE_UP, MOUSE_DOWN_OUTSIDE, or MOUSE_LEAVE are triggered but cannot be listened to directly in the sub-application.

To listen for mouse events across application domains, listen to all mouse activity in the security domain. To listen, get a reference to the sandbox root and register your event listeners with that SystemManager.

When loaded into a separate application domain, the topLevelSystemManager property refers to the sub-application’s SystemManager because the main application's SystemManager is in another application domain. As a result, you do not use the topLevelSystemManager property to get a reference to the main application’s SystemManager. Instead, you use the systemManager.getSandboxRoot() method to get a reference to the top-level SystemManager in the security domain. From this SystemManager, you can access all mouse activity in the current security domain.

The following example uses calls to the getSandboxRoot() method to get references to the top-level SystemManager in the security domain.

<?xml version="1.0" encoding="utf-8"?>
<!-- apploading/ZoomerPattern3.mxml -->
<s:Application 
    creationComplete="setup()" 
    height="250"
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

     <fx:Script>
     <![CDATA[
          import mx.core.UIComponent;
          import mx.managers.PopUpManager;

          [Bindable]
          public var data1:Array = ["Ice Cream", "Fudge", "Whipped Cream", "Nuts"];

          public var zoomTool:UIComponent;

          public function setup():void {
               // Draw the zoom rectangle.
               zoomWidget.graphics.lineStyle(1);
               zoomWidget.graphics.beginFill(0, 0);
               zoomWidget.graphics.drawRect(0, 0, 17, 17);
               zoomWidget.graphics.endFill();
               
               // Listen for mouse down events.
               zoomWidget.addEventListener(MouseEvent.MOUSE_DOWN, zoom_mouseDownHandler);
          }

          private var lastX:int;
          private var lastY:int;

          private function zoom_mouseDownHandler(event:MouseEvent):void {
               // When the mouse is down, listen for the move and up events.              
               // The getSandboxRoot() method lets you listen to all mouse activity in your
               // SecurityDomain.
               systemManager.getSandboxRoot().addEventListener(
                    MouseEvent.MOUSE_MOVE, zoom_mouseMoveHandler, true);
               systemManager.getSandboxRoot().addEventListener(
                    MouseEvent.MOUSE_UP, zoom_mouseUpHandler, true);

               // Update last position of the mouse.
               lastX = event.stageX;
               lastY = event.stageY;

               // Create and pop up the zoomTool. This is the rectangle that is dragged around.
               // It must be a popup so that it can float over other content.
               zoomTool = new UIComponent();
               PopUpManager.addPopUp(zoomTool, this);
               
               var pt:Point = new Point(zoomWidget.transform.pixelBounds.x, 
                    zoomWidget.transform.pixelBounds.y);
               pt = zoomTool.parent.globalToLocal(pt);
               zoomTool.x = pt.x;
               zoomTool.y = pt.y;
               zoomTool.graphics.lineStyle(1);
               zoomTool.graphics.beginFill(0, 0);
               zoomTool.graphics.drawRect(0, 0, 17, 17);
               zoomTool.graphics.endFill();

               // Hide the rectangle that was the target.
               zoomWidget.visible = false;
          }

          private function zoom_mouseMoveHandler(event:MouseEvent):void {
               // Update the position of the dragged rectangle.
               zoomTool.x += event.stageX - lastX;
               zoomTool.y += event.stageY - lastY;
               lastX = event.stageX;
               lastY = event.stageY;

               var bm:BitmapData = new BitmapData(16, 16);

               // Capture the bits on the screen.  
               // You must use the getSandboxRoot() method here, too, because it gets 
               // the parent of all of the pixels you are allowed to access.
               bm.draw(DisplayObject(systemManager.getSandboxRoot()), 
                    new Matrix(1, 0, 0, 1, -zoomTool.transform.pixelBounds.x - 2, 
                    -zoomTool.transform.pixelBounds.y - 2));

               // Create a Bitmap to hold the bits.
               if (zoomed.numChildren == 0) {
                    var bmp:Bitmap = new Bitmap();
                    zoomed.addChild(bmp);
               } else
                    bmp = zoomed.getChildAt(0) as Bitmap;

               // Set the bits.
               bmp.bitmapData = bm;

               // Zoom in on the bits.
               bmp.scaleX = bmp.scaleY = 8;
          }

          private function zoom_mouseUpHandler(event:Event):void {
               // Remove the listeners.
               systemManager.getSandboxRoot().removeEventListener(
                    MouseEvent.MOUSE_MOVE, zoom_mouseMoveHandler, true);
               systemManager.getSandboxRoot().removeEventListener(
                    MouseEvent.MOUSE_UP, zoom_mouseUpHandler, true);

               // Replace the target rectangle.
               zoomWidget.visible = true;

               // Remove the dragged rectangle.
               PopUpManager.removePopUp(zoomTool);
          }

     ]]>
     </fx:Script>
     
     <mx:HBox>
          <mx:HBox backgroundColor="0x00eeee" height="140" paddingTop="4" paddingRight="4">
               <mx:Label text="Drag Rectangle"/>
               <mx:UIComponent id="zoomWidget" width="17" height="17"/>
               <mx:Canvas id="zoom" 
                    borderStyle="solid" 
                    width="132" 
                    height="132"
               >
                    <mx:UIComponent id="zoomed" width="128" height="128"/>
               </mx:Canvas>
          </mx:HBox>
          <mx:List dataProvider="{data1}"/>
     </mx:HBox>
</s:Application>
The following example main application loads the previous example application. It sets the loadForCompatibility property to true so that the sub-application loads a multi-versioned application.
<?xml version="1.0" encoding="utf-8"?>
<!-- apploading/MainZoomerPattern3.mxml -->
<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/mx">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <mx:Text text="Cross-Versioning (trusted versioning application):"/>

    <mx:SWFLoader id="swf1" loadForCompatibility="true" source="ZoomerPattern3.swf"/>
     
</s:Application>