Merapi : A Bridge Between AIR and Java

DZone 's Guide to

Merapi : A Bridge Between AIR and Java

· Java Zone ·
Free Resource

If you have ever created an Adobe AIR application, there is a good chance you have run into problems with the Sandbox Security Model.  The Sandbox Security Model in AIR is in place to prevent developers from creating applications that can either mine information from or harm a user's machine.  These precautions are justified and are what make AIR a safe alternative for creating desktop applications.  When a user installs an AIR application, he is always met with the same standard install screens and knows that the software he is installing is safe.  The only downside to the Sandbox Model is that it can be very limiting to the developer.  Access to external drives, command line, and even lists of native applications are all blocked by the Sandbox Security. As a result, applications are often launched with only some of their possible features. 

With the release of AIR 2.0 to beta, a number of the security issues have been addressed.  Now applications can access external storage devices and can open up files in native applications, but there are still some limitations in place.  This is where Merapi comes into play.

Merapi is a bridge between Flex-based AIR applications and Java.  The Merapi bridge allows for communication between the two technologies through the use of messages.  The messages can contain complex data, and the AMF data serialization protocols are used to transfer the data.  Introducing Java into the equation allows developers to create cutting-edge software solutions, as Java is able to interact with far more aspects of the user's system.  By using Java, one is able to access not only the underlying code and features of the user's operating system but also the many libraries that Java has to offer.  As many external devices (such as barcode scanners, temperature sensors, and GPS units) often ship with Java based APIs, Merapi enables the developer to interact with those APIs and create applications tailor-made to fit specific scenarios that AIR on its own would not be able to accommodate. 

People often have concerns about circumventing the Sandbox Security Model of AIR; after all, that security was put into place for a reason. They do not want to see the market flooded with insecure applications that could be used by hackers to access users' machines.  However, this will not happen, chiefly because in order to use Merapi, both the AIR application and the Java application must be running at the same time.  The Java application must also be installed separately from the AIR application, and the AIR application itself cannot install the Java files.  If one does not install the Java files and run the Java application prior to starting the AIR application, then communication cannot take place.  The need for the Java files to be placed on the user's machine separately is what stops Merapi from being used in mainstream applications.  Merapi applications often need to be installed in very specific environments. This makes them ideal for internal applications within a company but not so ideal for the mass market.

The Code Stuff 

Now that we have looked at some of the theory behind Merapi, let's look at how it is used.  To give you a good understanding of how everything works, I am going to walk you through the process of making a Magic Eight Ball application with Merapi.  The front end of the application will be a Flex based AIR application that takes a question from the user and sends it to a Java application via Merapi.  The Java application will then generate a response to the question and send it back, also via Merapi, to the AIR application. Finally, the Air application will display the message.  By the end of this example, you will see how communication works in a multidirectional manner and should have a good grasp of the power that Merapi has to offer.

The first thing you need to do is get the latest Merapi files.  There are 3 SVN Repo’s that you should check out to make sure you have the latest files.  Merapi has only been in public beta for a few months, so there are regular updates to the code.

In the upcoming months all these files will be merged into a single trunk for a simpler download.

Once you have the latest files, you can start to create the Java side of the application.  When you create a Java application, the best practice is to create a main Java file that instantiates the necessary handler files.  The handler files, which should be located in a handlers package, are the files that contain most of the logic for your application.  The handlers are the classes that handle the incoming messages.  The messages themselves are typed, so the handlers will only interact with their related messages.  In your application you will also have message classes.  There will be a different message class for each type of message your application sends or receives.  Located in a messages package, these classes will be called from the handler classes.


The Java Steps

So first things first – create a new Java project in eclipse, and add the necessary jar files to the library.  The list of required jar files can be viewed in the screen shot below.


 After the project is created, create a file called magicEightBallRunner.  This is the main application file and should reside in the default package.  Once it has been created, add the following code:

import merapi.Bridge;
import handlers.questionHandler;

public class magicEightballRunner {

public static void main( String[] args )
new questionHandler();

In this code we import the bridge class and the questionHandler class.  We then open the bridge and create a new instance of the questionHandler, thus subscribing the application to the feed of messages in the bridge.  The same bridge is used for all Merapi applications, so the application will have access to all messages, including both those that relate to the application and those that don’t.  It is the questionHandler that selects which messages the application will access and which it will not.  For anyone who has prior experience with technologies such as Blaze or LiveCycle, this subscriber/ producer approach will seem familiar.  These two lines of code have set up the application to be a subscriber.

The next file that we must create is the handler.  As stated before, the handler class contains most of the logic for the application.  A handler is created for every type of message that the application is going to read, and inside the handler is a handleMessage function.  This function is called when the handler recognizes a message in the bridge that matches its criteria.  Another way to think of handlers is to think of them as event listeners.  The handler "listens" to the bridge, and acts when it encounters a message.

So create a handlers package, and create a class called questionHandler inside the package.  Then add the following code:

package handlers;

import messages.questionMessage;
import merapi.handlers.MessageHandler;
import merapi.messages.IMessage;
import java.util.Random;

public class questionHandler extends MessageHandler{
public questionHandler()
super( questionMessage.ASK_IT );

public void handleMessage( IMessage message )
if ( message instanceof questionMessage )
questionMessage qMessage = ( questionMessage ) message;
Random generator = new Random();
int randomIndex = generator.nextInt( 10 );

String answer = null;

case 1: answer = "As I see it, yes"; break;
case 2: answer = "It is decidedly so"; break;
case 3: answer = "Without a doubt"; break;
case 4: answer = "Reply hazy, try again"; break;
case 5: answer = "Better not tell you now"; break;
case 6: answer = "Concentrate and ask again"; break;
case 7: answer = "Don't count on it"; break;
case 8: answer = "Outlook not so good"; break;
case 9: answer = "Yes - definitely"; break;
case 10: answer = "You may rely on it"; break;


This class extends the MessageHandler class that is contained in Merapi.  At the top of the class, we import the necessary libraries.  When creating a handler, it is important to import the matching message class so that you are able to access information, such as message type, from the class.  It is also important to import the class if you are planning on sending any messages.  You must also import the IMessage and messageHandler classes from Merapi.  In this example we are also importing the java.util.Random class, since we will be randomly choosing what response to provide.  Most likely you will not need this in other examples.

The first part of this class is the constructor.  In the constructor, we call the super function and pass a static variable from the questionMessage class.  This variable is a string stating what type of message this handler will handle.  The string value can be whatever you wish but must match the string value in the AIR application. Otherwise, the handler will never pick up the messages from the bridge.

The second part of this class is the handleMessage function.  As stated before, every handler must have a handleMessage function. This function accepts an IMessage, which is the message object from the bridge.  This IMessage is then cast as the message type the handler is handling.  Once you have the message variable in the format that you need, you can use whatever logic you need to achieve your goal.  If you were creating an application that communicated with some 3rd party API, you would place that logic here.  In the case of this example, we are generating a random number which we are using in a switch statement to select the response we want to provide.  We then set the answer value of the questionMessage object that we created earlier and call the send function.  Calling the send function sends the message back to the bridge, where it will wait to be picked up by the AIR application.

Now let's take a look at the message class.  Create a new package called messages, and in that package create a new class called questionMessage.  Inside the class put the following code:

package messages;
import merapi.messages.Message;

public class questionMessage extends Message{

public static final String ASK_IT = "magicQuestion";

public questionMessage()

public String getQuestion() { return __question; }
public void setQuestion( String val ) { __question = val; }

public String getAnswer() { return __answer; }
public void setAnswer( String val ) { __answer = val; }

private String __question = null;
private String __answer = null;


As you can see, this is a very simple class.  A variable called ASK_IT is created (you may remember that this is the variable we call from the handler class) and is set to the value magicQuestion.  This is the value used to differentiate between the different message types that can be in the bridge.  In the constructor class we call the super function, and then we set up the getters and setters that are needed in the object.  In this example the object can store both a question and an answer and also retrieve said values.

Now that you have all the necessary files, run the application and make sure there are no errors.  If all goes well, the application will run, and nothing will really happen.  This is one of the few times where seeing nothing is a good thing.

The Flex Stuff

Now that the Java is in place, let's move onto building our AIR application.

The files for the AIR application are similar to the Java ones in structure.  There is a main application file, and there is a message class, but instead of having a handler class, that functionality is often just placed wherever the Merapi functionality is located. 

First thing you need to do to build the AIR application is to create a new project in Flex Builder.  When creating the project, remember to select that it is a Desktop Application.  When you get to the library section, add the SWC files you checked out from the Merapi Repo.

Once you have created your project, open up your main MXML file and place the following code in the file.

<?xml version="1.0" encoding="utf-8"?>
type="{ questionMessage.ASK_IT }"

import mx.rpc.events.ResultEvent;
import merapi.messages.IMessage;
import merapi.messages.Message;
import com.magiceightball.messages.*;

private function askIt():void{
var q:questionMessage = new questionMessage();
q.question = question.text;

private function handleResult(e:ResultEvent):void{
var message:questionMessage = e.result as questionMessage;
answerReceived.text = message.answer;
questionAsked.text = message.question;

<mx:Label x="40" y="42" text="What is your question?"/>
<mx:TextInput x="181" y="40" id="question"/>
<mx:Button x="108" y="94" label="Ask" click="askIt()"/>
<mx:Label x="150" y="168" id="answerReceived"/>
<mx:Label x="40" y="142" text="Question Asked:"/>
<mx:Label x="40" y="168" text="Answer Received:"/>
<mx:Label x="150" y="142" id="questionAsked"/>


Upon reviewing this code, you will see a lot of similarities between this code and the Java code.  Part of that is due to the similarities between ActionScript and Java, and part of it is due to Merapi making use of objects which in general are very similar from language to language.  The biggest difference you will see in the Flex code is that instead of having a separate handler class, this code specifies a message handler through the use of the MessageHandler tag.  In this tag we specify the type of message it is handling and what function to call when a message is received.  If you are building an application that will use different types of messages, you will need to use multiple MessageHandler tags.  In this example just like in the Java example, the message type is stored in the message class as a static variable.  In this case the message class is called questionMessage.  The value of this variable, just like in the Java example is magicQuestion.

The askIt() function is the function used to build and send a message to Java.  This function creates an instance of questionMessage and populates it with the user-supplied data.  It then calls the send function, which sends the message to the bridge.  Once in the bridge, the message can then be picked up by any other applications connected to the bridge.

The handleResult() function is called whenever a new message is received from Java.  The message is passed to the function through the resultEvent and is then cast as a QuestionMessage.  Once it is in the correct format, the answer which was generated by Java is extracted and displayed to the user.

Let's take a look at the final file we will need to create for this example, the questionMessage class.  Create a new ActionScript class called questionMessage in the path com/magiceightball/messages.  It is always the best practice to keep all message classes in a messages folder, just like you do in Java.  In the class add the following code:

package com.magiceightball.messages
import merapi.messages.Message;
[RemoteClass( alias="messages.questionMessage" )]

public class questionMessage extends Message
public static const ASK_IT : String = "magicQuestion";
public var question : String = null;
public var answer : String = null;

public function questionMessage()
super( ASK_IT );


As you can see, there is not much code required to create the message class, but the code that is in this class is very specific and must match up with the Java equivalent of this class exactly.  In this class you will notice a remoteClass function.  In it we set an alias value.  The alias value is the exact path that the questionMessage class in the Java application has.  Whenever creating a new message type, you must set an alias that maps it to where its Java equivalent is located.  In the Java code, we run a comparison to make sure that we are only dealing with objects of the same type.  Using the alias makes sure that Java will see this object as the same.  While you are developing your application, if you ever have an instance where you are firing off messages from your AIR application and Java is not receiving them, check the alias, since this will most likely be the root of the problem.  Many an hour has been lost to this single line of code.

This class must match also its Java counterpart's variables.  You will notice that this class has question and answer variables and that the Java class has get/set question and get/set answer methods.  It is important that both objects have the same methods, but since ActionScript automatically generates getters and setters for its public variables, you only need to create the variables in the ActionScript class.

Now that we have all the code created for this application, let's give it a whirl. The first thing you will want to do is to start up the Java application.  Go to the magicEightballRunnner file, and run it.  Once that is running error-free, run the AIR application.  You will see a text area and a button.  Type in a question, and hit the button.  If all goes well, below the button you should see the question that you asked and the response from Java.  Everything that is displayed below the button – both the question and answer – is data from the Java application.  You now have your first fully functional Merapi application!  Take some time to look around, and play with some of the methods until your are comfortable enough to make your own application.

As you can see from this example, it is very easy to create applications that communicate via the Merapi bridge.  This example alone was created in less than 45 minutes.  Opening up Java to AIR applications allows for a whole new area of application development.  Not only can the applications tie into other technologies that previously could only be accessed through web servers, but also the applications can now interact with and control devices that are accessible through serial ports and USB drives.  If you take a look at the Merapi web site, you will see that there are already a number of videos demonstrating AIR applications interacting with RFID scanners, GPS units, and even Lego Mindstorm robots.  These are but a few of the endless possibilities you have when connecting AIR applications with Java.  My question to you is: what are you going to build?


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}