Learning Cairngorm (Part 3)

Turning Java Classes into ColdFusion Components

The first thing I did was to convert all the java classes into ColdFusion components. The java components are in “WEB-INF\com\adobe\cairngorm\samples\store\*”.

I decided, for simplicity, to create my CFCs in the root directory of the Cairngorm store app, instead of in a subdirectory structure. You do not have to do that to use ColdFusion with Flex.

The first class I migrated was CreditCardDelegate. This contains a single function, validateCreditCard. It doesn’t look like an credit card validation is done in this method, it just picks a random number. Half the time it will be validated, and half the time it won’t. It’s not real world, but for proof of principle purposes it suffices. The java class used Math.random to pick the random number. I used ColdFusion’s function RandRange.

The next file was the ProductDelegate. It seems to me that a delegate is akin to service objects used commonly by ColdFusion developers. The ProductDelegate has a single method, getproducts. It returns a list type, which is a bit problematic. The Java List is an interface, with other types mapping to it. In the Data Access Object, the Linked List class is used. ColdFusion doesn’t have a parallel. At first I was going to use a query, but eventually decided upon using an Array of value objects. The Java version of ProductDelegate does not create an instance of the ProductDAO inside it. This is something I changed in the CFC, although I’m not sure if it was an improvement, but I don’t want to go back and change it.

The Java version had an abstract class named DAO. I didn’t bother to convert this.

ColdFusion and Java Differences

Next there is a ProductDAO.java file. This is the data access object layer. The Java version creates a LinkedList of product value objects. The ColdFusion version creates an Array of product value objects. In the Java code, it looked like an instance variable was created (products) and then never used again. This was a result of my inexperience with Java. In ColdFusion, there is no distinction between variable definition and variable assignment. The variable is defined as a private instance variable:

private List products = new LinkedList();

And then later reinitialized in a method:

products = new LinkedList();

I mistook the second for creating an function local variable of the same name as the instance variable. It’s not doing that, though. It is just re-initting it. The DAO does not access a database, local storage, an XML file, or web service to get it’s data. It’s all hard coded into the component. You wouldn’t want to do this in the real world, but for the proof-of-principle-ness of this app, it fine.

I took each Java code block, like this:

ProductVO product1 = new ProductVO();
product1.setId( 1 );
product1.setName( “USB Watch” );
product1.setDescription( “So, you need to tell the time of…” );
product1.setPrice( 129.99f );
product1.setImage( “assets/products/usbwatch.jpg” );
product1.setThumbnail( “assets/products/usbwatch_sm.jpg” );
products.add( product1 );

And turned it into CF code, like this:

<cfset var product1 = CreateObject(’component’,’ProductVO’)>
<cfscript>
product1.setId( 1 );
product1.setName( “USB Watch” );
product1.setDescription( “So, you need to tell the time of…” );
product1.setPrice( 129.99 );
product1.setImage( “assets/products/usbwatch.jpg” );
product1.setThumbnail( “assets/products/usbwatch_sm.jpg” );
ArrayAppend(products, product1 );
</cfscript>

All the vars went at the top of the method, of course. Once the Value Object is created, we just call the set methods to populate it with data. I copied most of the code from Java to ColdFusion with few . I removed the ‘f’ at the end of price. This most likely it stood for ‘float’, AKA decimal numbers. The only change of the code block is in the last line. In Java, the code was being added to a link list. In ColdFusion I’m using ArrayAppend.

The Product Value Object

The product value object was simple to convert from a code standpoint. However, getting it to work with Flex was a bit tricker. Flex and ColdFusion are very finicky on how CFCs will be automatically converted to ActionScript objects. Before getting into ActionScript, first, I’ll discuss the CF side of things.

There are six instance variables in the ProductVO component. An id, name, description, price, image, and thumbnail. I recreated all of them in ColdFusion. The Java code created the instance variables as private. With ColdFusion code, I had to put them in the this scope, and define the components using the cfproperty tag.

A bunch of get and set methods were in the Java version of the product value object. I wasn’t sure how closely the CFC and AS files had to be related, so I converted them all from Java to CF.

The Java method had two methods, toString and equals that ColdFusion would not let me convert. You cannot create methods that have the same name as as reserved words in CF. toString and equals are the name of generic Java methods, and ColdFusion does not allow you to create methods that are similar to names of built in functions. Even though toString and equals are not built in functions, CF still throws the error. With no other alternative, I left those two methods out.

I tested my components with this code:

<cfscript>
MyObject = CreateObject(’component’,’productdelegate’);
</cfscript><cfdump var=“#MyObject.getProducts()#”>

Once I got everything the CFCs working, I moved back to the Flex code.

Defining the Services

In theory, all you need to do at this point is to tell the remoteObject tags to point at the ColdFusion code, not the Java code. Open up com/adobe/cairngorm/samples/store/business/services.mxml. This is the file that defines the services your application uses. You’ll see one remoteObject for the productService and one for the creditCardService.

<mx:RemoteObject id=“productService” destination=“productServiceImpl” showBusyCursor=“true”>
</mx:RemoteObject><mx:RemoteObject id=“creditCardService” destination=“creditCardServiceImpl” showBusyCursor=“true”>
</mx:RemoteObject>

We could create those destinations in ColdFusion’s services-config.xml file, but I instead chose to use the default ColdFusion destination. Comment out the previous lines and replace them with these:

<mx:RemoteObject id=“productService”
destination=“ColdFusion”
source=“htdocs.Experiments.CairngormStore.ProductDelegate”
showBusyCursor=“true”>

</mx:RemoteObject><mx:RemoteObject id=“creditCardService”
destination=“ColdFusion”
source=“htdocs.Experiments.CairngormStore.CreditCardDelegate”
showBusyCursor=“true”>

</mx:RemoteObject>

You’ll have to modify the source attribute to with the path to your remote objects. I was hoping that after this, I’d be good to go. Unfortunately that was not the case. It would work with only this change. I was wrong. First, I forgot to add the services-config.xml to my compiler settings. In Flex Builder, bring up properties on your project, select Flex Compiler, and add this compiler argument:

-services C:\CFusionMX7\wwwroot\WEB-INF\flex\services-config.xml

Everything still wasn’t working kosher. Next, open up the ActionScript value object (com/adobe/cairngorm/samples/vo/ProductVO.as). Find this line:

[RemoteClass(alias=“com.adobe.cairngorm.samples.store.vo.ProductVO”)]

And change it to point to your CFC:

[RemoteClass(alias=“htdocs.Experiments.CairngormStore.ProductVO”)]

If you built your CFC object in the same place as the Java object, you may not need to change this line.

I tested the code and it still wasn’t working. Ben Forta has a great post about gotchas when doing this. make sure that the instance variables are identical in the AS file and ColdFusion file. They must also be in identical order. Check! Use full paths to the CFCs. Check! Specify RemoteClass in the AS file. Check! What’s left? The answer is case sensitivity.

You want to be very careful about the case sensitivity in your paths. My CF code worked fine without case sensitivity, but Flex was being problematic. Once I addressed the case sensitive issue, I was able to get a proof of principle template working.

But within the context of the cairgorm store, Something was still wrong. I opened up the com/adobe/cairngorm/samples/store/command/GetProductsCommand.as file. I believe the framework must look for this file when invoking the GetProductsEvent.EVENT_GET_PRODUCTS event, since I could not find it explicitly called anywhere. If the fault event is getting called, you’ll see the message “Products could not be retrieved” when loading the file. This means something is wrong with your remoteObject configuration.

However, if you get no errors, but nothing is happening then something else must be wrong. Or if you see other error messages, something else must be wrong. During the course of my development I experienced both situations. First I added a quick Alert to the result function to be sure that the results are being returned properly:

Alert.show( “Products retrieved!” );

That helped me verify that the result method was indeed firing. There was one line of ActionScript in the result method that I didn’t understand:

var products : ICollectionView = ICollectionView( event.result );

This line creates a product variable of ICollectionView, and calls the ICollectionView constructor with the ‘event.result’ as an argument. Unfortunately, ICollectionView is an interface and does not appear to have a constructor. This line was throwing errors.

After some digging I found that an ArrayCollection inherits the ICollectionView methods through the ListCollectionView class. All code written against the ICollectionView interface should also work against an ArrayCollection. I replaced the ICollectionView line above with these two:

var products : ArrayCollection = new ArrayCollection ();
products.source = event.result as Array;

And bingo, everything started working.

Getting it set up

I’m not sure how much of this code I’m allowed to distribute, but my code changes, along with the Cairngorm Store and Cairngorm framework are attached to this post.

This is what you have to do to get it working:

  1. Download the file and unzip it to a web accessible directory. Create a Flex builder project pointed at that directory.
  2. Add bin/Cairngorm.swc to your Library path.
  3. Make sure that Services compiler argument is pointed to the services-config.xml for Flex.
  4. Open up services.mxml and change the attributes to point to the CFCs. Watch your case sensitivity
  5. Open up ProductVO.as and make sure that the RemoteClass directive properly points to the location of ProductVO. Watch your case sensitivity here too.
  6. Build and run your project.

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: