WURFL Microservice Client API for Scala

Note: This guide assumes that you have access to a running instance of the WURFL Microservice HTTP Server, either through ScientiaMobile's Docker Registry, the AWS Marketplace, the Azure Marketplace, or other distribution channels. The guide also assumes familiarity with Device Detection (WURFL) and WURFL Capabilities.


WURFL Capabilities

WURFL Capabilities: WURFL capabilities are device properties represented in WURFL. While users of the WURFL Microservice for Docker have access to the list of licensed capabilities, customers who obtained the product through one of the Marketplace will have access to the WURFL capability list, predefined for the product they have licensed. Please note that there are two types of capabilities supported in WURFL: Static and Virtual.

While the difference between the two is mostly immaterial for you as a user as far as their practical usage goes, you still need to use two separate methods to use one or the other.

Note: While the wmclient is an API, it requires interaction with the WURFL Microservice server to work. This introduces some latency (hugely mitigated by a built-in caching layer). For this reason, ScientiaMobile does not refer to the WURFL Microservice Client (wmclient) as a "WURFL API". That name is reserved for the WURFL OnSite APIs.


This is the Scala Client API for accessing the WURFL Microservice. The Scala client works as a wrapper of the Java WURFL Microservice client API on which it depends (see the sbt or maven build files). The API is released under Open-Source and can be integrated with other open-source or proprietary code. The source code and an example project are available here.

** Prerequisites**

WURFL Microservice Scala client requires at least Java 8 and Scala 2.13.

**Obtaining, Installing and Running the WURFL microservice Scala client **

  1. Obtain the jar package with the wmclient library for Scala from Maven central. The jar file has a name wurfl-microservice-scala-{version_number}.jar

  2. Install the wurfl-microservice-scala-{version_number}.jar file in a Maven repository of your choice. As an alternative, you can add the Scala dependency in your SBT or Maven project. For example, using maven:

<dependency>
  <groupId>com.scientiamobile.wurflmicroservice</groupId>
  <artifactId>wurfl-microservice-scala</artifactId>
  <version>{version}</version>
</dependency>

or, using SBT:

libraryDependencies += "com.scientiamobile.wurflmicroservice" % "wurfl-microservice-scala" % {version}

The example project in the source code repository already contains a minimal SBT file with the WURFL microservice client Scala dependency.

  1. Go to the example project directory and edit Example.scala, modifying the WmClient.apply() call to include the public IP address of your AMI instance or Docker container service (e.g. replacing localhost with the IP address of your running AMI in AWS Marketplace, or the address of your Docker container service).

Note: There are multiple ways in which a service deployed through Docker can be exposed to the world around it. Please refer to your DevOps if in doubt of the actual address at which the service is running.

  1. In the root directory of the example project (where the build.sbt file is located), use the command sbt run to compile and run the example application.

The sample code in Example.scala will look like the following:

import com.scientiamobile.wurfl.wmclient.WmException
import com.scientiamobile.wurfl.wmclient.scala.WmClient

import scala.util.Sorting.quickSort

object Example {

  def main(args: Array[String]) {
    print("Running scala version ")
    println(scala.util.Properties.scalaPropOrElse("version.number", "unknown scala version"))

    try { // First we need to create a WM client instance, to connect to our WM server API at the specified host and port.
      val client = WmClient.apply("http", "localhost", "8080", "")
      // We ask Wm server API for some Wm server info such as server API version and info about WURFL API and file used by WM server.
      val info = client.getInfo()
      println("Printing WM server information")
      println("WURFL API version: " + info.getWurflApiVersion)
      println("WM server version:  " + info.getWmVersion)
      println("Wurfl file info: " + info.getWurflInfo)
      val ua = "Mozilla/5.0 (Linux; Android 7.1.1; ONEPLUS A5000 Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"
      // By setting the cache size we are also activating the caching option in WM client. In order to not use cache, you just to need to omit setCacheSize call
      client.setCacheSize(100000)
      // set the capabilities we want to receive from WM server
      client.setRequestedStaticCapabilities(Array[String]("brand_name", "model_name"))
      client.setRequestedVirtualCapabilities(Array[String]("is_smartphone", "form_factor"))
      println
      println("Detecting device for user-agent: " + ua)
      // Perform a device detection calling WM server API
      val device = client.lookupUseragent(ua)
      // Applicative error, ie: invalid input provided
      if (device.error != null && device.error.length > 0) println("An error occurred: " + device.error)
      else { // Let's get the device capabilities and print some of them
        val capabilities = device.capabilities
        println("Detected device WURFL ID: " + capabilities.get("wurfl_id"))
        println("Device brand & model: " + capabilities.get("brand_name") + " " + capabilities.get("model_name"))
        println("Detected device form factor: " + capabilities.get("form_factor"))
        if (capabilities.get("is_smartphone").equals("true")) println("This is a smartphone")
        // Iterate over all the device capabilities and print them
        println("All received capabilities")
        val it = capabilities.keySet.iterator
        while ( {
          it.hasNext
        }) {
          val k = it.next
          println(k + ": " + capabilities.get(k))
        }
      }
      // Get all the device manufacturers, and print the first twenty
      val limit = 20
      val deviceMakes = client.getAllDeviceMakes
      printf("Print the first %d Brand of %d retrieved from server\n", limit, deviceMakes.length)
      // Sort the device manufacturer names
      quickSort(deviceMakes)
      for (i <- 0 until limit) {
        printf(" - %s\n", deviceMakes(i))
      }
      // Now call the WM server to get all device model and marketing names produced by Apple
      println("Print all Model for the Apple Brand")
      val devNames = client.getAllDevicesForMake("Apple")
      // Sort ModelMktName objects by their model name
      devNames.sortWith(_.modelName >= _.modelName)

      for (modelMktName <- devNames) {
        printf(" - %s %s\n", modelMktName.modelName, modelMktName.marketingName)
      }
      // Now call the WM server to get all operative system names
      println("Print the list of OSes")
      val oses = client.getAllOSes
      // Sort and print all OS names
      quickSort(oses)
      for (os <- oses) {
        printf(" - %s\n", os)
      }
      // Let's call the WM server to get all version of the Android OS
      println("Print all versions for the Android OS")
      val osVersions = client.getAllVersionsForOS("Android")
      // Sort all Android version numbers and print them.
      quickSort(osVersions)
      for (ver <- osVersions) {
        printf(" - %s\n", ver)
      }
      // Cleans all client resources. Any call on client API methods after this one will throw a WmException
      client.destroyConnection
    } catch {
      case e: WmException =>
        // problems such as network errors  or internal server problems
        // note that WmException comes from the Java wrapped API
        println("An error has occurred: " + e.getMessage)
    }
  }
}

If you are using device detection from code running as part of an HTTP server, it is highly recommended that you pass the complete HTTPServletRequest object to the lookupRequest method as follows:

val device = client.lookupRequest(request)

This will provide optimal detection accuracy. For a complete reference of all static and virtual capabilities, you can refer to this document. Please note that WURFL Microservice for the AWS Marketplace comes with a predefined set of capabilities.


Error in case of Missing Capabilities

As mentioned above, depending on the configuration of your WURFL Microservice, one or more WURFL capabilities may be unavailable. In this case, the JSONDeviceData.capabilities map will not contain an entry for those capabilities and the caller will get a return value of zero for it (null).

Presence of a capability can be checked with:

if(device.capabilities.contains("has_cellular_radio") {
    value = device.capabilities.get("has_cellular_radio");
}

Additional capabilities can always be obtained by upgrading to a greater version of the AMIs in the AWS Marketplace or by contacting ScientiaMobile to license additional capabilities for the Docker Image or other products.




© 2021 ScientiaMobile Inc.
All Rights Reserved.

NOTICE: All information contained herein is, and remains the property of ScientiaMobile Incorporated and its suppliers, if any. The intellectual and technical concepts contained herein are proprietary to ScientiaMobile Incorporated and its suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden unless prior written permission is obtained from ScientiaMobile Incorporated.