cancel
Showing results for 
Search instead for 
Did you mean: 

WURFL - Updated

This is an update to the "Simply WURFL" article written in 2009.  Since that time, some of the underlying libraries have changed, especially with regards to logging.

 

What is WURFL?  WURFL stands for Wireless Universal Resource FiLe.  From the WURFL webpage: "WURFL is a Device Description Repository (DDR), i.e. a framework that enables applications to map HTTP requests to a description of the capability of the mobile device that requests the page."  WURFL is licensed and maintained by ScientiaMobile, Inc. and includes a Java API so it can be used with the Stingray Traffic Manager.  It is up to the user to make sure they comply with the ScientiaMobile WURFL license.  You do not need to know Java to get these examples working since all the necessary class and jar files are either attached to this article or available at the links below, but if you do want to modify the source code, the source files are also attached.  To get started with WURFL you first need to ensure your Stingray Traffic Manager has working Java support and then download the following items.

 

  1. wurfl Java API: This code has been tested with version 1.4.4.3.  You will need wurfl-1.4.4.3.jar.
  2. wurfl: The WURFL respository.  wurfl.zip which can be used as is or extracted as wurfl.xml.  This code has been tested with version 2.3.4.
  3. commons-collections: Java interfaces, implementations and utilities.  This code has been tested with version 3.2.1.  You will need commons-collections-3.2.1.jar.
  4. commons-lang: Helper utilities.  This code has been tested with version 3.1.  You will need commons-lang3-3.1.jar.
  5. sl4j: Logging framework.  This code has been tested with version 1.7.5.  You will need slf4j-api-1.7.5.jar and slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar depending on what you want to see with regards to logging messages.  slf4j-noop-1.7.5.jar will cause all messages to be suppressed while slf4j-simple-1.7.5.jar will cause all messages to appear in the Stingray event log as warnings.

 

Upload the files specified using the Calalogs > Java UI page on your Stingray Traffic Manager. Now you're all set to experiment with the following examples.

 

The first sample servlet is a very simple use-case of WURFL, useful as a base for your own servlet or for debugging. The intention is for it to introduce the WURFL API as it fits within the framework of Stingray Java Extensions. WURFL is typically configured using the ConfigListener model, but Stingray doesn't go as far as implementing all the nuts and bolts required by full web applications. Our WURFL servlet must perform the required initialization itself, so an init method has been implemented that sets up a WURFLManager. As much work as possible is done at servlet initialization time. Then all the doGet method needs to do is check the request against the pre-initialised WURFLManager.

 

The source code and the compiled class for this example, StingrayWURFLInfoServlet are attached to this article.  To compile the code yourself on the Stingray instance, after uploading the specified jar files, you can upload the source file as well and then from the $ZEUSHOME/zxtm/conf/jars directory execute the following command:

 

javac -cp "$ZEUSHOME/zxtm/lib/*:$ZEUSHOME/zxtm/conf/jars/* StingrayWURFLInfoServlet.java

 

To compile it on a different machine, you will need the following jar files that were uploaded to Stingray: commons-collections-3.2.1.jar, commons-lang3.3.1.jar, wurfl-1.4.4.3.jar as well as servlet.jar and zxtm-servlet.jar from Stingray.  These are available via links in the Stingray UI under Catalogs > Java.  Please see the Stingray Java Development Guide for more information.

 

To get the servlet working follow these steps:

 

  1. Upload the StingrayWURFLInfoServlet.class file using Stingray Catalogs > Java UI page, leave the "Automatically create TrafficScript rule" checkbox ticked
  2. If you want devices that are not included in the standard WURFL repository, like desktop browsers, to be detected properly, a patches file can be created and uploaded to Stingray using the Catalogs > Java page.   If this file is uploaded, click on the StingrayWURFLInfoServlet link on the UI page and add the parameter "wurfl_patches" with the value of the name of the patches file, e.g., "web_browsers_patch.xml".  For details on creating a patches file see the ScientiaMobile website.
  3. Set up a Virtual Server for testing.  The pool can be set to "discard" since StingrayWURFLInfoServlet will create a response.
  4. Associate the auto-generated StingrayWURFLInfoServlet rule with the test Virtual Server as a request rule.
  5. Visit the virtual server from different browsers on different devices.

 

The result should be a page showing some general information about your browser at the top followed by the full table of WURFL capabilities and their values. The following screenshots show the top part of the output using a iPhone and a desktop browser:

 

Safari on iPhone:

 

iPhoneScreenShot.jpg

 

Firefox on Windows:

winfirefoxscreenshot.jpg

 

This is fine as a demo but not particularly useful for a real world application. What we really want is a module that can export capabilities so that they can be used by TrafficScript and other extensions. We also don't want to specifically be a doGet processor, so we'll modify the servlet along the lines described in the article: Writing TrafficScript functions in Java

 

in Java*</a>

 

There are a lot of capabilities covered by WURFL, the tables in the above screenshots go on for several pages - more than 500 capabilities in all. So we'll make it possible for TrafficScript to specify what capability fields it wants.

 

This gives us a servlet that could be used from TrafficScript using a rule such as the following example.  This rule extracts a few values and caches them in the Global Associative Array using the user-agent as the key so that subsequent requests for the same user-agent don't require another look up.

 

sub checkWURFL() {  
   $ua = http.getHeader( "User-Agent" );  
   if (!string.length($ua)) return;  
   $markup = data.get("WURFL" . $ua . "preferred_markup");  
   $datarate = data.get("WURFL" . $ua . "max_data_rate");  
   $brand = data.get("WURFL" . $ua . "brand_name");  
   $cookie = data.get("WURFL" . $ua . "cookie_support");  
   $os = data.get("WURFL" . $ua . "device_os");  
   $osVersion = data.get("WURFL" . $ua . "device_os_version");  
   $isWireless = data.get("WURFL" . $ua . "is_wireless_device");  
  
   if (string.length($markup)) {  
      log.info("Returning cached values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand .   
               ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless: " . $isWireless);  
      $1 = $markup;  
      $2 = $datarate;  
      $3 = $brand;  
      $4 = $cookie;  
      $5 = $os;  
      $6 = $osVersion;  
      $7 = $isWireLess;  
      return;  
   }  
  
   # no cached values for the UA, so run it through WURFL  
   java.run("StingrayWURFLServlet", "max_data_rate", "preferred_markup", "brand_name", "cookie_support",   
            "device_os", "device_os_version", "is_wireless_device");  
   $markup = connection.data.get("preferred_markup");  
   $datarate = connection.data.get("max_data_rate");  
   $brand = connection.data.get("brand_name");  
   $cookie = connection.data.get("cookie_support");  
   $os = connection.data.get("device_os");  
   $osVersion = connection.data.get("device_os_version");  
   $isWireless = connection.data.get("is_wireless_device");  
   data.set("WURFL" . $ua . "preferred_markup", $markup);  
   data.set("WURFL" . $ua . "max_data_rate", $datarate);  
   data.set("WURFL" . $ua . "max_data_rate", $datarate);  
   data.set("WURFL" . $ua . "brand_name", $brand);  
   data.set("WURFL" . $ua . "cookie_support", $cookie);  
   data.set("WURFL" . $ua . "device_os", $os);  
   data.set("WURFL" . $ua . "device_os_version", $osVersion);  
   data.set("WURFL" . $ua . "is_wireless_device", $isWireless);  
   log.info("Returning fresh WURFL values for User-Agent: " . $ua . ", datarate: " . $datarate . ", markup: " . $markup . ", brand: " . $brand .   
            ", cookies: " . $cookie . ", os: " . $os . ", version: " . $osVersion . ", iswireless:" . $isWireless);  
   $1 = $markup;  
   $2 = $datarate;  
   $3 = $brand;  
   $4 = $cookie;  
   $5 = $os;  
   $6 = $osVersion;  
   $7 = $isWireless;  
   return;  
}  
  
# simple case to test the checkWURFL function  
  
checkWURFL();  
  
$html = "Max Data Rate: " . $2 . "kbps
Preferred Markup: " . $1 . "
Brand: " . $3 . "
OS: " . $5 . "
Version: " . $6 . "
Cookie Support: " . $4 . "
Is Wireless Device: " . $7; http.sendResponse("200", "text/html", $html, "");

 

The corresponding StingrayWURFLServlet source and compiled class file and slf4j-stingray.jar file are attached to this article.  The slf4j-stingray.jar file includes an implementation to properly direct slf4j messages to the Stingray Event Log. The logging code is documented here: slf4j Logging and Stingray Java Extensions. The slf4j-noop-1.7.5.jar or slf4j-simple-1.7.5.jar file uploaded for the previous example should be deleted.  To control what level of logging to output to the Stingray Event Log, a parameter, "log_level" can be set to a value (case insensitive) of "debug", "info", "warn" or "error".  If no value is set, the default is "warn".

 

Rather than the doGet method implemented in the StingrayWURFLInfoServlet we now have a simpler service method.

 

public void service(HttpServletRequest request, HttpServletResponse response)  
         throws ServletException, IOException {  
  
      String[] args = (String[])request.getAttribute( "args" );  
      if (args == null || args.length == 0) {  
         throw new ServletException("error: no arguments supplied");  
      }  
  
      Device device = manager.getDeviceForRequest(request);  
      Map capabilities = device.getCapabilities();  
  
      for (int i = 0; i < args.length; ++i) {  
         String cap = (String)capabilities.get(args[i]);  
         if (cap == null) {  
            Logger.warn("Java Servlet StingrayWURFLServlet: No capability found matching: " + args[i]);  
         } else {  
            ((ZXTMServletRequest)request).setConnectionData( args[i], cap );  
         }  
      }  
   }  

 

There is much more you could do with the StingrayWURFLServlet Java Extension provided in this article. Think of it as a starting point for developing your own solutions to improve the web browsing experience of your mobile users.

 

A few examples:

 

    1. The max_data_rate value retrieved above could be used to reduce image quality or size for people with low bandwidth devices. This would result in a snappier web browsing experience for these people as there would be less data for them to retrieve over their slow links.
    2. The preferred_markup value can be used to direct clients to different backend pools based on whether they can handle XHTML, or should be served WML.
    3. The streaming_flv value can be checked to see if the device has Flash video support and can thus be sent to your full bells-and-whistles website. A scaled down version could be made for those that only have Flash Lite, which is specified by the value of flash_lite_version. Devices that don't support flash at all (such as the iPhone) can be sent to a plain HTML version of the site, or WML as in the previous bullet point.

 

Speaking of the iPhone, it doesn't have Flash but its browser does have excellent AJAX support. You know your site is being visited by an iPhone user using the normal iPhone web browser when model_name is "iPhone" and mobile_browser is "Safari". If there are important differences between iPhone OS releases you can also check model_extra_info ordevice_os_version for this detail.

    1. For AJAX in general there are a whole set of specific properties:
      • ajax_manipulate_css
      • ajax_manipulate_dom
      • ajax_support_event_listener
      • ajax_support_events

 

Up to date documentation on all the WURFL capabilities can be found on the WURFL website.

 

<a title=h

 

Any of these values can also be passed on to your backend nodes of course. You could add special headers containing the values, a cookie, or a URL argument. You could also cache browser capabilities uniquely to each device with cookies or another method of session tracking, rather than cache the capabilities based solely on the user agent. Then you could offer users the ability to override special mobile device modes.

 

We would love to hear your ideas and learn how we can help you in this exciting area - the opportunities are practically limitless.

Comments

There is an article on WURFL in Riverbed Connections inviting users to share their uses of WURFL, so give it look and if you are using WURFL we would love to hear from you.

Adaptive Content: We Give You the Code, You Give Us Insight | Riverbed Connections