Jose Sandoval Google
 Resume     Book     Software     Drawings     Home     Subscribe to RSS feed Search Web Search josesandoval.com

RESTful Flex/Flash client
Saturday, December 26, 2009

This week I had to program in AS3. I didn't code from scratch the application I worked on; however, I designed the architecture, so I was familiar with the source and felt confident to jump in to do updates. In the process, I was introduced to the default Flash development environment--CS4.

Coming from a world of Vim, Eclipse, NetBeans, and Visual Studio, I found the IDE lacking in functionality. True enough, I can compile code and export everything into SWFs files. But what about the little extras that make developing software fun? Where's the vi plugin? Where's the build file? What about code completion?

A couple of Flash developers I know suggested I try Flash Builder, which is based on Eclipse. I downloaded it and coded my first application--a RESTful Flex client. My first application, however, is not a full AS3 app; it's a Flex app coded in a language that is a hybrid of XML and AS3.

The application, which I call TwitterFlex, looks as follows:



The running version is here TwitterFlex: you click the button and it connects to Twitter's REST API to retrieve the latest 20 public updates.

Let me break down the code in 3 sections--XML stuff, AS3 code, and UI logic--because I think most Flex application will have the same code structure that my toy example has.

XML Stuff: the web service connection
Connecting to web services through HTTP is such a common requirement that the Flex API already includes code to do just that.

Creating an HTTP call that connects to Twitter is done with the following XML code:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
viewSourceURL="srcview/index.html">

(1) <mx:HTTPService
(2) id="RESTService"
(3) url="json.jsp"
(4) resultFormat="text"
(5) result="onLoadTweetsResult(event)"
(6) fault="onLoadTweetsFault(event)"
(7) showBusyCursor="true">
(8) </mx:HTTPService>
If you've seen XML files before, the first line should look familiar: with the mx directive we're telling whatever will parse this file that we are using Adobe's http://www.adobe.com/2006/mxml package.

Line (1) instantiates an HTTPService object; in line (2) I give the instance an id of RESTService. In line (3) I set the URL value of json.jsp (because of cross domain issues, I need to call a local pass through to talk to Twitter--this is a simple JSP and the code is at the end of this entry). Lines (5) and (6) point to the event handlers of the HTTP responses, with (5) handling success and (6) handling failure.

Note that I have a service instantiated, but I haven't connected to it yet. I leave the connection to the service when the user clicks a button (see the UI section below).

AS3 Code: the <mx:Script></mx:String> tag
With the ability to make web service calls, I need to program the event handlers of the HTTP responses and other functions that are needed for user interaction or logic that needs to be performed as part user requests. AS3 code is enclosed in the XML element <mx:Script>. The AS3 code in my application looks as follows:
<mx:Script>
<![CDATA[
import mx.controls.dataGridClasses.DataGridColumn;
import mx.messaging.AbstractConsumer;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.collections.ArrayCollection;
import com.adobe.serialization.json.JSON;

[Bindable]
private var tweets:ArrayCollection;

private function loadTweets():void {
RESTService.send();
}

private function
onLoadTweetsResult(event:ResultEvent):void {
var rawJSON:String = String(event.result);
var arrayJSON:Array = JSON.decode(rawJSON) as Array;
tweets = new ArrayCollection(arrayJSON);
}

private function
onLoadTweetsFault(event:FaultEvent):void {
trace(event.fault.toString());
}

private function
getScreenName(tweet:Object,
column:DataGridColumn):String {
return tweet.user.screen_name;
}

private function
getName(tweet:Object,
column:DataGridColumn):String {
return tweet.user.name;
}
]]>
</mx:Script>
The import statements should be obvious. Next, however, is this [Bindable] statement just above the tweets variable. As per Adobe's documentation, this metatag is an event listener hook that updates anything using the instance of the data with a message saying that the original copy changed. In short, [Bindable] makes tweets a global variable.

Next, comes the loadTweets() function, which tells the web service I defined earlier to run by executing the send() method of the HTTPService object.

Handling of the HTTP responses is delegated to the onLoadTweetsResult() and onLoadTweetsFault() methods. The former, is where Twitter's JSON object is parsed using a JSON library that is available for download. Before you can use it, first download it and then add it to your Flex Builder's project library (I thought it was a default Adobe package, but it's not--let me save you some time here).

Finally, the last 2 methods, getScreenName() and getName(), return the value of the fields in the JSON object that I use in the UI components of the app, which I cover next.

The UI
The last portion of the code is the UI of the application. I won't cover the details of every XML tag available, because there are already many examples of this out there. What's more, I only use 5 UI elements: a VBox, a Label, a DataGrid, a DataGridColumn, and a Button. My UI, in code, looks as follows:
<mx:VBox 
width="100%"
height="50%"
paddingBottom="60"
paddingLeft="60"
paddingRight="60"
paddingTop="60">

<mx:Label
text="RESTful Flex/Flash client (jose@josesandoval.com)"
fontSize="24"
fontWeight="bold" />

(1) <mx:DataGrid
dataProvider="{tweets}"
width="100%"
rowCount="12">

(2) <mx:columns>
(3)<mx:DataGridColumn
width="200"
headerText="Screen Name"
labelFunction="getScreenName" />

(4) <mx:DataGridColumn
width="200"
headerText="Name"
labelFunction="getName" />

(5) <mx:DataGridColumn
headerText="Tweet"
dataField="text" />

</mx:columns>
</mx:DataGrid>

<mx:Button
label="Get Tweets"
click="{RESTService.send()}" />

</mx:VBox>
</mx:Application>
The only lines I will cover in detail are numbered. Everything above and below them is obvious.

Line (1) instantiates a DataGrid object that is provided in the code space mx. The tag's element dataProvider="{tweets}" is passing the grid object the tweets global variable I instantiated in the AS3 code--you can see how the event dispatching makes sense in the context of the application: if the state of tweets changes, every component that is using it has to be notified.

Lines (3) and (4) define columns in the grid. The tag's element labelFunction is telling the instance of the particular column that it needs to run the function named in the element's value. For example, getScreenName calls the function coded earlier getScreenName() and getName calls getName(). If you look at the functions above, you see that I'm accessing the user element of the parsed JSON object.

What about the variable column:DataGridColumn in the method's signature? Well, that's another callback registration that it's telling the code that it will be used in an object of type DataGridColumn somewhere while executing.

And finally, line (5) doesn't use a function callback. Because the object tweets has been brought into the scope of the loop for the DataGrid object, I can access a tweet's element directly and therefore I use dataField="text", where text is a member of the instance of the global tweets.

Final Words
This XML and AS3 code hybrid is the next evolution of computer languages. Flex is a compilable meta-language, with AS3 scripting capabilities, that lets us take advantage of the ubiquity of the Flash player. Flex apps are Flash apps and will run on any browser that has a Flash player installed.

Even though I'm liking coding in Flash and this new meta-computer language, I wonder why we insist in recreating all the functionality of the web browser in Flash applications? Flash is cool and all, but we can do most of what it does in plain HTML and JavaScript code.



If you want to see the whole listing in one place, Flex gives you the option of attaching the source code to your deployed applications. The source for this app is here TwitterFlex/srcview/index.html, which you can also access by right-clicking on the application and then selecting "View Source."

json.jsp
I mentioned earlier that I use a JSP file to serve as a proxy to talk Twitter from the hosting server. This is because you can't make direct calls from a Flash app to Twitter unless you are a registered user. I don't have a developer's API key, and for this example I still want to use the public stream. The JSP file looks as follows:
<%@ page contentType="application/json; 
charset=UTF-8" %>
<%@ page import="java.io.BufferedReader,
java.io.IOException,
java.io.InputStreamReader,
java.net.MalformedURLException,
java.net.URL,
java.net.URLConnection" %>
<%
try {
URL twitter =
new URL(
"http://twitter.com/statuses/public_timeline.json");
URLConnection tc = twitter.openConnection();
BufferedReader in =
new BufferedReader(
new InputStreamReader(tc.getInputStream()));
String line;

while ((line = in.readLine()) != null) {
out.println(line);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
in.close();
}
%>
I open a connection and make an HTTP GET call. I then stream the result back to the caller of the JSP, but I set the data stream to have a MIME type of application/json.


11:31 AM | 2 comment(s) |

Comments:

Unfortunately, Flex/Flash HTTP implementation has a lot of limitations for REST you didn't touch on; it has success/failure handlers, but doesn't let you get access to the return code or body in the case of a 'failure', making lots of rest responses hard to parse.

It will only let you do GET and POST unless you use a proxy to access DELETE, TRACE, PUT. It limits your access to headers like authentication.

Ultimately, once you get past the simplest of cases with Flex and REST, you'll find yourself painted into a corner.


Geoffrey, you're right. I didn't look at the other 3 HTTP requests--PUT, POST, DELETE.

These are client limitations that actually translated into some server REST framework implementations. Some frameworks were too browser centric and got away with doing PUT and DELETE requests by faking them with hidden parameters inside of GET and POST requests.



This page is powered by Blogger. Isn't yours?

Guestbook
© Jose Sandoval 2004-2009 jose@josesandoval.com