|
Table of Contents
|
Flex builder
Flex builder is an Eclipse based IDE for Flex, unfortunately not free (excepted for students). So Java developers will not be disoriented with it. The last version is Flex builder 3.
Tools
It provides some useful tools, such as:
- debugger
- WYSIWYG designer
- organize imports
- word completion
- refactoring (since the version 3)
- profiling (since the version 3)
These tools are not really as powerful as they are for Java, but it is quite nice to develop with a such environment.
Problem
The compiler was not able to find a file named localFonts.ser. The solution I found was to go into the specified directory, where there are two corresponding files: winFonts.ser and macFonts.ser. I am using Windows so I decided to copy, past, and rename the winFonts.ser into localFonts.ser. It worked.
MXML
General points
MXML files have the *.mxml extension. This language describes graphical user interfaces. Each component corresponds to an AS class, so it is easy to create or customize components, also to control them from AS scripts.
There are two views in Flex builder to develop MXML files:
- source: simple but effective
- design: WYSIWYG with drag nd drop
Style
It is possible, and recommended to use CSS styles to customize MXML screens. The assets as the same as for HTML:
- lightness
- flexibility
- reusability
CSS: style.css
myClass { background-color: red; } TextInput { font-family: Arial; font-size: 10pt; }
MXML
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Style source="style.css"/> <mx:Button styleName="myClass"/> <mx:TextInput/> </mx:Application>
ActionScript
AS files have the *.as extension. This object-oriented language is based on ECMAScript, like JavaScript, but with data types.
AS and MXML
Here are some examples of how to connect AS and MXML.
AS code inside MXML
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="init();"> <mx:Script> <![CDATA[ [Bindable] private var value: Number; private function init(): void { value = 1; } private function inc(): void { value++; } ]]> </mx:Script> <mx:Text text="{value}"/> <mx:Button click="inc();"/> </mx:Application>
In this way, it is possible to write AS code inside the MXML code, as in a class body: declaring attributes or functions.
Including AS code from a file
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script source="ActionScriptFile.as"/> </mx:Application>
Included files must be written as specified in the previous part, that is to say, as in a class body.
Calling AS classes
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ import myPackage.MyClass; private var myClass: MyClass = new MyClass(); ]]> <mx:Script/> </mx:Application>
Setters and getters
Contrary to Java, getters and setters are native in this language. Here is an example:
public final class Bean { private var _foo: Object; // Getter public function get foo(): Object { return _foo; } // Setter public function set foo(foo: Object): void { _foo = foo; } public static function example(): void { var bean: Bean = new Bean(); // Getter var o: Object = bean.foo; // Setter bean.foo = o; } }
The name of the attribute must be different of getters and setters, so I use to prefix private attributes with an underscore.
HTTP requests and responses
HTTP requests
The HTTPService object sends asynchronous HTTP requests. The only accepted values for the content type seems to be HTTPService.CONTENT_TYPE_XML (application/xml) and HTTPService.CONTENT_TYPE_FORM (application/x-www-form-urlencoded), but it did not work. The solution is simply to use the content type text/xml. It works, but only with the AS HTTPService object ; it also exists in MXML but this content type value is not valid in respect to the DTD or Schema.
var http: HTTPService = new HTTPService(); http.resultFormat = HTTPService.RESULT_FORMAT_XML; http.request = new XML(<requete/>).toXMLString(); http.contentType = "text/xml"; http.url = "http://localhost:8080/TelosysServer/action"; http.headers = "Cookie: JSESSIONID=" + sessionID; http.addEventListener(ResultEvent.RESULT, success); http.addEventListener(FaultEvent.FAULT, failure); http.send();
It is also possible to give the JSESSIONID in the URL rather than in HTTP request headers:
http.url = "http://localhost:8080/TelosysServer/action?JSESSIONID=" + sessionID;
HTTP responses
function success(evt: ResultEvent): void { http.removeEventListener(ResultEvent.FAULT, success); http.removeEventListener(FaultEvent.FAULT, failure); // HTTP response body trace(event.result); }
function failure(evt: FaultltEvent): void { http.removeEventListener(ResultEvent.FAULT, success); http.removeEventListener(FaultEvent.FAULT, failure); // HTTP response error trace(event.fault.faultDetail); }
But it is also possible to handle the two types of events in a single function:
http.addEventListener(ResultEvent.RESULT, response); http.addEventListener(FaultEvent.FAULT, response);
function response(evt: Event): void { http.removeEventListener(FaultEvent.FAULT, response); http.removeEventListener(ResultEvent.RESULT, response); if(evt is ResultEvent) { trace((evt as ResultEvent).result); } else if(evt is FaultEvent) { trace((evt as FaultEvent).fault.faultDetail); } }
XML
Thanks to E4X (ECMAScript for XML), it is quite easy to browse XML trees:
| xml.XXX or xml.child("XXX") | XXX child nodes |
| xml.XXX[0] or xml.child("XXX")[0] | First XXX child node |
| xml..XXX | All XXX nodes |
| xml.@XXX or xml.@["XXX"] | XXX attribute of the root node |
| xml.XXX.(@id == X) or xml.XXX.(@["id"] == X) | XXX child nodes where the attribute id = X |
| xml.text() | Text of the root node |
| xml.attributes() | Attributes of the root node |
To construct XML trees, these notations are powerful:
var xml:XML = <xml/>;
xml.appendChild(<XXX/>);
xml.XXX[0].@id = "X";
It is also possible to combine E4X with regular expression: E4X: Predicate Filtering with Regular Expressions.
Binding
The binding consists in connecting two variables. If one changes, the other changes too. The important AS keyword is [Bindable]. By default, MXML components are bindable, but not AS class attributes.
Unidirectional
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ [Bindable] private var value: Number; ]]> </mx:Script> <mx:Text text="{value}"/> </mx:Application>
Bidirectional
Natively, it is not possible to bind two properties bidirectionally, so I wrote the following AS function:
/** Make a binding in the two ways between: * - a graphical component property * - a class attribute (which gets the priority and has to be [Bindable]) * Returns true if the binding is successfull in the two ways */ public static function bind(viewObj: UIComponent, viewProp: String, modelObj: Object, modelProp: String): Boolean { var cwModelToView: ChangeWatcher = BindingUtils.bindProperty(viewObj, viewProp, modelObj, modelProp); var cwViewToModel: ChangeWatcher = BindingUtils.bindProperty(modelObj, modelProp, viewObj, viewProp); return cwModelToView.isWatching() && cwViewToModel.isWatching(); }
Data provider
Moreover, it is easy to fill a graphical component with an Array of data:
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"> <mx:Script> <![CDATA[ [Bindable] private var myData: Array; ]]> </mx:Script> <mx:ComboBox dataProvider="{myData}"/> </mx:Application>
Note: objects stored in the Array must have a toString method.
Some lacks
- No abstract classes or methods
- No surcharge methods
- No genericity
- No private or protected constructors
- No doLater method
- No unused imports warnings
- No eval method
- Basic exceptions mechanism
Telosys server
The original Telosys server has to be a little bit adapted, in order to work with a Flex client application. Here are the adjustments to make:
Session ID
The flash player has a limit: it does not send HTTP responses headers to applications. So it was impossible to get the JSESSIONID from common responses. The best way to avoid this problem is to "ask for" the ID in an HTTP response body.
Here is the code of the JSP page (id.jsp) we put on the server to get the session ID:
<%@page language="java" contentType="text/xml; charset=utf-8"%> <%out.println("<JSESSIONID>" + request.getSession().getId() + "</JSESSIONID>");%>
This page must be called at the startup of the application, in order to get a session ID, and to use it for all HTTP requests.
Security
I had no problem when using a Flex application from a web browser, but it did not work as well with the Flash player alone. The first request was not the one expected.
crossdomain.xml, is a file dedicated to set the permission to access data on a server from a Flash application. So I put the following file on the %Tomcat%/webapps/ROOT directory:
<?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 enables access to the server from all domain names. Now it works as well with web browsers as with the Flash player alone.
But a question still remains: why does it work with web browsers, without a crossdomain.xml file?
Data loading
Data required in screens are compiled in JSP pages. A way to load the data will have be found. Some solutions:
- JSP pages sending back XML
- REST services
Applications
Requester
This small application can be used to test the HTTPService object with a Telosys server containing a page id.jsp giving a session ID.
- /files/Requester.swf - Flash application
- /files/Requester-FlexBuilder3Project.zip - Flex builder 3 project
You can use the Flash application directly. To work with the source code, just import the content of the archive in Flex builder.
Bookstore
This application contains the screen Employee of Bookstore.
- /files/Telosys-FlexBuilder3Project.zip - Flex builder 3 project
As for the requester, you just have to import the content of the archive in Flex builder. Then you must set the server URL in the telosys.Settings class. That is all.
Settings
You must set up the server URL in the Settings class. That's all, but you can also implement a new format for data exchanges, or set the application name.
Model - View
When you launch the application, the process is:
- the view creates the model (which also creates other objects that you will see in the next part)
- the view makes bindings between the model and itself
- finally the view can send actions to the model, and the data are updated automatically thanks to the bindings
Initialization
The application needs to get a session ID from the Telosys server. The model creates a requester (that it will use for the rest of the time), which creates a session, which send the request. When the response arrives, the session uses the IFormat interface to convert the response into a useable object. This conversion process will be the same for the HTTP requests and responses linked to an action.
HTTP request
When an action happens on the view, the model calls the dedicated method of the requester, with a list of parameters (name + value). The requester creates a request object, then converts it into the right format, and finally send it to the server.
HTTP response
The process for the is quite the same as for the request, but with a Response object which is given to the handleRequest function of the model. Then the model uses the data it needs from the Response to update its attributes.
Flex server
There are two ways to deploy Flex applications on a remote server, and to compile them on the fly:
- a web project on a Java EE application server (for example Tomcat)
- an Apache HTTP module
Java EE application server
This solution seems to be the more powerful. You just have to download the Flex 3 compiler module for Java EE application servers, which is available on the Flex 3 SDK page. This module is a WAR that you have to deploy on the server of your choice. Then, there are at least three ways to process:
- MXML files directly
- JSP pages including MXML files
- JSP pages generating MXML code
I tried the first one, putting MXML files at the root of the web project, and AS packages and classes in the WEB-INF/flex/user_classes directory. It works. Now you can set up the behavior: Flex server performance.
Stuff
Java remoting
Unless the aim of a Telosys server is to communicate with clients using XML data, Adobe provides a very interesting technology, free and open source (LGPL v3): BlazeDS.
BlazeDS is the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Adobe® Flex™ and Adobe AIR™ applications for more responsive rich Internet application (RIA) experiences.
The evolution to more engaging RIAs has created the need for better data connectivity options. Remoting simplifies the reuse of existing server logic automatically marshalling calls between the Flash client and the Java methods on the server. In addition, the use of a AMF binary data transfer format increases performance, allowing applications to load data up to 10 times faster than with text-based formats such as XML or SOAP.
AMF (action message format) is a proprietary data format created by Macromedia in order to enable efficient communication between Flash applications and remote servers.
Unit testing
JSON
- Corelib: MD5 and SHA 1 hashing, image encoders, JSON serialization, number and date APIs
- Tutorial: Using JSON with Flex 2 and ActionScript 3





