Flash
Since its introduction in 1996 under the name FutureSplash, Adobe Flash (formerly Macromedia Flash) has become the de facto standard for web animation and interactivity. With each subsequent release of the Flash platform, it has become more full-featured to the point where it can now be considered a platform for any kind of interactive app. The Flash platform is currently composed of three different components:
- A file format named SWF (files in this format are commonly called Flash movies)
- apps for displaying SWF files, packaged as a standalone executable (Flash Player) and a browser plug-in
- Authoring tools
The specification for the SWF file format is available on Adobe's web site. However, the license terms prohibit the use of the specification to create alternatives to the Flash Player. It can be used to write apps that create SWF files. As a result, authoring tools for SWF files range from Adobe's tools (Flash Professional, Flash Standard, and Flex) to open source command-line compilers like MTASC.
ActionScript
Almost every version of Flash has included some level of scripting support. Flash 5, released in 2000, introduced a new scripting language named ActionScript. ActionScript is based on ECMAScript and thus resembles JavaScript. Flash 7 (a.k.a. Flash MX 2004) introduced ActionScript 2.0 in 2003 supporting language features more commonly associated with Java than JavaScript such as class inheritance, interfaces, and strong typing. ActionScript 3.0 was introduced with Flash Player 9 in 2006 and continues this trend with better exception support, true runtime typing, a new API for XML, and regular expression support. In addition to the language itself, ActionScript has an active developer community. You can produce Javadoc-style documentation with as2api and perform unit tests with AS2Unit. For links to these and other open source tools, check out the web site http://www.osflash.org.
Flex
In March 2004, Macromedia introduced the Flex server app as an alternative development platform for Flash. Unlike Flash Professional and Flash Standard, which use a binary file format called FLA and then compile the FLA file into a SWF file, Flex uses an XML file format called MXML, which is compiled into a SWF file. MXML files represent both the display and functionality of an app. Example 13-14 contains an example MXML file. You can see the result of compiling this MXML in . Even if you've never used ActionScript, Flash, or Flex, it is pretty obvious what this app does.
Example Simple MXML form app
<?xml version="1.0" encoding="utf-8"?> <mx:app xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" > <mx:Script> <![CDATA[ import mx.collections.ArrayCollection; [Bindable] private var listData:ArrayCollection = new ArrayCollection( ); private function addText( ) : void { listData.addItem(text.text); text.text = ""; } private function clearList( ) : void { listData.removeAll( ); } ]]> </mx:Script> <mx:List x="10" y="10" dataProvider="{listData}" /> <mx:TextInput x="10" y="162" /> <mx:Button x="45" y="216" label="Add Text" click="addText( )"/> <mx:Button x="194" y="216" label="Clear List" click="clearList( )"/> </mx:app> |
Simple MXML app in a browser

Flex was originally packaged only as a J2EE web app. MXML files were compiled into SWF within a servlet process. In addition, the compiled apps could use a data gateway that was part of the web app to access server-side resources like databases, Enterprise JavaBeans (EJB), and, of course, XML data. The first version of Flex was targeted at large companies, with a large license fee per CPU. Along with the server license came a development tool called Flex Builder. However, since MXML is just XML, and XML is just text, any text editor can be used to write MXML. With the release of Flex 2 in 2006, Adobe radically changed the structure and licensing model for Flex. The Flex compiler and documentation (what Adobe refers to as the Flex SDK or Flex Framework) can be freely downloaded after registering from http://www.adobe.com/products/flex. This free compiler can be used as a standalone app to create SWF files from MXML files. The resulting SWFs can be hosted on any web site (or not hosted at all); no server license is required. Flex Builder 2 can be downloaded by itself and is built on top of the Eclipse platform. There is still a per-CPU licensed server component for Flex 2 used to facilitate communication between Flex apps and backend systems.
XML in ActionScript 3.0
The remainder of this chapter will explore several different ways of working with XML documents using Flex 2 and ActionScript 3.0. ActionScript 3.0 is supported by Flash Player 9 and the forthcoming Flash 9 development environment in addition to Flex 2 (At the time of writing, an alpha version of Flash Professional 9 was available for download from http://labs.adobe.com. According to that web site, the sole purpose of this alpha release is to enable the creation of Flash apps using ActionScript 3.0..). Although ActionScript 2.0 is now quite prevalent, by the time this tutorial is printed, that may no longer be the case. Because Flex 2's file format is XML-based, it seems a better fit, both with the subject of this tutorial and its intended audience. Plus, using Flex 2 means that the examples can be compiled using a free download, which isn't the case if we were to use the normal Flash authoring tool.[]
[
] Although there are free trial versions available of Flash Professional and Flash Standard, that's not the same thing as just free.
To demonstrate these features, we will build a simple app that views the tutorial data used throughout this chapter. In addition to viewing a list of tutorials, users of this app will be able to add new tutorials. Finally, to show off the ability to push XML to Flash, we'll display the current date and time according to the server. Figures 13-12 and 13-13 contain screenshots of the app in use. Example 13-15 contains MXML to create the various form components. As with some of the Swing code from prior chapters, it's not necessary to understand these components completely. In most cases, the meanings are self-evident.
Flex app list page

Flex app form page

Example Form components in MXML
<?xml version="1.0" encoding="utf-8"?> <mx:app xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:Script> <![CDATA[ // our ActionScript will go here ]]> </mx:Script> <mx:Canvas x="10" y="10" > <mx:TabNavigator > <mx:Canvas label="Current tutorial List"> <mx:Button x="65" y="221" label="Load Books"/> <mx:DataGrid x="29" y="10" > <mx:columns> <mx:DataGridColumn headerText="Title"/> <mx:DataGridColumn headerText="Author"/> <mx:DataGridColumn headerText="Publication Date"/> </mx:columns> </mx:DataGrid> </mx:Canvas> <mx:Canvas label="Add a Book" > <mx:Label x="10" y="69" textAlign="right"/> <mx:TextInput x="156" y="65" /> <mx:Label x="10" y="116" textAlign="right"/> <mx:TextInput x="156" y="114" /> <mx:Label x="439.5" y="26" /> <mx:DateChooser x="400" y="65" /> <mx:Button x="156" y="203" label="Add Book" enabled="false" /> </mx:Canvas> </mx:TabNavigator> <mx:Canvas y="300" > <mx:Label x="345" y="10" /> <mx:Text x="430" y="10" textAlign="left"/> </mx:Canvas> </mx:Canvas> </mx:app> |
E4X
ECMAScript for XML (E4X) is an extension to the ECMAScript standard upon which ActionScript and JavaScript are based. E4X provides native support for XML documents within ECMAScript. Creating a new document with E4X can be done with code such as:
var doc=<content> <element attribute="1"> <child>Here's some child text</child> </element> <element attribute="2"> <child>Here's some more child text</child> </element> </content>
In ActionScript, it's preferable to include the type definition, so the first line above should really be:
var doc:XML=<content>
Accessing child elements uses a dot notation, such as:
doc.element[0].child;
Attributes are accessed using the @ symbol:
doc.element[0].@attribute;
This access can be used to read values from a document object or update the document:
doc.element[1].child = "Changing the child text. ";
Finally, in addition to the index-based access shown above, XPath expressions can be used:
doc.element[@attribute="2"].child = "Changing the child text when attribute = 2";
Parsing is done with the XML constructor:
var docString:String = "<content><element><child>text</child></element></content>"; var doc:XML = new XML(docString);
E4X in FirefoxIn addition to ActionScript, E4X has been implemented in the JavaScript engine used in the Mozilla Firefox browser. Firefox versions since 1.5 have contained E4X functionality. However, to use E4X in JavaScript, you must use a special type attribute to the script tag: <script type="text/javascript; e4x=1"> As of the time of writing, Microsoft had not announced plans to include E4X support in any future version of Internet Explorer. |
More information on E4X in general can be found on ECMA's web site: http://www.ecma-international.org/publications/standards/Ecma-357.htm.
XML data providers
ActionScript and Flex make it easy to bind the content of controls to portions of an XML document. In Example 13-15 above, we have a DataGrid control with three columns, which correspond to the child elements in our XML content. As a result of this correspondence, we can automatically populate the DataGrid control with XML content. In this case, we create a bindable list object and set the DataGrid control's dataProvider property with the list object's name:
// these two lines go inside <mx:script> tags [Bindable] private var gridData:ArrayCollection = new ArrayCollection( ); <!-- this is the updated <mx:DataGrid> control --> <mx:DataGrid x="29" y="10" dataProvider="{gridData}"> <mx:columns> <mx:DataGridColumn headerText="Title" dataField="title"/> <mx:DataGridColumn headerText="Author" dataField="author"/> <mx:DataGridColumn headerText="Publication Date" dataField="pubdate"/> </mx:columns> </mx:DataGrid>
Note that we also told each column what XML element to get its content from.
Sending and loading XML
ActionScript contains a generic URLLoader class that can be used to load data from an URL. There's a related class named URLRequest, which encapsulates all the parameters of an HTTP request. As with the XMLHttpRequest object discussed earlier in this chapter, it's possible to use URLRequest and URLLoader to retrieve XML data as well as to post XML data in the body of a request. In the sample app, we'll use these classes twice. First, when a user clicks on the "Load Books" button, we need to request a URL and add each tutorial defined in the resulting XML into the list we created above. Because we will be reusing the URL, we define it as a variable:
private var serviceURL:String = "http://localhost:8080/ch13-flex/books";
Then we create a loadBooks( ) function to be called when the user clicks the "Load Books" button:
function loadBooks( ) : void { // remove anything from our grid data gridData.removeAll( ); // load the data var loader:URLLoader = new URLLoader( ); loader.addEventListener("complete", getCompleteListener); loader.load(new URLRequest(serviceURL)); } <!-- modified <mx:Button> control --> <mx:Button x="65" y="221" label="Load Books" click="loadBooks( )"/>
You can see that, like XMLHttpRequest in asynchronous mode, URLLoader needs an event listener to be notified when the request is complete. But whereas XMLHttpRequest has a single function set as its onreadystatechange property, URLLoader can have different functions assigned to each of the six events it defines. These events are listed in Table 13-2. Each event has a name and an event class associated with it. An instance of this class is passed to the defined function. In the case of the complete event, the event class is the generic flash.events.Event class. So, our getCompleteListener( ) function looks like this:
function getCompleteListener(event:Event) : void { // cast the event target to a URLLoader object var loader:URLLoader = URLLoader(event.target); parseBookDoc(new XML(loader.data)); }
I've separated the parsing of the document from the listener function so that it can be reused later. In parseBookDoc( ), we simply loop through the book elements in the XML document and add each one to the gridData object.
function parseBookDoc(docXML:XML) : void { gridData.removeAll( ); for (var i:String in docXML.book) { gridData.addItem(docXML.book[i]); }
Table 13-2. URLLoader events
Event name | Event class |
---|---|
complete |
flash.events.Event |
httpStatus |
flash.events.HTTPStatusEvent |
ioError |
flash.events.IOErrorEvent |
open |
flash.events.Event |
progress |
flash.events.ProgressEvent |
securityError |
flash.events.SecurityErrorEvent |
The second place we use URLLoader is to post new tutorial information as XML to the server. We first assemble the XML document using E4X. As above, we create a function to be called upon a push of the "Add Book" button:
function addBook( ) : void { var newBookXML:XML = <book /> newBookXML.title = title.text; newBookXML.author = author.text; var pickedDate:Date = pubdate.selectedDate; if (pickedDate == null) pickedDate = new Date( ); newBookXML.pubdate = pubdateFormatter.format(pickedDate); var request:URLRequest = new URLRequest(serviceURL); request.method="POST"; request.contentType="text/xml"; request.data = newBookXML.toXMLString( ); var loader:URLLoader = new URLLoader( ); loader.addEventListener("complete", postCompleteListener); loader.load(request); } function postCompleteListener(event:Event) : void { var loader:URLLoader = URLLoader(event.target); parseBookDoc(new XML(loader.data)); } <!-- updated <mx:Button> control --> <mx:Button x="156" y="203" label="Add Book" enabled="false" click="addBook( )"/>
Using Flash XML sockets
Our final bit of XML is opening a socket to the server and listening for XML documents. In this case, these documents will contain the current date and time, like:
<current> <date>August 25, 2006</date> <time>6:00:03 PM EDT</time> </current>
Our app needs to open the socket on startup and update the display every time it receives a new document from the server. We could use URLLoader to accomplish something similar, by regularly polling the server for an XML document. However, by using a persistent socket connection, the server is able to control when and how frequently documents are sent. To facilitate the case of sending and receiving XML documents over a persistent socket connection, ActionScript includes a class named XMLSocket. Before we can use XMLSocket, we have to write a socket server. The class in Example 13-16 is not the best implementation of a server, but it will do for our purposes. If you were going to deploy this type of server for real use, you would want a multithreaded server. For more information on writing socket servers, see Java Network Programming by Elliotte Rusty Harold (Oracle).
Example Simple socket server
package javaxml3; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; import java.text.DateFormat; import java.util.Date; import org.jdom.Document; import org.jdom.Element; import org.jdom.output.XMLOutputter; public class DateTimeSocketServer extends Thread { private Document currentDocument; private Element dateElement; private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG); private XMLOutputter outputter; private int port; private Element timeElement; private DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.LONG); public DateTimeSocketServer(int port) { this.port = port; outputter = new XMLOutputter( ); currentDocument = new Document( ); Element root = new Element("current"); currentDocument.setRootElement(root); dateElement = new Element("date"); root.addContent(dateElement); timeElement = new Element("time"); root.addContent(timeElement); } public void run( ) { try { ServerSocket server = new ServerSocket(port); System.out.println("Listening for connections on port " + server.getLocalPort( )); while (true) { try { Socket socket = server.accept( ); BufferedWriter out = new BufferedWriter( new OutputStreamWriter(socket.getOutputStream( ))); while (!socket.isClosed( )) { String current = updateCurrentXML( ); System.out.println("sending XML"); out.write(current); // must end on a zero byte for Flash to see this // as a complete document out.write(0); out.flush( ); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } catch (IOException e) { System.err.println("Exception while accepting: " + e.getMessage( )); } } } catch (BindException e) { System.err.println("Could not start server. Port Occupied"); } catch (IOException e) { System.err.println(e.getMessage( )); } } private String updateCurrentXML( ) { Date date = new Date( ); dateElement.setText(dateFormat.format(date)); timeElement.setText(timeFormat.format(date)); return outputter.outputString(currentDocument); } public static void main(String[] args) { int port; try { port = Integer.parseInt(args[0]); } catch (Exception ex) { port = 8900; } Thread t = new DateTimeSocketServer(port); t.start( ); } } |
The key thing here is to output a zero byte following our XML document. If we do not do this, Flash will never know the document is complete. To connect to the server when the app is loaded, we create an init( ) method and reference it in the mx:app tag:
<mx:app xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init( )"> private var xmlsocket:XMLSocket = new XMLSocket( ); function init( ) : void { xmlsocket.addEventListener("connect", onXMLConnect); xmlsocket.addEventListener("data", onXMLData); xmlsocket.connect("localhost", 8900); }
As with URLLoader, we need to create event listeners. XMLSocket and URLLoader share many of the same events. The events available for XMLSocket are listed in Table 13-3.
Table 13-3. XMLSocket events
Event name | Event class |
---|---|
close |
flash.events.Event |
connect |
flash.events.Event |
data |
flash.events.DataEvent |
ioError |
flash.events.IOErrorEvent |
securityError |
flash.events.SecurityErrorEvent |
The full MXML source code for this app is contained in Example 13-17.
Example Complete MXML source
<?xml version="1.0" encoding="utf-8"?> <mx:app xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="init( )"> <mx:Script><![CDATA[ import mx.formatters.DateFormatter; import mx.collections.ArrayCollection; private var serviceURL:String = "http://localhost:8080/ch13-flex/books"; [Bindable] private var gridData:ArrayCollection = new ArrayCollection( ); private var pubdateFormatter:DateFormatter = new DateFormatter( ); private var xmlsocket:XMLSocket = new XMLSocket( ); function init( ) : void { pubdateFormatter.formatString = "MMMM YYYY"; xmlsocket.addEventListener("connect", onXMLConnect); xmlsocket.addEventListener("data", onXMLData); xmlsocket.connect("localhost", 8900); } function onXMLConnect(event:Event) : void { serverTime.text = "connected to server"; } function onXMLData(event:DataEvent) : void { var data:XML = XML(event.data); serverTime.text = data.date + " " + data.time; } function loadBooks( ) : void { // remove anything from our grid data gridData.removeAll( ); // load the data var loader:URLLoader = new URLLoader( ); loader.addEventListener("complete", getCompleteListener); loader.load(new URLRequest(serviceURL)); } // this function is called when the data URL has been loaded function getCompleteListener(event:Event) : void { // cast the event target to a URLLoader object var loader:URLLoader = URLLoader(event.target); parseBookDoc(new XML(loader.data)); } function parseBookDoc(docXML:XML) : void { gridData.removeAll( ); for (var i:String in docXML.book) { gridData.addItem(docXML.book[i]); } } function validateAddForm( ) : void { add.enabled = ((title.text.length > 0) && (author.text.length > 0)); } function addBook( ) : void { disableForm( ); var newBookXML:XML = <book /> newBookXML.title = title.text; newBookXML.author = author.text; var pickedDate:Date = pubdate.selectedDate; if (pickedDate == null) pickedDate = new Date( ); newBookXML.pubdate = pubdateFormatter.format(pickedDate); var request:URLRequest = new URLRequest(serviceURL); request.method="POST"; request.contentType="text/xml"; request.data = newBookXML.toXMLString( ); var loader:URLLoader = new URLLoader( ); loader.addEventListener("complete", postCompleteListener); loader.load(request); } function postCompleteListener(event:Event) : void { var loader:URLLoader = URLLoader(event.target); parseBookDoc(new XML(loader.data)); reenableForm( ); } function disableForm( ) : void { title.enabled = false; author.enabled = false; pubdate.enabled = false; add.enabled = false; load.enabled = false; } function reenableForm( ) : void { load.enabled = true; title.enabled = true; author.enabled = true; pubdate.enabled = true; title.text = ""; author.text = ""; } ]]></mx:Script> <mx:Canvas x="10" y="10" > <mx:TabNavigator > <mx:Canvas label="Current tutorial List"> <mx:Button x="65" y="221" label="Load Books" click="loadBooks( )"/> <mx:DataGrid x="29" y="10" dataProvider="{gridData}"> <mx:columns> <mx:DataGridColumn headerText="Title" dataField="title"/> <mx:DataGridColumn headerText="Author" dataField="author"/> <mx:DataGridColumn headerText="Publication Date" dataField="pubdate"/> </mx:columns> </mx:DataGrid> </mx:Canvas> <mx:Canvas label="Add a Book" > <mx:Label x="10" y="69" textAlign="right"/> <mx:TextInput x="156" y="65" change="validateAddForm( )"/> <mx:Label x="10" y="116" textAlign="right"/> <mx:TextInput x="156" y="114" change="validateAddForm( )"/> <mx:Button x="156" y="203" label="Add Book" enabled="false" click="addBook( )"/> <mx:Label x="439.5" y="26" /> <mx:DateChooser x="400" y="65" /> </mx:Canvas> </mx:TabNavigator> <mx:Canvas y="300" > <mx:Label x="345" y="10" /> <mx:Text x="430" y="10" textAlign="left" /> </mx:Canvas> </mx:Canvas> </mx:app> |
Cross-Domain Access
By default, the Flash Player has a fairly strict rule defining what network access is allowed: a Flash movie can only make network requests to the host with the exact same domain name from which it was loaded. If a Flash movie is loaded from http://www.example.com/flash/movie.swf, for example, it could request http://www.example.com/feed, but not http://www.othersite.com/feed or even http://data.example.com/feed. To allow access to these other hosts, a cross-domain policy file must be created. This policy file is called crossdomain.xml and is located on the host to which Flash needs to be granted access. If you had a data feed on http://www.othersite.com and wanted to allow access to Flash movies loaded from www.example.com, this policy file would need to be downloadable from http://www.othersite.com/crossdomain.xml:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="www.example.com" /> </cross-domain-policy>
Multiple allow-access-from-domain elements are permitted. In addition, you can use a wildcard to allow access to Flash movies loaded from any subdomain of example.com:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*.example.com" /> </cross-domain-policy>
Using the wildcard, you can allow access to Flash movies loaded from any domain:
<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*" /> </cross-domain-policy>
For more information on cross-domain policy files, see http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=tn_14213. There is much more to learn about the technologies discussed in this chapter. Hopefully it has provided you a taste of where you can use XML in the presentation tier of your web apps, as well as some pointers to where you can find more information on each of the topics.