A user on Twitter asked me about how to use cached data to serve up XML files. He had tried to use cfcache along with cfcontent and ran into trouble. I thought I'd whip up a quick example of how you could manually cache the data yourself, and I created a quick ColdFusion 9 version as well. Ok, so let's look at some sample code.
First, here is an example of the "slow" version. It creates an XML feed based on URL parameters. The code that generates the XML is artificially slowed down via the sleep call:
<cffunction name="getXML" output="false" returnType="string">
<cfargument name="type" type="string" required="true">
<cfset var res = "">
<cfsavecontent variable="res"><cfoutput><products type="#arguments.type#"><product></product></products></cfoutput></cfsavecontent>
<cfset sleep(2000)>
<cfreturn res>
</cffunction>
<cfparam name="url.type" default="cool">
<!--- ensure valid type --->
<cfif not listFindNoCase("cool,sexy,suave", url.type)>
<cfset url.type = "cool">
</cfif>
<cfset xml = getXML(url.type)>
<cfcontent type="text/xml" reset="true"><cfoutput>#xml#</cfoutput>
The basic gist of the code is: Get and validate a URL parameter type. Get the XML. Serve it up. Done. To make this a bit quicker, we can simply use the Application scope. Consider this newer version:
<cfapplication name="xmlcachingdemo">
<cffunction name="getXML" output="false" returnType="string">
<cfargument name="type" type="string" required="true">
<cfset var res = "">
<cfsavecontent variable="res"><cfoutput><products type="#arguments.type#"><product></product></products></cfoutput></cfsavecontent>
<cfset sleep(2000)>
<cfreturn res>
</cffunction>
<cfparam name="url.type" default="cool">
<!--- ensure valid type --->
<cfif not listFindNoCase("cool,sexy,suave", url.type)>
<cfset url.type = "cool">
</cfif>
<!--- check cache --->
<cfif not structKeyExists(application, "xmlcache_#url.type#")>
<cfset application["xmlcache_#url.type#"] = getXML(url.type)>
</cfif>
<cfcontent type="text/xml" reset="true"><cfoutput>#application["xmlcache_#url.type#"]#</cfoutput>
Note that I added a cfapplication tag directly in my code. I don't recommend that - but for a quick test, it does the trick. The main difference though is in the logic right before we serve up the XML. Now we look in the Application scope for a key based on the URL type. If the user requests the sexy xml, the key will be: xmlcache_sexy. Each of the three XML types will have their own key and their own place in the Application scope. If it does not exist, we simply store it. If you run this example, you will see that the first hit for each type is slow, but then the next request will be served up immediately.
One issue with this version though is that there is no time out for the cache. You can get around that by storing metadata with the key. In order words, store both the XML and the time it was generated. Then check that time and see if you should refresh the cache. Certainly doable, and it would only add a few more lines of code, but let's see how ridiculously easy this is in ColdFusion 9:
<cfapplication name="xmlcachingdemo2">
<cffunction name="getXML" output="false" returnType="string">
<cfargument name="type" type="string" required="true">
<cfset var res = "">
<cfsavecontent variable="res"><cfoutput><products type="#arguments.type#"><product></product></products></cfoutput></cfsavecontent>
<cfset sleep(2000)>
<cfreturn res>
</cffunction>
<cfparam name="url.type" default="cool">
<!--- ensure valid type --->
<cfif not listFindNoCase("cool,sexy,suave", url.type)>
<cfset url.type = "cool">
</cfif>
<!--- check cache --->
<cfset cachedXML = cacheGet("xmlcache_#url.type#")>
<cfif isNull(cachedXML)>
<cfset cachedXML = getXML(url.type)>
<cfset cachePut("xmlcache_#url.type#",cachedXML, createTimeSpan(0,0,10,0))>
</cfif>
<cfcontent type="text/xml" reset="true"><cfoutput>#cachedXML#</cfoutput>
I've switched my code to now check against the built in cache system using cacheget. I use the same keyname as before though. If the result is null, I run the slow function and store it, but note now that I can easily make use of the cache system's time out value. Now my XML will be cached for 10 minutes. I don't need to check the time at all, cacheGet will simply return a null when the item times outs.
p.s. The user on Twitter had mentioned issues with cfcache and cfcontent. I did not notice any issue with that though. I put cfcache on top of my page and it just worked. I've asked him to please let us (you and I, gentle reader) know more about the error/problem he saw. Maybe cfcache would be ok for him. Personally though I prefer the more manual approach in ColdFusion Not 9.