Tuesday 31 March 2015

Getting WURFL to work with CFML

This is a bit of an insight-free article (more so than usual, I mean), as I did this work ages ago so cannot recall the minutiae of the hassle it gave me, other than recalling there was some. A community member has - coincidentally - asked about the same exercise, so I'm gonna reproduce it here.

WURFL is - rather leadenly - an acronym for "Wireless Universal Resource FiLe": it's a library used for device identification. As I said, a while back we examined this route to for viability to identify devices, but in the end it wasn't a good fit for what we wanted to do or how we wanted to do it, so I abandoned the work. Still: I got it working so I'll repeat the exercise now. Shakir mentions ColdFusion or Railo... I'm gonna do this on Lucee instead. Railo being toast, after all.

First things first... get hold of the WURFL files. They provide solutions for Java, PHP, .Net and a Scala implementation. Obviously we're going for their Java implementation here. At time of writing, was the current version, so I'm grabbing that. Drilling into the directory structure, I find wurfl- (no direct link) which I'll download.

In that zip there's a jar, a lib directory and another zip file (and all the other usual crap one gets with a downloaded library:


Inside the zip file is wurfl.xml.

I had no idea what the hell I was supposed to do with all these files, so I just had a look at the Java Getting Started page (WURFL Java API 1.5), which has a bunch of Java code on it. As is the case with Java, it takes an awful lot of code to get anything done, but I figured I could distill it down to the bare minimum CFML code to achieve a proof of concept.

The "installation" was to dump wurfl-, the XML file and everything in the lib subdirectory into my WEB-INF\lucee\lib directory.

The code there loads the wurfl.xml file via web.xml, but I wanted my example to load the file via code, so I had a look at the API and found an approach using an XMLResource object. Other than that I simply excised all the Java cruft, and came up with this CFML version:

// wurfl.cfm
wurflXmlPath = expandPath("\WEB-INF\lucee\lib\wurfl.xml")
userAgent = "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.99 Mobile Safari/537.36"

resource = createObject("java", "net.sourceforge.wurfl.core.resource.XMLResource").init(wurflXmlPath)
wurflModel = createObject("java", "net.sourceforge.wurfl.core.resource.DefaultWURFLModel").init(resource, [])
engine = createObject("java", "net.sourceforge.wurfl.core.GeneralWURFLEngine").init(wurflModel)

device = engine.getDeviceForRequest(userAgent)
dump(var=device, label="Device")

os = device.getCapability("device_os")
dump(var=[os], label="OS")

capabilities = device.getCapabilities()
dump(var=capabilities, label="Capabilities")

Note that all this is pretty slow to run - 2-3sec on my machine (having removed the dumps so they don't impact processing) - so one would not want to be doing all that on every request. If I timed only the bit that needs to be done every request, it's still taking 0.5-1sec on my machine, which, I'm afraid, invalidates it as a prospect as a solution for me. Which is a big part of the reason why we didn't run with it.

And in this example I've used the user agent string from my phone. Normally one would pass CGI.USER_AGENT in there, but I'm running this on my desktop machine so not very real-world.

Oh... and the output of all that:

(that latter dump went on and on and on, but you get the idea).

OK, so that's that. Told you it was lacking in insight.