views:

176

answers:

2

Hi all

We are using EHCache with CF 8 to cache stuff on a central server using a RESTful interface over HTTP. I am trying to cache a cfquery object to the cache server.

I can get this to work if I call EHCache direct (i.e. store it in a local cache) but if I try to cache on a remote server over HTTP I am running into problems.

The code I am using is as follows:

<cfhttp url="http://localhost:8080/myCache/myKey"
  method="put" 
  result="r" 
  timeout="2" 
  throwonerror="true" >
    <cfhttpparam type="body" value="#ARGUMENTS.item#" />
</cfhttp>

CF doesn't like this reference to #ARGUMENTS.item# and it complains Complex object types cannot be converted to simple values.

Can anyone give me an example of how to put an object over http using CF? If this is not possible with CF then a Java example would be the next best thing.

Many thanks in advance!

PS: I do not want to use serialization to text/JSON etc. as this approach has issues with data integrity and most importantly it's not fast enough.

+3  A: 

Yes, you will have to serialize the arguments structure because there is no way to pass binary objects via HTTP without some form of serialization. As has already been mentioned, you can use JSON to serialize the object. An alternative to JSON that would also work is the cfwddx tag which can convert the object into XML. But JSON is probably better because it's not as verbose as the XML generated by cfwddx.

Richard Davies
+2  A: 

I figured out I can use the exact code above if I do the following to the ARGUMENTS.item variable before I pass it to the function:

<cfquery name="qData" datasource="#VARIABLES.dsn#">
select * from myData
</cfquery>

<!--- Setup and init Java objects --->
<cfset byteArrayOutputStream = createObject("java","java.io.ByteArrayOutputStream") />
<cfset objectOutputStream = createObject("java","java.io.ObjectOutputStream") />
<cfset byteArrayOutputStream.init() />
<cfset objectOutputStream.init(byteArrayOutputStream) />

<!--- Serialize the cfquery object --->
<cfset objectOutputStream.writeObject(qData) />
<cfset serializedQuery = toBase64(byteArrayOutputStream.toByteArray()) />
<cfset objectOutputStream.close() />    

<!--- Stick in the cache --->
<cfset myCache.put(myCacheName, key, serializedQuery) />

Bingo! After that serializedQuery is there to be used and it can be PUT across the wire via HTTP. When you get it back from EHCache, the you need to do the following:

<!--- Get result value from cache --->
<cfset cacheData = myCache.get(myCacheName, key) />

<!--- Java objects setup --->
<cfset byteArrayInputStream  = createObject("java","java.io.ByteArrayInputStream") />
<cfset objectInputStream = createObject("java","java.io.ObjectInputStream") />

<!--- deserialize --->
<cfset ba = toBinary(cacheData) />
<cfset byteArrayInputStream.init(ba) />
<cfset objectInputStream.init(byteArrayInputStream) />
<cfset deserializedQuery = objectInputStream.readObject() />
<cfset objectInputStream.close() />

<!--- Dump query --->
<cfdump var="#deserializedQuery#" />

This type of serialization is very, very quick too. I can understand others using JSON representations but I'm not sure they are as fast.

Anyway, this seems to work great for me. Thank you all for your input. I hope others find this useful.

Ciaran Archer