Merapi is an interesting project that wants to create a bridge between Air and Java on the desktop, something that already exists between Flex and Java on the web thanks to BlazeDS/LCDS and other similar projects.
It's made up of an actionscript part and a java part, together they build a messaging bridge based on AMF so that you can send messages from java to Air and viceversa.
Merapi is currently in a private alpha state, though a first beta has just been released on their site. If you want to get involved and download the package you have to create an account on http://www.merapiproject.net/ and wait for their approval (usually doesn't take longer than a few days).
The APIs are very simple, you can see an example of their usage on: http://www.merapiproject.net/index.php?option=com_content&view=article&id=47&Itemid=64&limitstart=3.
The idea is simple and is based on sending messages from one endpoint to the other. Sending a message from ActionScript:
var message : Message = new Message();
message.data = "Hello from Merapi Flex.";
message.type = "Reply";
Bridge.instance.sendMessage( message );
Sending a message from Java:
Bridge bridge = Bridge.getInstance();
Message message = new Message();
message.setData("Hello from Merapi Java.");
bridge.sendMessage(message);
Receiving a message in Flex:
<merapi:BridgeInstance
id="bridge" result="handleResult(event)" />
<mx:Script>
<![CDATA[
private function handleResult( event : ResultEvent ) : void
{
var message : IMessage = event.result as IMessage;
Receiving a message in Java:
Bridge.getInstance().registerMessageHandler("Reply", messageHandlerInstance );
public void handleMessage( IMessage message )
{
System.out.println( message.getData() );
}
There is a forum: http://www.merapiproject.net/index.php?option=com_fireboard and some interesting video of application examples: http://www.merapiproject.net/index.php?option=com_content&view=article&id=51&Itemid=84.
I decided to start playing with Merapi creating a AIR client for the Skype chat; first of all I started searching for a Java implementation of the Skype communication API. I found this project "http://skype.sourceforge.jp/index.php?Skype%20API%20For%20Java%20(English)", officially supported by Skype https://developer.skype.com/wiki/Java_API
It's a well written and powerful library so it was very easy to understand.
In my application I used two message types:
- "skype" - for generic API commands
- "skypeChat" - for chat messages
then I created two specific payloads for the Merapi message dependent on its type:
SkypeCommand in ActionScript
package
{
import mx.collections.ArrayCollection;
[RemoteClass(alias="SkypeCommand")]
public class SkypeCommand
{
public static const GET_VERSION:String = "getVersion";
public static const GET_FRIENDS:String = "getFriends";
public var name:String;
public var data:Object;
}
}
SkypeCommand in Java
public class SkypeCommand {
public static final String GET_VERSION = "getVersion";
public static final String GET_FRIENDS = "getFriends";
private String name;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SkypeChatMessage in ActionScript:
package
{
[RemoteClass(alias="SkypeChatMessage")]
public class SkypeChatMessage
{
public var skypeId:String;
public var message:String;
}
}
SkypeChatMessage in Java:
public class SkypeChatMessage {
private String skypeId;
private String message;
public String getSkypeId() {
return skypeId;
}
public void setSkypeId(String skypeId) {
this.skypeId = skypeId;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
As you can see, class mapping works exactly the same way it works in BlazeDS.
You have to initialize the bridge on ActionScript:
<merapi:BridgeInstance id="bridge"
result="bridgeMessageHandler()" />
and on Java you have to register message type handlers:
Bridge.getInstance().registerMessageHandler( "skype", new SkypeMessageHandler() );
Bridge.getInstance().registerMessageHandler( "skypeChat", new SkypeMessageHandler() );
and a Chat message listener
Skype.addChatMessageListener(new SkypeChatListener());
On AIR creationComplete I send two skype commands to get version and friends list:
private function init():void {
var versionCmd:SkypeCommand = new SkypeCommand();
versionCmd.name = SkypeCommand.GET_VERSION;
var listCmd:SkypeCommand = new SkypeCommand();
listCmd.name = SkypeCommand.GET_FRIENDS;
bridge.send("skype", versionCmd);
bridge.send("skype", listCmd);
}
On Java side, in the SkypeMessageHandler, for "skype" type:
SkypeCommand cmd = (SkypeCommand)((Message) message).getData();
Message response = new Message();
response.setType("skype");
SkypeCommand respCmd = new SkypeCommand();
String commandName = cmd.getName();
if(commandName.equals(SkypeCommand.GET_VERSION)) {
respCmd.setName(SkypeCommand.GET_VERSION);
respCmd.setData(Skype.getVersion());
} else if(commandName.equals(SkypeCommand.GET_FRIENDS)) {
ContactList list = Skype.getContactList();
Friend[] friends = list.getAllFriends();
String[] friendIds = new String[friends.length];
for (int i = 0; i < friends.length; i++) {
friendIds[i] = friends[i].getId();
}
respCmd.setName(SkypeCommand.GET_FRIENDS);
respCmd.setData(friendIds);
}
response.setData(respCmd);
Bridge.getInstance().sendMessage(response);
I resend back a SkypeCommand of the same type so that in the AIR app I can recognize the response:
private function bridgeMessageHandler():void
{
if(bridge.lastMessage.type == "skype")
switch(bridge.lastMessage.data.name) {
case SkypeCommand.GET_VERSION:
versionLabel.text = "Skype version: "+bridge.lastMessage.data.data as String;
break;
case SkypeCommand.GET_FRIENDS:
var friendsArray:Array = bridge.lastMessage.data.data as Array;
friendsList.dataProvider = friendsArray;
break;
}
For chat messages sent by AIR, I only forward them to the Skype API:
SkypeChatMessage chatMsg = (SkypeChatMessage)((Message) message).getData();
Chat c = Skype.chat(chatMsg.getSkypeId());
c.send(chatMsg.getMessage());
For chat messages sent by a friend, in the SkypeChatListener I forward the message to the bridge:
public void chatMessageReceived(ChatMessage arg0) throws SkypeException {
SkypeClient.hideSkypeWindow();
Message response = new Message();
response.setType("skypeChat");
SkypeChatMessage msg = new SkypeChatMessage();
msg.setSkypeId(arg0.getSenderId());
msg.setMessage(arg0.getContent());
response.setData(msg);
try{
Bridge.getInstance().sendMessage(response);
} catch(Exception e) {
e.printStackTrace();
}
}
You have to start the java application first, then the AIR, otherwise you will get IOErrors on the bridge. And here is the AIR app in action:

This is the AIR installer: Download file
and the runnable jar file: Download file
Waiting for the next version of AIR, that will let you call executable files, Merapi could be a good starting point to make your apps interoperate with AIR; there is a java implementation for nearly everything you will need, thus Merapi exponentially expands the AIR potential on desktop.
Comments (3)
Great article! Thank you a lot!
Posted by milos | February 8, 2009 10:06 PM
Posted on February 8, 2009 22:06
Great article. Very helpful. Can you explain how to pass an array of objects from java -> air and back? For example, if I have an array of my own Person objects that I want to pass from Java to AIR using merapi?
Posted by kwade | March 1, 2009 4:38 AM
Posted on March 1, 2009 04:38
Hi kwade,
you can pass object to the AIR application the same way you would do using BlazeDS/LCDS.
Posted by Emanuele Tatti | March 3, 2009 4:10 PM
Posted on March 3, 2009 16:10