Invoking the Flex Compiler from Adobe AIR… with Merapi and the Flex Compiler API
As the first step in a larger effort trying to automate my Flex Application development, I have been creating a simple Adobe AIR application that through the AIR bridge from the Merapi Project invoke the Flex compiler.
The implementation is only intended as the first experiment, so currently the use case for the App is a bit farfetched… however, the offspring of this experiment and the potential implications are very strong in terms of workflow optimizations.
Anyways, lets get on with it…
I have to warn you in advance, this is not the simplest possible implementation, but originally it was not my intention to post about it. A better sample would have excluded all the didn’t relate to the central elements which in this case is the Flex Compiler API and getting in touch with it through Merapi. Anyways… after this disclaimer… let’s get on with it…
There are a number of prerequisites in order to get started.
First you need to download and familiarize yourself with the Merapi Project.
Then you need to get the Flex Compiler. The easiest way is to grab it from the Flex SDK which was installed alongside Flex Builder / Flash Builder.
You can find the JAR file in following location:
{Flash Builder Installation Directory}/sdks/{SDK Version}/lib/flex-compiler-oem.jar
On my machine the location looks like this:
Applications/Flash Builder Plugin 4 Beta 1/sdks/4.0.0/lib/flex-compiler-oem.jar
Setting up the appropriate Java project is an exercise I will leave for you to accomplish on your own as out of scope of this post to go through that.
After setting up the project, the first we need to create the Java application which will be running in the background and listening for messages from Adobe AIR.
This application will instantiate the Merapi AIR bridge and through this listen for commands in the form of Compile message types.
It could look something like this…
import java.io.File;
import merapi.Bridge;
import flex2.tools.oem.Application;
public class FxsdkJavAirApplication
{
public static void main(String[] args)
{
Bridge.open();
new CompileHandler();
}
protected static void compile(CompileMessage message)
{
try
{
String inputFile = message.inputFile;
String outputFile = message.outputFile;
Application application = new Application(new File(inputFile));
application.setOutput(new File(outputFile));
application.setProgressMeter(new FxsdkJavAirProgressMeter(message));
long result = application.build(true);
if (result > 0)
{
System.out.println("COMPILE OK");
}
else
{
System.out.println("COMPILE FAILED");
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
The message which will be using to pass through the bridge is called CompileMessage and inherits from Merapi’s message class and we are using the Application class from the Compiler API to actually do the building of the SWF from the MXML file.
import merapi.messages.Message;
public class CompileMessage extends Message
{
public static final String COMPILE = "compile";
public String inputFile = null;
public String outputFile = null;
public int progress;
public CompileMessage()
{
super();
}
}
Seeing that I copied most of the Java code from the HelloWorld sample of the Merapi project, the calling back to the Adobe AIR application is implemented in the ProgressMeter seeing that this was a very easy and convenient way of getting the callback to function. It should not be regarded as a recommended way of doing it, this is merely an experiment and should not be taken on account for recommended ways of implementing callbacks.
import merapi.messages.IMessage;
import flex2.tools.oem.ProgressMeter;
public class FxsdkJavAirProgressMeter implements ProgressMeter
{
private CompileMessage message;
private long before, after;
FxsdkJavAirProgressMeter(IMessage message)
{
super();
this.message = (CompileMessage)message;
}
public void start()
{
before = System.currentTimeMillis();
System.out.print("begin...");
}
public void end()
{
after = System.currentTimeMillis();
System.out.println("done");
System.out.println("Elapsed Time: " + (after - before) + "ms");
this.message.send();
}
public void percentDone(int n)
{
System.out.print(n + "...");
this.message.progress = n;
this.message.send();
}
}
One of the key elements in the establishing the bridge between Adobe AIR and Java is the message handler invoked by the Java application.
This eventually registers a message type and will assume the role of the observer in the messaging flow.
import merapi.handlers.MessageHandler;
import merapi.messages.IMessage;
public class CompileHandler extends MessageHandler
{
public CompileHandler()
{
super( CompileMessage.COMPILE );
}
public void handleMessage( IMessage message )
{
if( message instanceof CompileMessage )
{
CompileMessage compileMessage = (CompileMessage)message;
FxsdkJavAirApplication.compile(compileMessage);
}
}
}
These 4 classes are all the constitute the Java application based on the existing Flex Compiler API and the classes from the Merapi Project.
Now in the same order, I will go through the Flex part of the application.
First we have the application itself, in this case it is the container of the form and the Mate EventMap and to spice thinks up, a PreferenceMap which wraps the logic relating to managing user preferences.
<?xml version="1.0" encoding="utf-8"?> <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" xmlns:simplecompile="org.hello.fxsdkjavair.simplecompile.*"> <simplecompile:SimpleCompileForm /> <fx:Declarations> <simplecompile:MainEventMap /> <simplecompile:PreferenceMap /> </fx:Declarations> </s:WindowedApplication>
The message which corresponds to the message in the Java application looks something like this.
package org.hello.fxsdkjavair.simplecompile
{
import merapi.messages.Message;
[Bindable]
[RemoteClass(alias="CompileMessage")]
public class CompileMessage extends Message
{
public static const COMPILE:String = "compile";
public var inputFile:String = null;
public var outputFile:String = null;
public var progress:int;
public function CompileMessage()
{
super( COMPILE );
}
}
}
Then we have the form which contains the code allowing us to manage the inputfile and the outputfile paths.
<?xml version="1.0" encoding="utf-8"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:merapi="http://merapiproject.net/2009"
xmlns:simplecompile="org.hello.fxsdkjavair.simplecompile.*"
width="100%" height="100%">
<fx:Script>
<![CDATA[
import com.adobe.air.preferences.Preference;
import com.asfusion.mate.core.Cache;
import mx.rpc.events.ResultEvent;
protected function compileHandler_resultHandler(event:ResultEvent):void
{
this.currentState = READY;
}
protected function compileButton_clickHandler(event:MouseEvent):void
{
this.currentState = PROCESS;
this.compileMessage.inputFile = inputFileInput.text;
this.compileMessage.outputFile = outputFileInput.text;
this.compileMessage.send();
this.preference.setValue( "inputFile", inputFileInput.text );
this.preference.setValue( "outputFile", outputFileInput.text );
}
[Bindable]
public var preference:Preference
public static const READY:String = "ready";
public static const PROCESS:String = "process";
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout />
</s:layout>
<fx:Declarations>
<simplecompile:CompileMessage id="compileMessage" />
<merapi:MessageHandler id="compileHandler" type="{ CompileMessage.COMPILE }" result="compileHandler_resultHandler(event)" />
</fx:Declarations>
<s:Group width="100%" height="30">
<s:layout>
<s:HorizontalLayout>
</s:HorizontalLayout>
</s:layout>
<s:TextInput id="inputFileInput" width="100%" text="{ this.preference.getValue('inputFile') }" />
<s:TextInput id="outputFileInput" width="100%" text="{ this.preference.getValue('outputFile') }" />
<s:Button id="compileButton" label="Compile" click="compileButton_clickHandler(event)" enabled.process="false"/>
<mx:Label text="{ this.compileMessage.progress }%" />
</s:Group>
<mx:DataGrid id="resultGrid" width="100%" height="100%" />
<s:states>
<mx:State name="ready" />
<mx:State name="process" />
</s:states>
</s:Group>
For your reference, I will just be adding the EventMap and the PreferenceMap as well…
First we have the EventMap which is a core element of Mate based applications, again… this as well is outside the scope of this post to discuss further.
<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:mate="http://mate.asfusion.com/">
<fx:Script>
<![CDATA[
import com.adobe.air.preferences.Preference;
import mx.events.FlexEvent;
]]>
</fx:Script>
<fx:Declarations>
<mate:EventHandlers type="{ FlexEvent.INITIALIZE }">
<mate:ObjectBuilder generator="{ Preference }" />
</mate:EventHandlers>
<mate:EventHandlers type="{ FlexEvent.LOADING }">
<mate:MethodInvoker generator="{ Preference }" method="load" />
</mate:EventHandlers>
<mate:EventHandlers type="{ Event.CLOSING }">
<mate:MethodInvoker generator="{ Preference }" method="save" />
</mate:EventHandlers>
</fx:Declarations>
</mate:EventMap>
This is the preferencemap, again it might be total overkill to put in a sample such as this… but seeing that it is a basic part of any application I do these days, I saw no reason to exempt it. Remembering the user’s preferences is paramount when creating user experiences which seeks to satisfy the increasingly high expectations of users today.
<?xml version="1.0" encoding="utf-8"?>
<mate:EventMap xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo"
xmlns:mate="http://mate.asfusion.com/">
<fx:Script>
<![CDATA[
import com.adobe.air.preferences.Preference;
]]>
</fx:Script>
<fx:Declarations>
<mate:Injectors target="{ SimpleCompileForm }">
<mate:PropertyInjector source="{ Preference }" targetKey="preference" />
</mate:Injectors>
</fx:Declarations>
</mate:EventMap>
This is far from a complete or perhaps even suitable sample to illustrate the interoperation with both Merapi and the Flex Compiler API, but I decided to put it out here as part of a collaborative effort of getting my hands dirty with extending Flash Builder and Flash Catalyst.
If you have any questions regarding this sample, please don’t hesitate to contact me or comment on the post.
I will soon make the source code available under the “unsponsored” framework.
Flex SDK 3.3 released
So finally… the long awaited Flex SDK 3.3 finally left the list of stable builds and entered the world of releases…
This is great news seeing that Flex SDK 3.2 seemed to be somewhat of a bastard and 3.3 contains a lot of fixes…
Check it out…
http://www.adobe.com/products/flex/flexdownloads/
YES… Fx prefix will go away !
Adobe has decided to remove the Fx prefix and implement the multiple namespace solution… this is imho. GREAT news… I am so happy… Thank you Adobe… you rock!
Read more about it here…
http://www.adobeforums.com/webx/.59b7e849
Flex SDK : Skinning in Flex 4 (Codename: Gumbo)
The Flash Player is the delivery mechanism for some of the most creative work to be found on the web today. Flex applications however have gained a reputation for looking too similar to each other, as many developers choose to use the Flex default look and feel (known as Halo) as opposed to applying extensive styling or skinning. Research performed by Adobe shows that only 46% of our frequent Flex users do extensive skinning while only 22% even do major style adjustments. This is not to say that customization never happens, but Adobe found that it remains too challenging to create a truly custom experience. It is therefore a priority for Gumbo (The new version of the Flex SDK) to make easy customization of Flex application experiences the norm instead of the exception.
Exciting stuff… Check it out…
http://opensource.adobe.com/wiki/display/flexsdk/Gumbo+Themes
Adobe Flex : Flex 3 Compiler Design Document
Now there is a design document available at the Adobe site… nice move
http://opensource.adobe.com/wiki/display/flexsdk/Flex+3+Compiler+Design
Adobe Flex Resources Tool Suite
I plan to implement a small set of tools to facilitate handling of resource files in Adobe Flex.
I will address the following objectives:
1. objective
It must allow editorial staff to update and manage resources.
* Client should be implemented in AIR / Flex.
* Server should be implemented in .NET
2. objective
It must allow editorial staff to compile resources to their final output.
* Should use the Flex SDK compilers based on Java.
3. objective
It must allow editorial staff to deploy updated resources.
* Should be possible to do 24/7 without causing downtime.
4. objective
It could facilitate that editorial and technical staff can work in parallel, respectively translating and updating resources to their initial values by the editorial staff and creating the points in the application where each resource will be used by the technical staff.
* Points still needs to be determined.
Configuring Flex Builder to use the HellFire compiler
Clement Wong has created an excellent post on how to do RPC compilation of Flex Applications, Modules and Libraries.
The results he has been able to obtain are very thought-invoking and definitely something I will give a shot the first time I get 5 minutes to spare…
Check it out for yourself…
http://stopcoding.wordpress.com/2008/06/17/hellfire_compiler/
Adobe Flex : Cross-versioning
The “Marshall Plan” is the nickname for the SDK feature to support cross-versioning.
The nickname comes from the aspect of the feature that uses shared events and/or the SandboxBridge to marshal objects across ApplicationDomains. Marshalling was popularized in Windows as a way of transcoding objects so they can be shared between applications in different address spaces.
If the vision comes true, the “Marshall Plan” will define a post-3.1 Flex that liberate developers from having to have all of their code compiled by the same version of Flex.
Read more about this initiative which are planned to be part of the Flex 4 release.
http://opensource.adobe.com/wiki/display/flexsdk/Marshall+Plan
Adobe Flex : Linkreports
An important tool when trying to examine an application with the objective to divide it into modules and libraries or generally if you want to understand your application better, is the linkreport compiler option.
Adding the following to your compiler options will print a linkreport upon a successful compile. A linkreport is an XML file which lists all type definitions included in the output as well as the classes it depends on, but has not included.
-linkreport=[FileLocation][Filename].xml
eg. -linkreport=../reports/Linkreport.xml
It will generate the file on disk relative to the BIN directory, so if you want to place the linkreport in a separate directory on level with the BIN directory you have to append ../ to the beginning of the value.






3 comments