Put JAX-RS to work with Adobe Flex

January 13th, 2010 by Henri Bezemer

In my previous post I’ve shown how to build a simple set of RESTful services with JAX-RS, and how to access those services from a XForms client. That example also showed the RESTful principle of HATEOAS. In this post I’ll show how to build a Adobe Flex 3 client to those services.

The Flex SDK

Adobe has been kind enough to make the Flex SDK available for free. The SDK provides you with the basic tools like the compiler. The compiler can be used at the command line or via a set of Ant tasks. The SDK does not provide an IDE. If you want an IDE to develop Flex applications, you should consider buying the Adobe Flex Builder. For a small project, the Flex SDK in combination with a good editor will be adequate, especially one with support for XML and Ant (such as Eclipse or NetBeans).

Download and install (unzip) either the Open Source Flex SDK or the Adobe Flex SDK. The Adobe Flex SDK contains a debug version of the Flash player. The biggest benefit of replacing your regular version of the Flash player with this one is that you’ll see a “stack trace” when your program aborts, where the regular version remains silent. I’d suggest that you install the debug version, since it’s a snap to reinstall the regular version anyway.

If you’re interested in learning to write Flex applications, I suggest that you buy the book Flex 3 in Action (Manning), which I think is a pleasant and complete book for people with programming experience.

The customers resource

In this post I’ve introduced the customers RESTful resource. Please take a look at the XML content representing the resource. Below is a shortened version:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<customers xmlns="http://www.zienit.com/customer" xmlns:ns2="http://www.w3.org/2005/Atom">
	<customer id="1">
		<first-name>Roland</first-name>
		<last-name>Langston</last-name>
		<street>746 Hill Avenue</street>
		<city>Fabens</city>
		<state>HI</state>
		<zip>87743</zip>
		<ns2:link type="application/xml" href="http://localhost:8080/simplerest/customers/1" rel="self" />
	</customer>
...
	<ns2:link type="application/xml" href="http://localhost:8080/simplerest/customers?start=0&size=10" rel="previous" />
	<ns2:link type="application/xml" href="http://localhost:8080/simplerest/customers?start=20&size=10" rel="next" />
</customers>

Deploy the simplerest war file to an application server like GlassFish. The resource will then be available at an URI like http://localhost:8080/simplerest/customers.

Ant

After working with Maven for some time, working with Ant feels a bit like a trip down memory lane. It turns out that invoking the Flex compiler from Ant is such a simple undertaking that I didn’t want to invest time to further investigate the Maven alternatives for Flex. Here is the build.xml for this project:

<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="main">
	<taskdef name="mxmlc" classname="flex.ant.MxmlcTask" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar"/>
    <description>simple Flex project</description>
	<target name="main">
	    <mxmlc
	    	file="${basedir}/src/main/flex/list.mxml"
	    	output="${basedir}/target/list.swf">
	        <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
	        <source-path path-element="${FLEX_HOME}/frameworks"/>
	    </mxmlc>
	</target>
</project>

Note that you have to set up an environment variable called FLEX_HOME. It must point to the directory where you installed the Flex SDK. If you are running Ant from Eclipse, you can get away with adding a property in Eclipse (Windows->Preferences->Ant->Runtime->Properties).

I’ve tried to follow the basic Maven project structure, so the source code must be in /src/main/flex and the product of compilation (a *.swf or ’swiff’ file) goes to /target.

MXML

The simplest Flex applications, like this example, consist of only a single MXML file. As the name suggests, this is basically an XML file. You get a bit of extra support from your IDE if you associate the extension *.mxml with the XML editor. MXML is mixed with ActionScript code (an ECMAScript derivitive), which unfortunately does not get any syntax coloring treatment from Eclipse. Here is the application’s source code in list.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	layout="absolute" creationComplete="init()"
>
    <mx:Script>
        <![CDATA[
            private namespace m = "http://www.zienit.com/customer";
            use namespace m;
            private namespace atom = "http://www.w3.org/2005/Atom";
            use namespace atom;

			[Bindable]
			private var resource:String;

			[Bindable]
			private var has_prev:Boolean;
			[Bindable]
			private var has_next:Boolean;

			private function init():void {
				resource =  Application.application.parameters.simplerest+"/customers";
				simplerest.send();
			}

			private function next():String {
				return simplerest.lastResult.atom::link.(@rel=='next').@href;
			}

			private function prev():String {
				return simplerest.lastResult.atom::link.(@rel=='previous').@href;
			}

			private function go_next():void {
				resource = next();
				simplerest.send();
			}

			private function go_prev():void {
				resource = prev();
				simplerest.send();
			}

			private function done():void {
				has_prev = (prev() != "");
				has_next = (next() != "");
			}

			private function displayName( row:Object, column:DataGridColumn ):String {
      			return row.m::['first-name'] + ' ' + row.m::['last-name'];
      		}

			private function displayStreet( row:Object, column:DataGridColumn ):String {
      			return row.m::street;
      		}

			private function displayCity( row:Object, column:DataGridColumn ):String {
      			return row.m::city;
      		}

			private function displayZip( row:Object, column:DataGridColumn ):String {
      			return row.m::zip;
      		}
        ]]>
    </mx:Script>
	<mx:HTTPService id="simplerest"	url="{resource}" resultFormat="e4x" result="done()"/>
	<mx:Panel width="100%" height="100%" layout="absolute" title="{resource}">
		<mx:VBox width="100%" height="100%">
			<mx:DataGrid width="100%" height="250" id="datagrid" dataProvider="{simplerest.lastResult.m::customer}">
				<mx:columns>
					<mx:DataGridColumn headerText="Name" labelFunction="displayName"/>
					<mx:DataGridColumn headerText="Street" labelFunction="displayStreet"/>
					<mx:DataGridColumn headerText="City" labelFunction="displayCity"/>
					<mx:DataGridColumn headerText="Zip" labelFunction="displayZip"/>
				</mx:columns>
			</mx:DataGrid>
			<mx:ControlBar height="50">
				<mx:Button label="Previous" click="go_prev()" visible="{has_prev}"/>
				<mx:Button label="Next" click="go_next()" visible="{has_next}"/>
			</mx:ControlBar>
		</mx:VBox>
	</mx:Panel>
</mx:Application>

Let’s take a closer look at this file. To understand what’s going on, consider the xml structure of the file to represent a hierarchy of visual components and consider the action script to handle events coming from the visual components and to manipulate properties of the visual components.

The outermost component is the Application container. It triggers an event creationComplete, which it sends to a function init (line 3). Lets skip the script part for now and look at the other components. The Panel, VBox and ControlBar are mainly there to layout the other components. The DataGrid will be used to show the list of customers in a tabular format. The DataGridColumn’s describe the grid’s individual columns. The two Button’s will be used to fetch and display the previous or next list of customers, following the HATEOAS design of the customers resource.

A central role is played by the HTTPService component. This is not a visual component, but a component that provides data to any of the other components (this construct is not at all different from what we got with Visual Basic 3 back in 1993!). The HTTPService will perform a HTTP get against the given url and will hold the response. The DataGrid binds to the data provided by HTTPService through an expression in the attribute dataProvider (line 68).
This expression is in E4X (ECMAScript for XML) syntax. E4X provides an intuitive way to access XML data. However, namespaces and element names which are legal in XML but illegal in ECMAScript (such as ‘first-name’) require some syntactic sugar.

I think that programming in Flex is quite fun because of the expressive power that you get with events and binding.

HTML wrapper

After succesfull compilation we end up with a ’swiff’ called list.swf. It needs a HTML container in order to run in your browser. Here’s a very basic HTML wrapper:

<html>
	<head>
		<title>Customers</title>
	</head>
	<body>
		<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="500" height="350">
			<param name="movie" value="list.swf" />
			<param name="FlashVars"
		value="simplerest=http://localhost:8080/simplerest" />
		</object>
	</body>
</html>

This HTML wrapper is only valid for IE. The other browser use the element embed. Note that there is an additional parameter “FlashVars” which will be made available to the Flex application by the runtime. This parameter is a container for one or more other parameters, in our example it contains a parameter called simplerest which is set equal to the “root” of the customer resource’s URI. You can change it if you’ve deployed it to a different URI. To start the application, you can simple open this HTML file in your browser straight from the file system.

crossdomain.xml

Flex provides a simple yet effective security mechanism, which we need to give some extra instructions. This is needed because the Flex app is loaded from one location (namely the file system) while the XML data will be loaded from another location (namely localhost). Flex doesn’t allow that, unless you put a file called crossdomain.xml at the root of the other location. If you’re using GlassFish, you must put this file in /domains/domain1/docroot (or something similar if you’ve named your domain differently). The contents of crossdomain.xml are as follows:

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

This crossdomain.xml allows access to this host (localhost) to Flex applications loaded from any host. This is very generous and not something you do outside of any test environment.

Thanks for reading and lots of fun with Flex!

One Comment

1

[...] Dit blogartikel was vermeld op Twitter door pieter wolthoorn, Henri Bezemer. Henri Bezemer heeft gezegd: How to invoke JAX-RS RESTful service from Adobe Flex 3.0 application http://bit.ly/8qRKei #Java #JAX-RS #Jersey #GlassFish #Flex [...]