Flex/AIR to Java Communication Using Merapi

November 13, 2008By 16 Comments

If you’re looking for a way to communicate between Flex and Java, there’s a great API available for doing just that. It’s called Merapi and is basically a bridge for sending messages between Java applications and Flex/AIR apps. I recently worked on a project for Adobe (shhh, until MAX!) where I used Merapi to communicate between an Eclipse plugin and an AIR app and found it extremely cool! One of the greatest things I found with it is the fact that it is written on top of the AMF3 libraries so passing objects between the two is a piece of cake!

In this post I will show how I was able to use Merapi to suit my needs of communication. I started with Merapi on the Java side by creating a Merapi Bridge instance and registering the various messages I wanted to listen to along with the handler object. The object specified as the handler needs to implement the handleMessage() method and gets called when a message being listened to is received. Each message has a name to identify it between the Java and ActionScript/Flex side. The code to register the messages in Java looks like the following:

          Bridge br = Bridge.getInstance();
          br.registerMessageHandler(MSG_FINDCOMPS, this);      // MSG_FINDCOMPS is a static String with value FindComponents
          br.registerMessageHandler(MSG_FINDFEATCOMPS, this);  // MSG_FINDFEATCOMPS is a static String with value FindFeaturedComponents

and the code to handle those messages when they are received on the Java side looks like the following:

          public void handleMessage( IMessage message ) {
		// Check message type and handle accordingly
		if (message.getType().equals(MSG_FINDCOMPS)) {
			final IMessage m = message;
			resultTableDisplay.syncExec(new Runnable() {
		        public void run() {
		            final ArrayList al = (ArrayList)m.getData();
		        	compList.setComponents(al);
			    }
		        });
		}
		if (message.getType().equals(MSG_FINDFEATCOMPS)) {
		    // In this case, we want to go ahead and reply with a message containing the Plugin Transfer
		    // Object so we have the Eclipse workspace path etc... back in the AIR app
		    TDFPluginTransferObject tpto = new TDFPluginTransferObject();
		    tpto.setSelectedComponentId(null);
		    try {
	                IPath path = (IPath) org.eclipse.core.resources.ResourcesPlugin.getWorkspace().getRoot().getRawLocation();
			tpto.setPluginDownloadPath(path.toString());
			this.sendMessage(MSG_SENDINFO,tpto);
		    }
		    catch (IllegalStateException ex) {
			System.out.println("IllegalStateException:" + ex);
		    }

		    final IMessage m = message;
		    featuredTableDisplay.syncExec(new Runnable() {
		    public void run() {
		        final ArrayList al = (ArrayList)m.getData();
		             featuredCompList.setComponents(al);
		        }
		    });
		}
	}

Notice in the code shown above, I am also sending a message to the AIR side with a custom object containing data I need there as a response to a message I had received. That object is represented by a class on both the the AIR and Java side and all serializing/deserializing of it is done for me underneath, which is awesome! More about that again shortly. My sendMessage() method is where I actually do all the sending from the Java side to the AIR side and looks like the following:

        public void sendMessage(String msgType, Object msgData) {
		try {
			Bridge.getInstance().sendMessage(new Message(msgType,null, msgData));
		}
		catch(Exception ex) {
			System.out.println("Exception " + ex);
		}
	}

So now we need to head over to the AIR side to see how this all fits together. First I need to make sure that the app was called from my plugin since I have specific behavior that is needed in that case. So to do this I check for an invoke argument that I pass from the Eclipse side when I invoke the AIR app. If found, then I want to create a Merapi Bridge instance and listener, along with a specified handler method for the responses. As you have probably noticed, the Bridge object is implemented as a Singleton, so you always get that one instance.

	if (invokeEvt.arguments[0] == "plugin") {
	        isPlugin = true;
		bridge = Bridge.instance;
	  	bridge.addEventListener(ResultEvent.RESULT,handleResponse);
	}

The handleResponse() method to specified above is called when a response is received from Java and that handler looks like the following:

       private function handleResponse(event:Event):void
       {
            var ev:ResultEvent = ResultEvent(event);
		var msg:Message = Message(ev.result);
		if (msg.type == "FindComponents") {
			if (msg.data != null) {
				var msgType:String = msg.type;
				comps = objectData.getFilteredComponents(String(msg.data));

	    		}
			var m : Message = new Message();
           		m.type = "FindComponents";
               		m.data = comps;
               		bridge.sendMessage(m);
		}
		else if (msg.type == "SendPluginTO") {
		        pluginTransferObject = TDFPluginTransferObject(msg.data);
		}
		else if (msg.type == "Minimize") {
			this.minimize();
		}
		else if (msg.type == "Dock") {
			this.dock();
		}
		else if (msg.type == "Undock") {
			// We need to figure out what the download path is from the returned message and set it
			pluginTransferObject = TDFPluginTransferObject(msg.data);
			this.objectList_change(null); // Fire the navigation to the component double-clicked from plugin
			this.undock(event);
		}
		else if (msg.type == "Exit") {
			isPlugin = false;
			onExiting(event);
		}
     }

The Java side is the ‘initiator’ of the communication, and the AIR app handles it’s requests via the above method, however the AIR side can also send a message to the Java side once the bridge has been established. An example of that is shown below, were we specify the component type name and its data before sending.

                var m : Message = new Message();
	       	m.type = MSG_FINDCOMPS;  // where MSG_FINDCOMPS is a static variable string with value FindComponents
	        m.data = featuredComps;
	        bridge.sendMessage(m);

There is one line of code from the handleResponse() method I’d like to point out in particular, where I’m creating the custom object below:

        pluginTransferObject = TDFPluginTransferObject(msg.data);

This is the other side of the object serialization that occurs automatically for me so my object is ready to go upon receipt. Two main things to be noted here… 1) you MUST put the remote path of the Java side object to be mapped to inside your custom ActionScript object (such as in my TDFPluginTransferObject in this case). That statement looks like the following:

[RemoteClass(alias="com.adobe.tourdeflex.core.TDFPluginTransferObject")]

and 2) all properties of the object must match exactly to the fields on the Java side, names, capitalization, everything.

So that about sums it up. The use of Merapi in this application definitely added that ‘WOW’ factor, and is still super cool to me and everyone that sees it so I highly recommend checking it out! The Merapi project is currently in private beta and about to be released soon. The guys behind it, namely Adam Flater and Dave Meeker, have been working very hard to support it and were extremely helpful in making sure we were able to use it for our needs. Thanks Adam & Dave :)!! They will also be at Adobe MAX, so be sure and check out all the cool stuff they’re doing!

Filed in: Adobe FlexFlex/AIRJava Tags:

About the Author ()

Comments (16)

Trackback URL | Comments RSS Feed

  1. asadsiddiqi says:

    Fabulous article . That does show the usage of API and I love it …. Since Java is my new love now ( hehe ) I really enjoy reading JAVA code !! U have a great blog !! keep it going and the good words coming !!
    Thank you

  2. starsirius says:

    First, thanks for your great article!

    But I have a serious problem while implementing my application.
    That is, I can send custom object from Java to AIR.
    And I can send primitive data type from AIR to Java.
    However, I can not send “custom object” from AIR to Java.
    Actually, I got the java.lang.ClassCastException!

    I found it in your code here:
    final ArrayList al = (ArrayList)m.getData();
    Could you show me more details about it?

    I also found an article here, but still have no idea.
    http://www.merapiproject.net/index.php?option=com_fireboard&Itemid=58&func=view&id=69&catid=8&limitstart=6

    Thank you very much. :)

  3. devgirl says:

    The way that I passed my custom objects was via a Flex ArrayCollection – to a Java ArrayList. So on my Flex/AIR side, I have custom class called Component with the [RemoteClass(alias="com.adobe.tourdeflex.core.Component")] declaration in it, which specifies the remote class on the Java side to map to. I need to make sure my Java side has this same package name and the same exact properties and methods so they map correctly. Then, on my AIR side I put a all of my custom Component objects in an ArrayCollection, and then actually pass the ArrayCollection object. So in this case, the comps objects is an ArrayCollection:

    m.data = comps;
    bridge.sendMessage(m);

    And on the Java receiving side, it would be handled with:
    final ArrayList al = (ArrayList)m.getData();

    where that Component class matches the component class on the AIR side.

    I hope this helps!!
    Holly

  4. starsirius says:

    Thanks for the quick response!

    Finally the problem was solved by upgrading the Merapi framework to the newest version (0.0.90).
    But, anyway, your tutorial really helps!
    Thanks again! :)

  5. Juho says:

    Hi,
    does it thinkable that Air and Java communication could be developt/use on a synchronus way.

    Best regards
    Julien

  6. Francisco says:

    When used merapi had problems such as CPU 100%
    processing, fixed TCP port, then developed my
    own classes, java and flex. Are available
    projects for download in my blog

  7. Jakir says:

    is there is any sample application for this
    i am new to java so need help how actually flex and java work together

  8. Gregor says:

    Very nice,

    helped me a lot of lot…

    thanks devgirl

  9. slick says:

    this is nice… thanks devgirl. smart lady developers are so rare. :)

  10. LeoR says:

    Guys, I am having a big problem.
    My Java App has many threads, and sometimes when these threads send many messages at the same time, I lose some of these messages on the flex side.
    I am using the version 0.1.8.
    Can anyone help me please?
    Thanks

  11. hey, surfing 2 mins on web and have to say very
    good site and I’m gonna like it.

    look forward to surfing around and reading alot of topics here.

    figured I say what’s up!

  12. Usama says:

    i receive an error message on compiling a code

    log4j:WARN No appenders could be found for logger (merapi.Bridge).
    log4j:WARN Please initialize the log4j system properly.

    plz help

  13. Usama says:

    my Handel Response function triggers two time one for previous result and one for new result and next time it triggers tree times and so on is there any way to clear the previous data and only got a new one

Leave a Reply