Sep 09
ColdFusion 8 CFC serialization
Posted by James Netherton | Sunday 09 September 2007 4:44 PM | In ColdFusion
I was skimming through an old Java book and came across an example that used Java's ObjectOutputStream to serialize objects. This got me thinking about a much trumpeted new feature in ColdFusion 8, the ability to serialize CFC objects.
Serialization enables you to 'save' the state of an object, persist it and then read it back into memory at a later time. You can use the Java ObjectOutputStream class to serialize a ColdFusion component, together with all of its instance data.
First, I create a couple of simple CFC's, I've left out many of the cffunction and cfargument attributes for quickness:
SimpleObject.cfc
<cfset variables.name = ""/>
<cfset variables.age = 0/>
<cfset variables.randomStuff = structNew()/>
<cfset variables.address = ""/>
<cffunction name="init">
<cfset variables.randomStuff["year"] = 2007/>
<cfset variables.randomStuff["month"] = 9/>
<cfset variables.randomStuff["day"] = 9/>
<cfreturn this/>
</cffunction>
<cffunction name="getName">
<cfreturn variables.name/>
</cffunction>
<cffunction name="getAge">
<cfreturn variables.age/>
</cffunction>
<cffunction name="getRandomStuff">
<cfreturn variables.randomStuff/>
</cffunction>
<cffunction name="getAddress">
<cfargument name="address"/>
<cfreturn variables.address/>
</cffunction>
<cffunction name="setName">
<cfargument name="name"/>
<cfset variables.name = arguments.name/>
</cffunction>
<cffunction name="setAge">
<cfargument name="age"/>
<cfset variables.age = arguments.age/>
</cffunction>
<cffunction name="setAddress">
<cfargument name="address"/>
<cfset variables.address = arguments.address/>
</cffunction>
</cfcomponent>
Address.cfc
<cfset variables.street = ""/>
<cfset variables.city = ""/>
<cfset variables.country = ""/>
<cfset variables.postcode = ""/>
<cffunction name="init">
<cfreturn this/>
</cffunction>
<cffunction name="getStreet">
<cfreturn variables.street/>
</cffunction>
<cffunction name="getCity">
<cfreturn variables.city/>
</cffunction>
<cffunction name="getCountry">
<cfreturn variables.country/>
</cffunction>
<cffunction name="getPostCode">
<cfreturn variables.postcode/>
</cffunction>
<cffunction name="setStreet">
<cfargument name="street"/>
<cfset variables.street = arguments.street/>
</cffunction>
<cffunction name="setCity">
<cfargument name="city"/>
<cfset variables.city = arguments.city/>
</cffunction>
<cffunction name="setCountry">
<cfargument name="country"/>
<cfset variables.country = arguments.country/>
</cffunction>
<cffunction name="setPostCode">
<cfargument name="postcode"/>
<cfset variables.postcode = arguments.postcode>
</cffunction>
</cfcomponent>
Now I need to write code that will serialize instances of the above objects. First I must instantiate them and provide some dummy data:
cfcserialize.cfm
simpleObject = createObject("component","simpleObject").init();
simpleObject.setName("James");
simpleObject.setAge(26);
addressObject = createObject("component","address").init();
addressObject.setStreet("Liverpool Street");
addressObject.setCity("London");
addressObject.setCountry("United Kingdom");
addressObject.setPostCode("ABC 123");
simpleObject.setAddress(addressObject);
</cfscript>
I can now serialize the simpleObject variable, which is of course an instance of the simpleObject class. Notice I only have to serialize simpleObject as it holds a reference to an address object.
cfcserialize.cfm continued
fos = createObject("java","java.io.FileOutputStream").init(expandPath("./cfc.obj"));
oos = createObject("java","java.io.ObjectOutputStream").init(fos);
oos.writeObject(simpleObject);
oos.close();
</cfscript>
Run the script and if there were no errors, you should see a file named 'cfc.obj' within the directory where the script was executed from. Peek inside the file and you'll see a bunch of junk which represents the CFC serialized data.
The next obvious step is to deserialize the data. For this we'll be using the Java ObjectInputStream class.
cfcdeserialize.cfm
fis = createObject("java","java.io.FileInputStream").init(expandPath("./cfc.obj"));
ois = createObject("java","java.io.ObjectInputStream").init(fis);
simpleObject = ois.readObject();
ois.close();
address = simpleObject.getAddress();
</cfscript>
The readObject method is used to retrieve the CFC instance back from the 'cfc.obj' file. Notice that once we have read the object back from the file, we can start using it immediately, as the code example does by retrieving a reference to the address object.
Now we can write out the dummy data we set earlier to prove that the serialization and deserialization process has really worked.
cfcdeserialize.cfm continued
Name: #simpleObject.getName()#<br/>
Age: #simpleObject.getAge()#<br/><br/>
Street: #address.getStreet()#<br/>
City: #address.getCity()#<br/>
Country: #address.getCountry()#<br/>
Post Code: #address.getPostCode()#<br/><br/>
</cfoutput>
<cfdump var="#simpleObject.getRandomStuff()#">
One thing you may want to do is wrap a cftry / cfcatch block around the serialization and deserializtion code so that you can close either the ObjectInputStream or ObjectOutputStream. You could potentially run into resource leaks if ColdFusion encounters and error and hasn't reached the code that closes the object stream.
Overall, it's quite a powerful technique. I just need to think of some use cases where it would come in handy.....
3 Comments
[Post comment]
1
Posted by David Stamm | Tuesday 20 November 2007 12:44 PM
I had heard rumors of CF8's serialization capabilites, but I had no idea how it easy it was to implement. Thanks for the excellent post!
No one in my development shop can think of an excuse to use serialization either. Good to know we can rely on the familiar Java API if we do think of something!
2
Posted by Brook Davies | Monday 03 December 2007 10:27 AM
BUT it does not work with arrays in your CFC, de-serialization fails. Thats lame, seems to me like a have ass implementation. Who doesn't use arrays? I hope this gets fixed so that it is useable in the real world and not just in a pretty test case with a simple CFC.
3
Posted by Peter | Monday 30 June 2008 3:08 AM
Thanks for this.