Sunday, 18 March 2012

WURFL in ASP.NET Applications

An application that uses WURFL is simply an application that needs to check a number of effective browser capabilities in order to decide what to serve.  The WURFL database contains information about a huge number of devices and mobile browsers. This information essentially consists in a list of over 500 capabilities. Each device, uniquely identified by its user-agent string (UAS), falls into a group and inherits the capabilities defined for the group, plus the delta of base capabilities that make it unique.
Programming a WURFL-based application is therefore a fairly easy task. All you need to do is loading WURFL data in memory and query for specific capabilities you’re interested in for the currently requesting browser/device. Let’s step into some more details for an ASP.NET Web Forms application. (If you’re using ASP.NET MVC, don’t panic—it’s exactly the same.)

Loading WURFL Data

WURFL data changes over time but it’s not certainly real time data. So you can blissfully load all of it at the startup of the application and keep it cached for as long as you feel comfortable. When an update is released, you just replace the data and restart the application. Both programming and deployment aspects of the operation are under your total control.
In an ASP.NET application, the Application_Start method is the place where you perform all one-off initialization work. Here’s how you can instruct the method to load and cache WURFL data.  
public class Global : HttpApplication
{
   
public const String WurflManagerCacheKey = "__WurflManager";
   
public const String WurflDataFilePath = "~/App_Data/wurfl-latest.zip";
    public const String WurflPatchFilePath = "~/App_Data/web_browsers_patch.xml";
    private void Application_Start(Object sender, EventArgs e)
    {
       
var wurflDataFile = HttpContext.Current.Server.MapPath(WurflDataFilePath);
        var wurflPatchFile = HttpContext.Current.Server.MapPath(WurflPatchFilePath);
        var configurer = new InMemoryConfigurer()
                .MainFile(wurflDataFile)
                .PatchFile(wurflPatchFile);
        var manager = WURFLManagerBuilder.Build(configurer);
        HttpContext.Current.Cache[WurflManagerCacheKey] = manager;
    }
}
You can specify multiple patch files by simply calling the PatchFile method multiple times in the same chained expression.
var configurer = new InMemoryConfigurer()
                .MainFile(wurflDataFile)
                .PatchFile(wurflPatchFile1)
                .PatchFile(wurflPatchFile2);
As you can see, both file names and cache details are under your control. You might want to maintain a copy of the WURFL data on your Web server. The API doesn’t currently support reading from other than local files. You can also specify the WURFL data files in the web.config file. In this case, you replace the call to InMemoryConfigurer with ApplicationConfigurer.
var configurer = new ApplicationConfigurer();
The web.config section has to look like below:
<wurfl>
   <mainFile path="..." />   <patches>
     
<patch path="..." />
     :
 
</patches>
</
wurfl>
Note that the <wurfl> section is a user-defined section and needs be registered before use with the .NET infrastructure. For this reason, you also need to add the following at the top of the web.config file:



&lt;br&gt;&amp;amp;lt;configuration&amp;amp;gt;&lt;br&gt; &amp;amp;lt;configSections&amp;amp;gt;&lt;br&gt; &amp;amp;lt;section name="wurfl" &lt;br&gt;type="WURFL.Config.WURFLConfigurationSection,wurfl" /&amp;amp;gt;&lt;br&gt; &amp;amp;lt;/configSections&amp;amp;gt;&lt;br&gt; :&lt;br&gt;&amp;amp;lt;/configuration&amp;amp;gt;&lt;br&gt; Caching WURFL Data
In Web applications, it is always a good habit to cache any large and constant block of data; WURFL data is no exception. To cache data in ASP.NET applications, you typically use the native Cache object. WURFL doesn’t mandate any special guideline as far as caching is concerned. You are completely free of determining the name of the key used to retrieve the data. Likewise, it is up to you to implement caching and expiration policies. For example, you can make cached WURFL data dependent on the timestamp of the wurfl_latest.zip file and/or patch files. In this way, cached data is automatically cleared when you replace the WURFL file. For more information on using cache dependencies in ASP.NET, have a look at Programming ASP.NET 4”, Dino Esposito, Microsoft Press.
To be precise, you never cache raw data but you cache, instead, a reference to the object that owns that data. The object you cache is the WURFL manager. This is also the object you query for capabilities. Here’s how you retrieve the cached instance of the manager.
var wurflManager = HttpContext.Current.Cache[WurflManagerCacheKey] as IWURFLManager;
It goes without saying that you should check the instance for nullness as there’s no guarantee that the reference is still there when you try to access it. A good practice is checking whether the instance is null and reload it as appropriate. Another alternative is not using the (otherwise recommended) Cache object, but stick to the Application object which still keeps its content global to all sessions but doesn’t implement any scavenging policies.

Refactoring Your Code Just a Bit

You don’t actually need much code to initialize WURFL; but this is not an excuse to keep it quite simplistic. The sample applications for both ASP.NET Web Forms and ASP.NET MVC use a helper class that encapsulate much of the required logic and contain some additional features such as auto-reload of WURFL data in case the ASP.NET cache is cleared.
In particular, you might want to add the WurflLoader.cs file to your project and rewrite the startup code as follows:
private void Application_Start(Object sender, EventArgs e)
{
    WurflLoader.Start();  
}
The WurflLoader class has another static method through which you retrieve the instance of the WURFL manager to use for your queries. The method is GetManager and all it does is retrieving and returning the WURFL manager instance from the cache. If the instance is null, it then automatically creates and caches new one. Here’s the source code:
public static IWURFLManager GetManager()
{
    return GetManager(HttpContext.Current);
}
public static IWURFLManager GetManager(HttpContext context)
{
    var wurflManager = context.Cache[WurflManagerCacheKey] as IWURFLManager;
    if (wurflManager == null)
        return InitializeWurflManager(context, _wurflDataPath, _wurflPatchFilePath);
    return wurflManager;
}
The WurflLoader class is self-contained and has no dependencies on other files, except the WURFL binaries.

Querying for Capabilities

Once you hold a WURFL Manager object in your hands, you’re pretty much done. A WURFL manager is an object that implements the IWURFLManager interface.
public interface IWURFLManager
{
    IDevice GetDeviceForRequest(WURFLRequest wurflRequest);
   
IDevice GetDeviceForRequest(string userAgent);
   
IDevice GetDeviceForRequest(HttpRequest httpRequest);
    IDevice GetDeviceById(string deviceId);
    ICollection<IDevice> GetAllDevices();
}
The class offers a few methods for you to gain access to the in-memory representation of the detected device model. You can query for a device in a variety of ways: passing the ASP.NET HttpRequest object, the user agent string, the device ID, or a WURFL specific request object. The WURFL specific request object —the class WURFLRequest—is merely an aggregate of data that includes the user agent string and profile.
All of the methods on the WURFL manager class return an IDevice object which represents the matched device model. The interface has the following structure:
public interface IDevice
{
   string Id { get; }
   string UserAgent { get; }
   string GetCapability(string name);
   IDictionary<string, string> GetCapabilities();
}
You can check the value of a specific capability through the following code:
var is_tablet = device.GetCapability("is_tablet");
If you call the GetCapabilities method you get a dictionary of name/value pairs. The value associated with a capability is always expressed as a string even when it logically represents a number or a Boolean.
Armed with capabilities—WURFL supports more than 500 different capabilities—you are ready to apply the multi-serving pattern to your next ASP.NET mobile Web site.

Summary

The WURFL API currently available for ASP.NET is a rather generic one that works very closely to the metal and doesn’t provide much abstraction. This is good news as it allows developers to fully customize every single aspect of their code interaction with WURFL.  In particular, you can control the caching logic and can easily define your helper classes (like the aforementioned WurflLoader class)  taking into account testability and dependency injection.

No comments:

Post a Comment