Embedding Flex Applications in a Browser: Chapter 20 - Programming Flex 3
Pages: 1, 2, 3

As we already mentioned, the history.js file code is shortsighted in the way it works, and because of this you'll need to edit it for it to work with SWFObject-embedded applications. The primary error is that history.js assumes that all applications are embedded using object tags for Internet Explorer and embed tags for other browsers. That assumption doesn't work for SWFObject-embedded applications that use nested object tags. Therefore, you need to adapt the history.js code to be able to find applications embedded using nested object tags. The authors of this book don't claim to be JavaScript experts, and we don't claim that our solution is the best or most elegant, but it does work and it is simple. All you need to do is edit history.js and add a few lines of code to the getPlayer() method, as shown in Example 20-11. This code returns if it is defined. We'll set this property in the HTML file using SWFObject to retrieve the reference.

Example 20-11. Changes to getPlayer() in history.js

function getPlayer(objectId) {
         if(BrowserHistory.flexApplication != null) {
              return BrowserHistory.flexApplication;
         }
        var objectId = objectId || null;
        var player = null;
        if (browser.ie && objectId != null) {
            player = document.getElementById(objectId);
        }
        if (player == null) {
            player = document.getElementsByTagName('object')[0];
        }

        if (player == null || player.object == null) {
            player = document.getElementsByTagName('embed')[0];
        }

        return player;
    }

Next, in the HTML, we need to set BrowserHistory.flexApplication to reference the Flex application instance. We need to use SWFObject's getObjectById() method to retrieve the reference. We'll also use the addLoadEvent() method of SWFObject to ensure that we assign the reference once the application is defined in the page. Example 20-12 shows the new code.

Example 20-12. Setting BrowserHistory.flexApplication in the HTML file

<script type="text/javascript">
            swfobject.registerObject("flexApplication", "9.0.0");
            swfobject.addLoadEvent(loadEventHandler);
            function loadEventHandler() {
                BrowserHistory.flexApplication =
swfobject.getObjectById("flexApplication");
            }
        </script>

With these changes, SWFObject will work with BrowserManager. You need to make sure that when you deploy the application, you deploy the .swf file, the HTML file, swfobject.js, and the history directory with history.js, history.css, and historyFrame.html.

Section 20.3: Flash Player Security

Flash Player enforces security rules for what and how applications can access data, and you'll notice this especially when embedding a Flex application in an HTML page. Flex applications can typically access all data resources in the same domain as the .swf. For example, if the .swf is deployed to www.example.com, it can access a web service that is also deployed at www.example.com. However, access to data resources at different domains is disallowed by Flash Player unless that domain explicitly gives permission. The Flash Player security rules disallow access to data resources unless the domains match exactly, including subdomains, even if the domain names resolve to the same physical address. That means an .swf deployed at www.example.com cannot access data from test.example.com or even example.com unless the server explicitly allows access. The domain can give permission by way of a cross-domain policy file.

Note: When working with the socket class and loading data through sockets rather than through request/response mechanisms, Flash Player 9,0,115,0 and later introduce an additional socket policy file requirement. Since Flash Player security is continually evolving, we recommend that you review the latest articles at http://www.adobe.com/devnet/flashplayer/ for any changes that may impact your application.

A cross-domain policy file is an XML file that resides on the server that hosts the data resources. The format for a cross-domain policy file is as follows:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
          "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="www.example.com" />
</cross-domain-policy>

The root <cross-domain-policy> node can contain one or more <allow-access-from> elements. The <allow-access-from> elements specify the domains that can access the resources on the server. You can use an * wildcard in place of the subdomain, which means that any subdomain can access the data resources. For example, the following policy allows access from www.example.com, beta.example.com, test.example.com, etc.:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
          "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*.example.com" />
</cross-domain-policy>

You can also use the * wildcard in place of the entire domain to allow access from all domains:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
          "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*" />
</cross-domain-policy>

If the server uses HTTPS and wants to allow access to .swf files deployed on nonsecure domains, it must specify a value for the secure attribute. The following allows access to .swf files deployed at www.example.com:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
          "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="www.example.com" secure="false" />
</cross-domain-policy>

By default, Flash Player looks for a policy file named crossdomain.xml at the root of the web server from which it is requesting the data resources. If Flash Player attempts to load an XML document from http://www.example.com/data/xml/data.xml, it will look for http://www.example.com/crossdomain.xml. If you want to set different permissions for different resources on a server, you can optionally deploy different policy files in different locations on the server. For example, a policy file located at http://www.example.com/data/xml would apply only to the resources in that directory. However, when you place policy files in nondefault locations, you must use ActionScript to load the policy file in your Flex application. The ActionScript code uses the static loadPolicyFile() method of the flash.system.Security class. The following loads a policy file:

Security.loadPolicyFile("http://www.example.com/data/xml/policy.xml");

Deploying a cross-domain policy file presupposes that you have access to the server with the data resources—or that you can persuade those with the server to deploy the policy file. In the few cases where you cannot deploy a policy file on a server whose data resources you need to utilize, you have the option of deploying a proxy file on your server. A proxy file is a file that exists on your server (a .jsp, an ASP.NET page, a ColdFusion page, a PHP page, etc.) to which your Flex application can make requests. The proxy file then makes the requests to the remote resource and relays the data back to Flash Player.

Section 20.4: Using Runtime Shared Libraries

Runtime shared libraries are a way to share assets and libraries among multiple .swf files on the same domain. This is useful when you have several .swf files that comprise an application or that span several applications deployed in the same domain in which each .swf file utilizes many common assets and/or libraries. For example, if a.swf and b.swf both utilize the same subset of 25 classes and embedded images that add up to 100 KB, the user has to download the same 100 KB twice, once for each .swf.

The theory behind runtime shared libraries involves a concept called linking. All .swf files employ one or both forms of linking: static and dynamic. By default, all linking is static. When an asset or source file is statically linked with an .swf, it is compiled into the .swf. Dynamic linking means that the asset or source file is not compiled into the .swf, but the .swf has a reference to an .swf into which it has been compiled. Through dynamic linking, you can specify certain elements that should not be compiled into an .swf to reduce the total file size of the .swf. The .swf is then linked to another .swf where the elements have been compiled. This allows you to extract common elements from two or more .swf files and place them into another .swf to which all the .swf files are linked dynamically. This new .swf is called a runtime shared library.

We can understand the benefit of runtime shared libraries by looking at the a.swf and b.swf example in more detail. In this example, a.swf is 200 KB and b.swf is 400 KB. Both .swf files are deployed on the same domain. The two .swf files happen to use of common elements. That means that if a user uses both a.swf and b.swf, she downloads 600 KB, of which 100 KB is duplicate content. Using a runtime shared library, you can introduce a new .swf, library.swf, which contains the 100 KB of common content. Although there's some overhead in creating a runtime shared library for our purposes, we'll keep the numbers simple: a.swf will now be 100 KB and b.swf will now be 300 KB. Each will be dynamically linked to library.swf, which also has to download. However, the second time the user requests library.swf it will be retrieved from client cache rather than from the server, effectively saving 100 KB.

The underlying manner in which you create and use runtime shared libraries is always the same. However, if you are working with the compiler from a command line or from a custom build script using Ant, the workflow is different from using Flex Builder, which automates a lot of the work involved with runtime shared libraries.

Creating Runtime Shared Libraries with the Command-Line Compilers

When you want to create a runtime shared library with the command-line compilers, you need to use both the mxmlc application compiler and the compc component compiler. First you must use the compc compiler to compile all the common elements into an .swc file. An .swc is an archive format, and in the case of a runtime shared library it contains two files: library.swf and catalog.xml. The .swf file contained within the .swc is the runtime shared library file. You then use the mxmlc compiler to compile the application as usual, but this time you notify the compiler to dynamically link to the runtime shared libraries.

Note: Creating runtime shared libraries is an advanced feature. You may want to return to this section only after you're comfortable creating Flex applications and you want to optimize an application or applications that would benefit from runtime shared libraries.

Using compc

Like the mxmlc compiler, the compc compiler has options that you can use to determine what gets compiled and how. The first option you'll need to specify is source-path, which tells the compiler where to look for the files you want to compile. If you are compiling classes in packages, the source-path should be the root directory of the packages. If you want to use the current directory you must still specify a value, using a dot. If you want to use more than one directory, you can list the directories delimited by spaces.

You must compile one or more classes into a runtime shared library. You have to list each class using the include-classes option. There is no option to simply include all the classes in a directory. You must list each class individually. You must list each class using the fully qualified class name, and you can list multiple classes by separating them with spaces.

You must also specify an output file when calling compc. Use the output option, and specify the path to an .swc file that you want to export. The following example compiles the class com.oreilly.programmingflex.A into an .swc file called example.swc:

compc -source-path . -include-classes com.oreilly.programmingflex.A
-output example.swc

Compiling many classes into a runtime shared library can result in a very long command. To simplify this you can use either configuration files or manifest files.

Like mxmlc, you can use configuration files with compc by specifying a load-config option. Also like mxmlc, the compc compiler automatically loads a default configuration file called flex-config.xml, and unless you want to duplicate all the contents of flex-config.xml (much of which is required), it generally is better to specify a configuration file in addition to the default by using the += operator, as in the following example:

compc -load-config+=configuration.xml

The following example configuration file is the equivalent of the earlier command, which specified the source path and output, and included classes from the command line:

<flex-config>
  <compiler>
    <source-path>
      <path-element>.</path-element>
    </source-path>
  </compiler>
  <output>example.swc</output>
  <include-classes>
    <class>com.oreilly.programmingflex.A</class>
  </include-classes>
</flex-config>

If you want to include many classes, you can simply add more <class> nodes, as in the following example:

<flex-config>
  <compiler>
    <source-path>
      <path-element>.</path-element>
    </source-path>
  </compiler>
  <output>example.swc</output>
  <include-classes>
    <class>com.oreilly.programmingflex.A</class>
    <class>com.oreilly.programmingflex.B</class>
    <class>com.oreilly.programmingflex.C</class>
    <class>com.oreilly.programmingflex.D</class>
  </include-classes>
</flex-config>

You can use manifest files to achieve the same result of simplifying the compiler command. However, manifest files also have an added benefit in that they allow you to create a namespace for components that you compile into the runtime shared library. This is more useful when the runtime shared library contains user interface components that you want to be able to add to an application using MXML tags. However, using a manifest file is not hurtful in any case, because it lets you simplify the compiler .

A manifest file is an XML file in the following format:

<?xml version="1.0"?>
<componentPackage>
    <component id="Identifier" class="ClassName"/>
</componentPackage>

The following example will tell the compiler to add classes A, B, C, and D to the library:

<?xml version="1.0"?>
<componentPackage>
    <component id="A" class="com.oreilly.programmingflex.A"/>
    <component id="B" class="com.oreilly.programmingflex.B"/>
    <component id="C" class="com.oreilly.programmingflex.C"/>
    <component id="D" class="com.oreilly.programmingflex.D"/>
</componentPackage>

Once you've defined a manifest file, you need to tell the compiler to use the file. You can achieve that with the namespace and include-namespaces options. A namespace is an identifier that you can use within your MXML documents that will map to the manifest file contents. The namespace option requires that you specify two values: first the namespace identifier and then the manifest file to which the identifier corresponds. The include-namespaces option requires that you list all the identifiers for which you want to compile the contents into the .swc file. The following example compiles the classes specified in manifest.xml into the .swc:

compc -namespace http://oreilly.com/programmingflex manifest.xml
-include-namespaces http://oreilly.com/programmingflex -output example.swc

You can also combine the use of a manifest file with a configuration file. The following configuration file uses the manifest file:

<flex-config xmlns="http://www.adobe.com/2006/flex-config">
  <compiler>
    <source-path>
      <path-element>.</path-element>
    </source-path>
    <namespaces>
      <namespace>
        <uri>http://oreilly.com/programmingflex</uri>
        <manifest>manifest.xml</manifest>
      </namespace>
    </namespaces>
  </compiler>
  <output>example.swc</output>
  <include-namespaces>
    <uri>http://oreilly.com/programmingflex</uri>
  </include-namespaces>
</flex-config>

When you use a runtime shared library, you'll need two files: the .swc and the library .swf file contained within the .swc file. You need the .swc file because the mxmlc compiler uses the .swc file to determine which classes to dynamically link. You need the .swf file because it's the file you deploy with the application and from which the application loads the libraries. The SWC format is an archive format—essentially a ZIP format. You can use any standard unzip utility to extract the .swf file from the .swc. The .swc always contains a file called library.swf that you should extract and place in the deploy directory for the application. If you plan to use several runtime shared libraries with an application, you need to either place the library.swf files in different subdirectories or rename the files.

Compiling an application using a runtime shared library

Once you've compiled an .swc file containing a runtime shared library and extracted the .swf file, you next need to compile the application that uses the library. To accomplish that you'll use mxmlc in much the same way as you'd compile an application that uses only static linking. However, when you use a runtime shared library, you need to dynamically link the relevant classes in the main application and tell the application where to find the runtime shared library .swf file at runtime. The external-library-path option specifies the .swc file or files that tell the compiler which classes to dynamically link. Use the runtime-shared-libraries option to tell the compiler where it can find the runtime shared library file(s) at runtime. The following tells the compiler to compile the application using example.swc for dynamic linking and example.swf as the URL for the shared library:

mxmlc -external-library-path=example.swc
-runtime-shared-libraries=example.swf Example.mxml

You can use configuration files for these purposes as well. The following configuration file achieves the same result as the preceding command:

<flex-config>
  <compiler>
    <external-library-path>
      <path-element>example.swc</path-element>
    </external-library-path>
  </compiler>
  <file-specs>
    <path-element>RSLClientTest.mxml</path-element>
  </file-specs>
  <runtime-shared-libraries>
    <url>example.swf</url>
  </runtime-shared-libraries>
</flex-config>

When you deploy the application, you must also deploy the runtime shared library .swf file. You do not need to deploy the .swc file along with the rest of your application.

Using Ant to build runtime shared library applications

As you've seen, building an application that uses a runtime shared library requires quite a few steps. To summarize:

  1. Compile the .swc.
  2. Extract the .swf.
  3. Move the .swf.
  4. Compile the application.

Using Ant can simplify things because you can write just one script that will run all the tasks. The following is an example of such a script:

<?xml version="1.0"?>
<project name="RSLExample" basedir="./">

  <property name="mxmlc" value="C:\FlexSDK\bin\mxmlc.exe"/>
  <property name="compc" value="C:\FlexSDK\bin\compc.exe"/>

  <target name="compileRSL">
    <exec executable="${compc}">
      <arg line="-load-config+=rsl/configuration.xml" />
    </exec>
    <mkdir dir="application/rsl" />
    <move file="example.swc" todir="application/rsl" />
    <unzip src="application/rsl/example.swc" dest="application/rsl/" />
  </target>

  <target name="compileApplication">
    <exec executable="${mxmlc}">
      <arg line="-load-config+=application/configuration.xml" />
    </exec>
  </target>

  <target name="compileAll" depends="compileRSL,compileApplication">
  </target>

</project>

Using Flex Builder to Build Runtime Shared Libraries

Flex Builder automates a lot of the tasks and provides dialog boxes for steps to create and use runtime shared libraries. Working with runtime shared libraries in Flex Builder comprises two basic steps: creating a Flex Library Project and linking your main application to the library project.

Creating a Flex Library Project

The first step in creating a Flex Library Project is to create the project by selecting File→New→Flex Library Project. Every Flex Library Project needs to have at least one element—generally a class. You can add classes to the project as you would any standard Flex project. Once you've defined all the files for the project, you'll next need to tell Flex Builder which of those classes to compile into the .swc file. You can do that by way of the project properties. You can access the properties by selecting Project→Properties. Then select the Flex Library Build Path option from the menu on the left of the dialog. In the Classes tab you should select every class that you want to compile into the library. This is all that is necessary to create a Flex Library Project.

Linking an application to a library

When you want to use a library from a Flex application, you need to tell Flex Builder to link to the corresponding Flex Library Project. You can accomplish this by selecting Project→Properties for the Flex project. Then select the Flex Build Path option from the menu in the dialog, and select the "Library path" tab. Within the "Library path" tab you click the Add Project button. This opens a new dialog that prompts you to select the Flex Library Project you want to link to your application. When you select the library project and click OK, the project will show up in the "Library path" tab list. By default, libraries are statically linked rather than dynamically linked. You must tell Flex Builder to dynamically link the library by expanding the library project icon in the list, selecting the Link Type option and then selecting the Runtime Shared Library (RSL) option from the menu.

When you add a library project to the library path for a Flex project, the application can use any of the classes defined in the library project.

Adding Nonclass Assets to Runtime Shared Libraries

Runtime shared libraries do not directly allow you to dynamically link anything other than classes. That means you cannot directly add a dynamic link to an asset such as an image, a sound, or a font. However, if you can embed an asset in an ActionScript class, you can add indirect dynamic linking. (See Chapter 11 for more details on general embedding.) The following example embeds an image using a class constant:

package com.oreilly.programmingflex {
  public class Images {

      [Embed(source="image.jpg")]
      public static const IMAGE_A:Class;

  }
}

You can compile such a class into a runtime shared library, and the asset (an image in this case) is also embedded into the runtime shared library. The following example illustrates how you could use the dynamically linked image from an application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

  <mx:Script>
    <![CDATA[

      import com.oreilly.programmingflex.Images;

    ]]>
  </mx:Script>
  <mx:VBox>
    <mx:Image source="{Images.IMAGE_A}" scaleContent="true"
              width="100" height="100" />
  </mx:VBox>
</mx:Application>

Section 20.5: Summary

In this chapter, you learned how to embed Flex applications in HTML pages. You learned how to use the industry-standard SWFObject to embed Flex applications using the static publishing method employing standards-compliant nested object tags. You also learned how to enable deep linking within Flex applications and how to integrate an application with the browser's Back and Forward buttons using BrowserManager. Furthermore, you learned how to work with cross-domain loading issues, and you learned about creating runtime shared libraries to improve loading times on applications sharing common libraries and assets.

This excerpt is from Programming Flex 3. If you want to try your hand at developing rich Internet applications with Adobe's Flex 3, and already have experience with frameworks such as .NET or Java, this is the ideal book to get you started. Programming Flex 3 gives you a solid understanding of Flex 3's core concepts, and valuable insight into how, why, and when to use specific Flex features. Learn to get the most from this amazing and sophisticated technology.

buy button