Flex, ColdFusion, Java, and BlazeDS: with JSON?

by

We are building a sizable new Flex-based mapping application to support our internal Endorsed Local Provider program. We are passing a significant amount of data between Flex and ColdFusion. So, we architected a service layer that exposes the interface between Flex and CF, and are using BlazeDS to allow us to link Java-based data transfer objects (DTO) on the server side to equivalent Actionscript objects in Flex.

We chose to use Java DTOs rather than CFCs because we found that the Java versions were 15-20 times faster on ColdFusion server. For example, a loop through 1,000 records takes an average of 1.5 seconds:


<cfloop query="rstAddresses">
	<cfset currAddress = CreateObject("component",
                                "components.lampo.mapping.vo.Address") />
	<cfset currAddress.setId(rstAddresses.address_id) />
	<cfset currAddress.setStreet1(rstAddresses.address_line_1) />
	<cfset currAddress.setStreet2(rstAddresses.address_line_2) />
	<cfset currAddress.setCity(rstAddresses.city) />
	<cfset currAddress.setStateCode(rstAddresses.state) />
	<cfset currAddress.setPostalCode(rstAddresses.zipcode) />
	<cfset stcAddresses[rstAddresses.address_id] = currAddress />
</cfloop>

Changing the first line to use a Java object reduces the time to an average of 75 milliseconds:


<cfloop query="rstAddresses">
	<cfset currAddress =
                CreateObject("java", "com.lampo.mapping.vo.Address").init() />
    ...
</cfloop>

I created a custom Java class that generates the Actionscript DTOs from the Java DTOs. We had no trouble sending Java objects to Flex with ColdFusion services through BlazeDS where the parameters passed from Flex were built-in objects, like Strings, Numbers, or Maps. However, when calling a method from Flex that passed one of our custom DTOs as a parameter, the server reported the following error: “Unable to invoke CFC – Could not find the ColdFusion Component or Interface”.

It turns out that if you use ColdFusion services through BlazeDS, meaning that you are using the “cf-object” adapter in your remoting-config.xml, BlazeDS only links ActionScript DTOs with ColdFusion CFC objects on the server. You have to use the “java-object” adapter for it to link Actionscript DTOs to their Java counterparts, but then you have to build your entire service layer in Java, instead of ColdFusion.

Here’s an example of the kind of objects we’re talking about. We have a service method called getPolygons(BoundingBox bb). Flex passes a BoundingBox object, which contains two LatLng objects, and the server returns all of the zip code polygons in that box.

The Java BoundingBox DTO looks like this:


package com.lampo.mapping.vo;
import java.io.Serializable;

public class BoundingBox implements Serializable
{
	//Properties
	private LatLngOnly upperLeftLatLng;
	private LatLngOnly lowerRightLatLng;

	public BoundingBox() {;}

	//Methods
	public LatLngOnly getUpperLeftLatLng() {
		return upperLeftLatLng;
	}
	public void setUpperLeftLatLng(LatLngOnly upperLeftLatLng) {
		this.upperLeftLatLng = upperLeftLatLng;
	}
	public LatLngOnly getLowerRightLatLng() {
		return lowerRightLatLng;
	}
	public void setLowerRightLatLng(LatLngOnly lowerRightLatLng) {
		this.lowerRightLatLng = lowerRightLatLng;
	}

}

And the auto-generated Actionscript version is:


package com.lampo.mapping.vo
{
    import com.lampo.mapping.vo.LatLngOnly;

    [RemoteClass(alias="com.lampo.mapping.vo.BoundingBox")]
    public class BoundingBox
    {
        public var lowerRightLatLng:LatLngOnly;
        public var upperLeftLatLng:LatLngOnly;

        public function getLowerRightLatLng():LatLngOnly
        {
            return this.lowerRightLatLng;
        }
        public function setLowerRightLatLng(in_lowerRightLatLng:LatLngOnly):void
        {
            this.lowerRightLatLng = in_lowerRightLatLng;
        }
        public function getUpperLeftLatLng():LatLngOnly
        {
            return this.upperLeftLatLng;
        }
        public function setUpperLeftLatLng(in_upperLeftLatLng:LatLngOnly):void
        {
            this.upperLeftLatLng = in_upperLeftLatLng;
        }
    }
}

The key to BlazeDS’s ability to link the Actionscript to the Java object is the [RemoteClass(alias="...")] block. From ColdFusion to Flex, a BoundingBox or any other custom object is automatically serialized by BlazeDS and all is well. The problem is going from Flex back to CF with Java DTOs.

To address this, we tried doing some enhancements to the built-in deserialization code, wrapping a ColdFusion DTO around a Java DTO, and decompiling coldfusion.flash.messaging.ColdFusionAdapter to figure out how to make it load Java DTOs. After weighing these ideas we decided to try serializing our Actionscript DTOs into JSON before sending them to ColdFusion. This turned out to be a relatively pain-free solution that maintained our desire to use matching objects on the client and server.

Details

First, we found Adobe’s as3corelib, which includes very handy methods for serializing any object into JSON. Once you download their libraries and include them in your Flex project, it’s as simple as:


import com.adobe.serialization.json.JSON;
JSON.encode(yourObj);

To decode them, we found JSON-lib on SourceForge. Once you have it installed in ColdFusion (see below) you can add a function like this in your Java DTO:


public static BoundingBox makeFromJSON(String inJSON)
	throws Exception
{
	// Place property name/classes in this
        // map to give JSONFunction hints about
	// how to deserialize the JSON
	Map<String, Class> hintsMap = new HashMap<String, Class>();
	hintsMap.put("upperLeftLatLng", LatLngOnly.class);
	hintsMap.put("lowerRightLatLng", LatLngOnly.class);
	BoundingBox bb = (BoundingBox)JSONObject.toBean(
				JSONObject.fromObject(inJSON), BoundingBox.class, hintsMap);
	return bb;
}

Since the BoundingBox object includes properties that are also custom objects, you have to provide a Map that links the property names to the classes, so that JSON-lib can generate the object from the input JSON. This is very handy, and can be called in ColdFusion like this:


<cfscript>
	bbStatic = CreateObject("java", "com.lampo.mapping.vo.BoundingBox");
	jsonBB = "{""upperLeftLatLng"":{""longitude"":-84.5,""latitude"":36}," &
             """lowerRightLatLng"":{""longitude"":-86.79118,""latitude"":39.107}}}";
	realBB = bbStatic.makeFromJSON(jsonBB);
</cfscript>

The JSON-lib setup is a little tricky. Here’s how I installed it into ColdFusion 8:

  1. Download JSON-lib and copy the .jar to WEB-INF/lib. The .jar I used is: json-lib-2.2.2-jdk15.jar.
  2. Download EZ-Morph and copy the .jar to WEB-INF/lib. The .jar I used is: ezmorph-1.0.5.jar.
  3. Download Apache’s Commons-Lang library and copy the .jar to WEB-INF/lib. The .jar I used is: commons-lang-2.4.jar.
  4. Download Apache’s Commons-Collections library and copy the .jar to WEB-INF/lib. The .jar I used is: commons-collections-3.2.1.jar.
  5. Copy the following JARs from WEB-INF/cfusion/lib to WEB-INF/lib: commons-logging.1.0.4.jar, commons-logging-api.1.0.4.jar, log4j-1.2.12.jar.

With this solution, we’re seeing excellent performance and are maintaining the goal of passing Strongly-typed DTOs between Flex & ColdFusion through BlazeDS using a well-defined service interface.

Advertisements

Tags: , , , , ,

5 Responses to “Flex, ColdFusion, Java, and BlazeDS: with JSON?”

  1. Andres Almiray Says:

    I’m glad you found Json-lib to be helpful. Please feel free to suggest any improvements you deem necessary =)

  2. ColdFusion Testing Woes With Java Objects « Web Monkeys with Laserbeams Says:

    […] Web Monkeys with Laserbeams Just another WordPress.com weblog « Flex, ColdFusion, Java, and BlazeDS: with JSON? […]

  3. More ColdFusion Testing Woes With Java Objects « Web Monkeys with Laserbeams Says:

    […] Another minor glitch I ran into while trying to test the ColdFusion method calls is with the SerializeJSON function built into ColdFusion 8. Because of the way BlazeDS works, in order to send objects from the Flex app back to ColdFusion we have to convert the object to a JSON string, send it, and then rebuild the object in ColdFusion from the JSON string. My teammate explains the process in his blog entry about the tech stack “Flex, ColdFusion, Java, and BlazeDS: with JSON?” […]

  4. Nargis Says:

    Can you help me out with how to De-serialize java object into flex object .

    i am using following with my result handler

    public function resultHandler(e:ResultEvent):void{
    var data:String = XML(e.result).toString();
    data = data.replace( /\s/g, ” );
    var jd:JSONDecoder = new JSONDecoder( data );

    var nodeReg:BoundingBox = jd.getValue();
    trace(nodeReg);

    }
    and it gives following error.

    Type Coercion failed: cannot convert Object@1cf8c511 to com.lampo.mapping.vo.BoundingBox

  5. doez Says:

    thx

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: