Grails Mobile Browser Detection with WURFL
Here's a quick and cheap solution for detecting and redirecting clients on mobile browsers to mobile versions of your Grails application using WURFL.
WURFL is basically an XML file containing information about different mobile browsers and their capabilities such as WAP, WML, PNG, J2ME, etc. support.
*** Disclaimer: WURFL is not always 100% correct on its detection and capability look-up. WURFL depends on users to report resource data and therefore can be prone to missing or incorrect resource definitions.
Having said that... here's how to integrate mobile browser detection into your Grails application.
First, download the latest WURFL resource file and place it in your classpath. I put mine in /src/java/
.
Since the root WURFL resource file's main purpose is to define the capabilities of mobile browsers, we'll need to download a patch file (found here) to have it be able to recognize regular web browsers.
Download the patch file and save it to your classpath as well.
Next, we'll grab the WURFL Java API and add it to our "/lib" folder. The link below is to the Hello World sample application but we only need the wurfl-x.x.x-x.jar
.
The backport-util-concurrent.jar
for your particular Java version is also required and should be added to the /lib
folder.
Download: wurfl-latest.zip
Download: web_browsers_patch.xml
Download: wurfl-x.x.x-x.jar
Download: backport-util-concurrent.jar
Now we'll configure Grails/Spring to load the required classes.
Edit the Spring beans conf at /grails-app/conf/spring/resources.groovy
// Place your Spring DSL code here
beans = {
...
xmlns util: "http://www.springframework.org/schema/util"
// WURFL
// Groovy resource implementation of classpath:spring-wurfl.xml
wurflResource(org.springframework.core.io.ClassPathResource, "/wurfl-latest.zip") { }
wurflSpringResource(net.sourceforge.wurfl.core.resource.SpringXMLResource, ref("wurflResource")) { }
// Begin Patch
wurflPatch(org.springframework.core.io.ClassPathResource, "web_browsers_patch.xml") { }
wurflPatchSpringResource(net.sourceforge.wurfl.core.resource.SpringXMLResource, ref("wurflPatch")) { }
util.list(id: "patchResourceList") {
ref(bean: "wurflPatchSpringResource")
}
wurflPatchResources(net.sourceforge.wurfl.core.resource.WURFLResources, ref("patchResourceList")) { }
// End Patch
wurflModel(net.sourceforge.wurfl.core.resource.DefaultWURFLModel, ref("wurflSpringResource"), ref("wurflPatchResources")) { }
wurflMatcherManager(net.sourceforge.wurfl.core.handlers.matchers.MatcherManager, ref("wurflModel")) { }
wurflDeviceProvider(net.sourceforge.wurfl.core.DefaultDeviceProvider, ref("wurflModel")) { }
wurflService(net.sourceforge.wurfl.core.DefaultWURFLService, ref("wurflMatcherManager"), ref("wurflDeviceProvider")) { }
wurflManager(net.sourceforge.wurfl.core.DefaultWURFLManager, ref("wurflService")) { }
wurflUtils(net.sourceforge.wurfl.core.WURFLUtils, ref("wurflModel")) { }
wurflHolder(net.sourceforge.wurfl.core.DefaultWURFLHolder, ref("wurflManager"), ref("wurflUtils")) { }
...
}
Next, we'll create a simple service to detect the browser type from the request.
package org.test.services
import org.apache.commons.lang.StringUtils
import net.sourceforge.wurfl.core.Device
class MyWurflService {
// Inject the WurflManager we defined in resources.groovy
def wurflManager
def Boolean isMobile(request) {
def Device device = this.getDevice(request)
def String iwdCapability = device.getCapability("mobile_browser")
// Debugging System.out.println("Capability of 'mobile_browser': ${iwdCapability}")
return StringUtils.isNotBlank(iwdCapability)
}
def Device getDevice(request) {
Device device = wurflManager.getDeviceForRequest(request)
def capabilities = device.getCapabilities()
// Debugging capabilities.each { System.out.println(it) }
return device
}
}
And finally, we'll test our service with a simple redirection.
package org.test.controllers
class TestController {
// Inject org.test.services.myWurflService
def myWurflService
def beforeInterceptor = {
if (null == params.action || params.action == 'index') {
if (myWurflService.isMobile(request)) {
redirect(action: 'mobile')
}
}
}
def index = { }
def option = { }
def mobile = { }
}
The controller catches requests to /text/index
and calls the myWurflService.isMobile()
method passing the request object.
If WURFL determines that the request is from a mobile browser, the user is redirected to the /test/mobile
action.
You can extend functionality even further by detecting what markup is used on the device making the request and redirecting accordingly.
You can easily test your app using the User Agent Switcher extension in Firefox.