SolutionsTools & SDKSupport  



Quick Links
 
WURFL: The Java API
 
 
By Luca Passani

Last month we featured an article Introducing WURFL Part 1 and it included the format of the WURFL repository. wurfl.xml is just a big XML file. In Part 1, we explained that handling raw XML is not required to use WURFL, since the project also makes programming interfaces (APIs) available for the most popular platform (Java and PHP).

In this article, Part 2, we will start where the Java example in last month's article left off: taking a deeper look at the WURFL Java API. There are two main functions performed by the API:

   given : a User-Agent string
   return: a WURFL device ID
and
   given : a WURFL Device ID and a capability name
   return: a capability value

Retrieving a Device ID through the User-Agent
 
Two different methods in the UAManager class can do the job:
  • getDeviceIDFromUA(String user-agent)
    returns a String representing a WURFL device ID. This function looks in the WURFL for an exact match. If you don't get it, you will receive "generic" (i.e. the ID of the unrecognized device).
  • getDeviceIDFromUALoose (String user-agent)
    also returns a string representing a WURFL device ID. The difference is that this method won't take no for an answer nearly as easily. In case an exact match is not found, a few heuristics are applied to find a device that's reasonably similar.
At the time of this writing, the heuristics are the following:
  1. Chars are removed one at the time from the end of the string. At each step a match is attempted between the string and the first part of each existing user-agent. This ends when either a match is found or the query string has become five chars.
  2. Vodafone has introduced on its network a bunch of devices that add the "Vodafone/" string at the beginning of the user-agent. Those devices are listed in the WURFL. In case of a miss, the "Vodafone/" string is removed and the matching algorithm is applied from start.
  3. If a match is not found, the matching algorithm looks for hints that still can lead to some useful assumptions: "UP.Browser/4","UP.Browser/5","UP.Browser/6", "Series60","Mozilla/4.0" and so on.
  4. Defeat! return "generic".
If you wonder about the performance of this animal, don't worry. Queries are cached, so any subsequent query with the same data is resolved with a simple HashMap lookup.
 
Retrieving Capability Values
 
Once you have a device ID, you are ready to rock and roll: just use getCapabilityForDevice() to retrieve the value of any capability. This is a method of the CapabilityMatrix class.

- getCapabilityForDevice(String deviceID, String capabilityName)
Returns the value of a capability for a given device. There is not much to add here:

System.out.println(cm.getCapabilityForDevice("sie_m50_ver1","resolution_width"));
Q: Excuse me, Mr. Capability Matrix, how many horizontal pixels can I rely on for a Siemens M50?
A: 110 pixels, Sir.


Simple and powerful.
 
Setting up the Singletons
 
The methods above are simple to use, but there is a tiny bit of work you need to do before you can use them. Parsing the WURFL and traversing it to retrieve information is heavy stuff. The API is architected in a way that time-consuming operations are performed either at start-up or only once.

For this reason, you need to create a UAManager and CapabilityMatrix before you invoke the corresponding methods above. For the same reason, you don't just create a UAManager and CapabilityMatrix using 'new()', rather you request ObjectsManager to give them to you. ObjectsManager will make sure that a new object is created only if you are the first to request. Otherwise, you get the existing one (or better a reference to it, but I assume you have a smattering of Java from before). UAManger and CapabilityMatrix are examples of Singletons, in case you are familiar with pattern-theory parlance. If you are not, there's no need to worry: Just make sure that you have these two lines in your code, and you will have someone to talk to when you need to make a query:

UAManager uam = ObjectsManager.getUAManagerInstance();
CapabilityMatrix cm = ObjectsManager.getCapabilityMatrixInstance();

A link to a resource about Singletons can be found in the references.
 
Time for an Example
 
The java snippet at the end of the WURFL intro last month was an example of usage of the Java API. Let's have a look at something more practical here: the implementation of the 'br' tag of the WALL library. There is no common 'br' (breakline) tag for CHTML, XHTML MP and WML, but ... almost.
  • Most CHTML devices honor the <br> tag and simply ignore <br/> tags
  • Most XHTML MP devices will honor both <br/> and <br> tags.
  • Some XHTML MP device will throw an error if a single unslashed <br> is found and no information will be displayed (except a pretty useless error message)
This is a job for WALL, the Wireless Abstraction library:

------------------------------------------------------------------------------
   public int doStartTag() throws JspException {

       try {
         cm = ObjectsManager.getCapabilityMatrixInstance();
         uam = ObjectsManager.getUAManagerInstance();
         request = (HttpServletRequest) pageContext.getRequest();
         
         //get the user agent
         UA = TagUtil.getUA(this.request);

         //Check the capability string
         warning = TagUtil.checkCapability("preferred_markup");
         if (warning.length() > 0) {
             throw new JspException(warning);
         }

         device_id = uam.getDeviceIDFromUALoose(UA);

         capability_value = cm.getCapabilityForDevice(device_id, "preferred_markup");
	 capability_value = TagUtil.getWallMarkup(capability_value);

         if ( capability_value.startsWith("xhtmlmp") ) {
	     try {
		 JspWriter out = pageContext.getOut();
		 out.print("<br/>");
	     } catch(IOException ioe) {
		 throw new JspException("Error in WALL tag 'br': " + ioe.getMessage());
	     }
	 }

         if ( capability_value.startsWith("wml") ) {
	     try {
		 JspWriter out = pageContext.getOut();
		 out.print("<br/>");
	     } catch(IOException ioe) {
		 throw new JspException("Error in WALL tag 'br': " + ioe.getMessage());
	     }
	 }
	 
	 //CHTML <br> without trailing slash
	 if ( capability_value.startsWith("chtml") ) {
	     try {
		 JspWriter out = pageContext.getOut();
		 out.print("<br>");
	     } catch(IOException ioe) {
		 throw new JspException("Error in WALL tag 'br': " + ioe.getMessage());
	     }

	 }

         return (SKIP_BODY);

      } catch (Exception e) {
	  throw new JspException("Error in WALL tag 'br': " + e.getMessage());
      }

   }
------------------------------------------------------------------------------


As usual, real apps need to spend much time in managing errors, so, here is an abridged version that shows the relevant parts:

------------------------------------------------------------------------------
1         cm = ObjectsManager.getCapabilityMatrixInstance();
2         uam = ObjectsManager.getUAManagerInstance();
3         request = (HttpServletRequest) pageContext.getRequest();
4         
5         //get the user agent
6         UA = TagUtil.getUA(this.request);
7
8         device_id = uam.getDeviceIDFromUALoose(UA);
9
10        capability_value = cm.getCapabilityForDevice(device_id, "preferred_markup");
11        capability_value = TagUtil.getWallMarkup(capability_value);
12
13        if ( capability_value.startsWith("xhtmlmp") ) {
14		 out.print("<br/>");
15        }
16
17        if ( capability_value.startsWith("wml") ) {
18		 out.print("<br/>");
19	  }
20
21        //CHTML <br> without trailing slash
22        if ( capability_value.startsWith("chtml") ) {
23		 out.print("<br>");
24        }
------------------------------------------------------------------------------
  • lines 1 and 2 create our singletons.
  • lines 3 and 6 extract the User-Agent string out of the request. TagUtil.getUA() simple gives the developer a chance to override the User-Agent HTTP header for debugging purposes.
  • line 8 does whatever it can to retrieve the ID of the device definition that best models the requesting device out of the user-agent string.
  • lines 10 and 11 query the 'preferred_markup' for the device. TagUtil.getWallMarkup() normalizes the retrieved value into one of the three that WALL expects ('wml','xhtmlmp' and 'chtml')
  • lines 13 through 24 send the most appropriate break-line tag back according to the preferred markup.
It's simple stuff. No rocket science here.
 
All That We Have Left Behind
 
The main functionalities of the API are covered, but a bunch of other things are worth mentioning.
  • CapabilitMatrix.isCapabilityIn(String capability_name)
    In real apps, you may want to check that a given capabilities is defined in the WURFL before querying it. An error here and you may spend more time than you wish tracking out what went wrong where. This method does that.

  • ListManager is also a singleton and it supports a bunch of methods to retrieve lists of devices and capabilities you can loop on. In practical applications, you don't need those methods, since you typically deal with one device at the time. The methods become necessary when you want to implement WURFL utilities to analyze the WURFL repository and build reports. For example, you may request a list of all actual devices (devices with 'actual_device_root==true').

  • WurflDevice encapsulates in a Java class everything the WURFL knows about a given device. Also not very useful unless you need to build reports of some kind. This also includes the use of the WurflDevice.getActual_device_root(), which can be used to tell if a given device is marked as 'actual device' in the WURFL.

  • WurflSource is an interface that lets you initialize the API using alternative sources from which to read wurfl.xml. Totally honestly, this functionality is not very tested. If it doesn't work, give the WURFL team a holler on the WMLProgramming mailing list.

Coming up: PHP and WURFL
 
This pretty much tells you all you need to know to use the Java API in your own application. You can go one level deeper by looking at the Java docs or, even the WURFL API source code available on the Sourceforge CVS repository. Next month we will cover the WURFL API for PHP. I'll talk to Andrea and try to get him to write that part of the tutorial. See you soon.
 
References
 
The WURFL Home Page: it also carries the APIs for the different platforms.

WURFL API Javadocs

WALL 'br' Tag

The Singleton Pattern (in Java)

Download a "stable" version of the WURFL file (also zipped)

Latest WURFL version through CVS
 
 
Copyright © 2000-2008 Openwave Systems Inc.    Openwave  |  Terms & Conditions  |  Privacy Policy