WURFL OnSite Java API: User Guide ================================= Introduction ------------ WURFL OnSite for Java Application Programming Interface (API) is a mobile device detection software component that can quickly and accurately detect device models and over 500 capabilities of that device. The API library allows applications to perform real-time detection for a variety of uses, such as content adaptation, redirections, advertising, and data traffic analysis. If you would like to learn general information about WURFL and device detection, you can read e-book here: https://www.devicedetection.com/ #### Operating System Support Not applicable #### Java version support As of API version 1.12.3.0, WURFL OnSite for Java requires Java 8 or higher. With the introduction of Jakarta EE 9, the enterprise Java application ecosystem has faced a huge change. The most impacting one is the naming change from the Oracle owned `javax.*` package namespace to the new `jakarta.*` In order to use the new namespace we provided a `wurfl-jakarta-X.Y.W.Z.jar` which is built using the new jakarta namespace. You can find it inside the Onsite Java API package in your customer vault. If you need to develop and deploy a web application that runs on Tomcat 10.x, Glassfish 6.x, Jetty 11.x or any other server that comply to this spec, you may want to use the jakarta compliant jar file. Installation Process -------------------- #### Start a Trial, or Purchase a License To get access to the WURFL OnSite for Java installation package, you will need to [contact ScientiaMobile](https://www.scientiamobile.com/trial-request/) to start a trial or purchase a license. #### Where to Download the WURFL OnSite for Java Installation Package Part of the trial or purchase process involves [registering](https://my.scientiamobile.com) for a free account on my.scientiamobile.com. Once you have registered, login to the "My Account" page. Under the File Management tab, you will see the installation package under **Products** -> **WURFL OnSite**.

![java_install_package](/img/java_1.png)

#### What's In The Installation Package The provided zip file contains all the files you need to install and run WURFL. Some key components include: `wurfl.jar` - this jar file is the WURFL OnSite API `wurfl.zip` - contains the wurfl.xml device data file, also referred to as the WURFL Snapshot. This file contains all the devices known to WURFL. A limited `wurfl.zip` file is included with the package to quickly demo the product. You will eventually want to download a full copy of your `wurfl.zip` by using your personal WURFL Snapshot URL and applying the `WURFLUpdater` class (see below) to automate the weekly update of your WURFL Snapshot. If you would like to trial a different set of capabilities, then please contact your ScientiaMobile account representative.

![wurfl_java_elements](/img/java_2.png)

#### Installing Dependencies If you are using Maven to handle external dependencies, follow the instructions in the last paragraph to setup ScientiaMobile's private Maven repository usage in your project. If you do not use Maven or any other dependency management tool, all the dependencies you need are packed into the onsite zip release file under core.zip/release.zip, where you will find a file `wurfl-version_number.jar` and a directory lib, which contains all the dependencies needed by the WURFL Java API to compile and run. #### Installing WURFL Copy the dependencies in your project and compile and run your Java programs using the `-cp` option. If you need more information about Java compilation from the command line, you can read this article by [Oracle](http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html) > **Note:** The WURFL API is closely tied to the `wurfl.xml` file. New versions of the `wurfl.xml` are compatible with old versions of the API by nature, but the reverse is **not** true. Old versions of the `wurfl.xml` are **not** guaranteed to be compatible with new versions of the API. #### Starting WURFL The `WURFLEngine` is a high-level interface, introduced to further abstract and simplify management of the WURFL API. Initializing the `WURFLEngine` is enough to start using the API and is the entry point to all WURFL functionalities. The default implementation of`WURFLEngine` is `GeneralWURFLEngine`. > Since v1.8.0.0, the package names have changed to *com.scientiamobile*. Please update your configuration accordingly. If you need to use the old package name (net.sourceforge.wurfl), a legacy release package will be present. New modules, such as `WURFLUpdater`, will only be available with the new package names. > **Note**: The release of WURFL OnSite for Java (v1.9.4.0) was the last release to contain support for the legacy package name (net.sourceforge.wurfl) and the legacy release package. Please update to the new package name (com.scientiamobile) to avoid any disruptions to your project. ```java import com.scientiamobile.wurfl.core.Device; import com.scientiamobile.wurfl.core.GeneralWURFLEngine; public class Wurfl { public static void main(String[] args) { // wurfl.xml path can be either absolute or relative to the application classpath root. WURFLEngine wurfl = new GeneralWURFLEngine("wurfl.zip"); // load method is available on API version 1.8.1.0 and above wurfl.load(); String user_agent = "Mozilla/5.0 (Linux; Android 4.2.1; N9600 Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.111 Mobile Safari/537.36"; Device device = wurfl.getDeviceForRequest(user_agent); System.out.println("Is Tablet: " + device.getCapability('is_tablet')); System.out.println("Can Assign Phone Number: " + device.getCapability('can_assign_phone_number')); } } ``` If you are configuring a web application using a `web.xml` file, you can: ```xml com.scientiamobile.wurfl.core.web.WURFLServletContextListener wurfl /WEB-INF/wurfl.zip wurflPatch /WEB-INF/patch_1.xml,/WEB-INF/patch_2.xml ``` WURFLEngine implementation is loaded lazily (ie: when getDeviceById or getDeviceForRequest are called for the first time), but starting from version 1.8.1.0 you can load it programmatically by invoking method `load`. ```java WURFLEngine wurfl = new GeneralWURFLEngine("wurfl.xml"); wurfl.load(); ``` #### Connecting to a User Agent Source The `WURFLServletContextListener` will automatically find the user agent string of an HTTP request. For testing purposes, you can manually define the User-Agents submitted to WURFL via `wurfl.getDeviceForRequest(user_agent)`. If you wish to manually define a HTTP request, with User-Agent Client Hints for example, you can construct a map and manually place all the headers like so: ```java // Construct a header map String ua = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Mobile Safari/537.36"; Map headers = new HashMap<>(); headers.put("User-Agent", ua); headers.put("Sec-Ch-Ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\""); headers.put("Sec-Ch-Ua-Full-Version-List", "\" Not A;Brand\";v=\"99.0.1.2\", \"Chromium\";v=\"96.1.2.3\", \"Google Chrome\";v=\"96.2.3.4\""); headers.put("Sec-Ch-Ua-Platform", "Android"); headers.put("Sec-Ch-Ua-Platform-Version", "12"); headers.put("Sec-Ch-Ua-Model", "Pixel 6"); ``` You can then use the constructed header map to create a `WURFLRequest` object which can be used to perform device detection: ```java // Perform the device detection using the WURFL OnSite for Java API using the constructed header map WURFLRequest req = new DefaultWURFLRequest(headers); device = wurfl.getDeviceForRequest(req); ``` If you do not need to check the header quality, you can directly pass the constructed header map to `getDeviceForRequest()` method as shown here: ```java // Construct a header map String ua = "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Mobile Safari/537.36"; Map headers = new HashMap<>(); headers.put("User-Agent", ua); headers.put("Sec-Ch-Ua", "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"96\", \"Google Chrome\";v=\"96\""); headers.put("Sec-Ch-Ua-Full-Version-List", "\" Not A;Brand\";v=\"99.0.1.2\", \"Chromium\";v=\"96.1.2.3\", \"Google Chrome\";v=\"96.2.3.4\""); headers.put("Sec-Ch-Ua-Platform", "Android"); headers.put("Sec-Ch-Ua-Platform-Version", "12"); headers.put("Sec-Ch-Ua-Model", "Pixel 6"); // Perform the device detection using the WURFL OnSite for Java API using the constructed header map Device device = wurfl.getDeviceForRequest(headers); ``` >**IMPORTANT:** Empty header values are treated as valid and those headers are not discarded. If you build your HTTP request programmatically from a data source such as logs, DB data, spreadsheet, etc., please make sure that you DO NOT add headers with empty strings as values (this may also be the result of "casting" a `NULL` / `NONE` / `NaN` to a string): >

> *Avoid:* > ``` > {headerName}:{headerValue} > ``` >*Use:* >``` >if notNullOrEmpty(headerValue): > {headerName}:{headerValue} >```
Retrieving Static WURFL Capabilities -------------------- To retrieve information about a device, WURFL matches a user agent to a specific device profile and then performs a lookup of the device's WURFL capabilities. There are two types of WURFL capabilities: **static** and **virtual** (see the next section for virtual capabilities instructions). Each type of capabilities require different methods to retrieve values, so be sure to determine whether your WURFL capabilities are static or virtual by consulting our WURFL capabilities documentation (https://www.scientiamobile.com/capabilities). Static capabilities describe device and browser properties that do not (normally) change. For instance, a device's resolution_width will not change over time. Static properties can be stored in memory by the API, readily available for the next fast look-up. To retrieve the value of a static capability, use the `getCapability()` method from a `Device` object: For example, to retrieve the value of the static capability resolution_width (the screen width expressed in physical pixels) you would use the following: `device.getCapability('resolution_width')` Retrieving Virtual WURFL Capabilities -------------------- Virtual capabilities return a property whose value is based on the evaluation of other static capabilities or further inspection of parameters found in the HTTP request. Please check if the your capabilities are among WURFL's virtual capabilities listed on this [table]( https://www.scientiamobile.com/capabilities/?capability-category%5B%5D=virtual) Unlike static capabilities, virtual capabilities use the `getVirtualCapability()` method. For example, to retrieve the value of the virtual capability 'is_smartphone' (a boolean value based on whether a device conforms to ScientiaMobile's definition of a smartphone), you would use the following: `device.getVirtualCapability('is_smartphone')` **Note:** You can override virtual capability return values by assigning values to their corresponding control capabilities (controlcaps) in a [patch file](https://docs.scientiamobile.com/guides/patch-files). A more detailed write-up of control capabilities and how they work with virtual capabilities is available [here](https://www.scientiamobile.com/virtual-capabilities-and-filters-introducing-wurfl-api-1-5/). How to Improve and Maintain WURFL Accuracy -------------------- Configuring WURFL Updater ### #### Configuring WURFL Updater A new WURFL device data snapshot is released weekly on Sunday night. An out-of-band WURFL snapshot may be released to our customers between weekly snapshots in case of a significant improvement to the data (for example, if a high-profile device is released mid- week and it is not yet in WURFL). API version 1.8.0.0 introduced WURFL updater; a new set of classes which allow a client to automatically update their WURFL Snapshot. In order to use the `WURFLUpdater`, you must have your personal WURFL Snapshot URL in the following format: https://data.scientiamobile.com/xxxxx/wurfl.zip. Your personal WURFL Snapshot URL can be found by logging into your [ScientiaMobile account](https://my.scientiamobile.com/myaccount): **My Products** -> **View Account** (for your subscribed product) -> **WURFL Snapshot Generator** (https://data.scientiamobile.com/xxxxx/wurfl.zip)

![snapshot](/img/wurfl_snapshot.png)

Do note that the `rootPath` passed to the `WURFLEngine` constructor must be writable from the process/task that is executing the Java API since `WURFLUpdater` will update `rootPath` file, and a wurfl.zip file must already be present in order for the Updater to determine whether or not it needs to update the file. The suggested setting for WURFL updater is "periodic" mode; i.e. `WURFLUpdater` will periodically check to see if a new version of the wurfl.zip has been released, and if so, download it and reload the engine with the new version; all while the standard `WURFLEngine` is running and serving requests. #### Running "periodic" Updates. ```java String rootPath = "wurfl.zip"; WURFLEngine engine = new GeneralWURFLEngine(rootPath); // remember to modify the url below with your personal WURFL Snapshot url WURFLUpdater updater = new WURFLUpdater(engine, "https://data.scientiamobile.com/xxxxx/wurfl.zip"); updater.setFrequency(Frequency.DAILY); updater.performPeriodicUpdate; ``` Being a periodic task, the updater will run perpetually until `updater.stopPeriodicUpdate()` is called. Periodic updates do not return a result. Failed/Successful results must be checked in log files/console messages. If needed, `WURFLUpdater` can also run in "on demand" mode, i.e. check for a new version of the `wurfl.zip` once and then stop. #### Running "on demand" update. ```java String rootPath = "wurfl.zip"; WURFLEngine engine = new GeneralWURFLEngine(rootPath); // remember to substitute url below with your personal WURFL Snapshot url WURFLUpdater updater = new WURFLUpdater(engine, "https://data.scientiamobile.com/xxxxx/wurfl.zip"); UpdateResult result = updater.performUpdate(); ``` On demand updates run once per call and returns a result that can be used to programmatically check if update has been successful or, in case of failure, get an error message. #### Proxy Configuration for WURFL Updater Sometimes you may need to configure a proxy to run a `WURFLUpdater` instance. In this case, the initialization becomes: ```java String rootPath = "wurfl.zip"; WURFLEngine engine = new GeneralWURFLEngine(rootPath); // proxy settings String proxyHost = "proxy.example.com" // replace it with your proxy host int proxyPort = 80; // replace with your proxy port ProxySettings proxy = new ProxySettings(proxyHost, proxyPort, Proxy.Type.HTTP); // ProxySettings proxy = new ProxySettings(proxyHost, proxyPort, Proxy.Type.SOCKS); // SOCKS proxy type is also supported. // remember to modify the url below with your personal WURFL Snapshot url WURFLUpdater updater = new WURFLUpdater(engine, "https://data.scientiamobile.com/xxxxx/wurfl.zip", proxy); ``` ####Updating the WURFL API While there is no rigid schedule for API updates, historically ScientiaMobile releases API updates on a quarterly basis. In addition to weekly updates of the device data snapshot, updating the API is also critical to maintaining accuracy and performance. As part of the WURFL license, customers are entitled to upgrading to the latest and greatest API release. These API updates will appear in your My Account when they become available. We will send a notification of the API update and details on the improvements included in the new release to the email associated with the account. You may manually subscribe to the ScientiaMobile eNewsletter list if you have another address where you'd like to receive the notifications. Java users who use Docker may also consider using [WURFL Microservice for Docker](https://www.scientiamobile.com/products/wurfl-microservice-docker-detect-device/) to simplify the deployment and maintenance process involved in API updates. How to Accelerate Performance and Scaling -------------------- Through filtering, caching and leveraging multi-threaded processing, WURFL OnSite for Java can achieve strong performance and scaling. #### Filtering WURFL Capabilities ### In order to reduce memory usage and increase performance, you can specify a subset of the 500+ WURFL capabilities to load into memory. The list of the selected capabilities can be set by calling the `setCapabilityFilter` method on a `WURFLEngine` instance. The list can be passed as either a string array `(String[])` or as a generic collection `(Collection)`: ```java String[] capabilities = { "device_os", "brand_name", "model_name", "release_date", "has_qwerty_keyboard" }; ``` If you are configuring using a `web.xml` file, you can apply a filter by: ```xml capability-filter device_os brand_name model_name release_date has_qwerty_keyboard ``` #### Cache Configuration ### In order to increase performance while processing real HTTP traffic, we suggest setting up an Least Recently Used (LRU) cache. The LRU caching strategy will speed up look-up operations on processed User Agents by keeping them in an LRU map. For example, the following configures the size of the map to 100,000 entries: ``` cp = new LRUMapCacheProvider(100000); engine.setCacheProvider(cp); ``` By default the cache will be set to 30,000 entries which accounts for 7 to 10 MB of additional memory usage. Users are advised to size their cache generously (100,000 or more) to increase performance. **For Users of the old DOUBLE LRU Cache Provider (pre 1.9.1):** Backwards compatibility, older configurations are still supported and will not generate errors or warnings, but internally the new SINGLE LRU Cache is adopted for better performance. For more information, please see LRU Cache Mechanism. > **Note:** `engine.setCacheProvider(cp);` must be called before calling `engine.load()`. #### Checking User-Agent frozenness and HTTP headers quality ### Starting from version 1.12.5.0 the WURFL Onsite Java API provides two new methods of the `WURFLEngine`: `isUaFrozen()` and `headerQuality()`. `boolean isUaFrozen(String userAgent)` returns a boolean value which, if true, means that the input User-Agent string won't be updated by the sender browser. `HeaderQuality headerQuality(HttpServletRequest request)` returns an enumeration value that describes the HTTP headers quality. It has three possible values: - **HEADER_QUALITY_FULL**: all the headers needed for a successful WURFL detection are present. Eg. a header with all UA-CH header fields present - **HEADER_QUALITY_BASIC**: only some of the headers needed for a successful WURFL detection are present - **HEADER_QUALITY_NONE**: no UA-CH headers are present #### Multi-threading and Multi-processor WURFL OnSite for Java can improve performance through multi-threaded processing. All WURFL APIs are thread safe. Users should share the same WURFL engine across all different threads that need to execute lookups. Customizing WURFL with patch files -------------------- The idea behind OnSite and InFuze is that you get an updated version of the `wurfl.xml` file as new and more accurate information becomes available and is added to WURFL. Developers can create and add a `wurfl_patch.xml` file to their system, which stores modified/enhanced groups and capability lists for new or existing WURFL devices. When the `wurfl.xml` file is parsed, the patch file is also imported to build a modified version of the device database. To learn more, refer to our [patch file documentation](https://docs.scientiamobile.com/guides/patch-files) Configuring with the Spring Framework -------------------- ### The WURFL API (starting with 1.4) is completely decoupled from the Spring framework. This allows non-Spring users to import and use WURFL without having to include the Spring library and everything involved with it. Using Spring does not mean that the `web.xml` file is no longer involved. You must tell your application that all the features of the API are now supported through Spring. In practice, this means that the `web.xml` file will contain the following lines: ```xml contextConfigLocation /WEB-INF/wurfl-ctx.xml : org.springframework.web.context.ContextLoaderListener ``` In the `wurfl-helloworld-spring-{version}.war` web application, you can find an easy way to configure the WURFL API using Spring. The `wurfl.ctx` context file will contain: ```xml ``` If you would like to set a capability filter, you will need to add the following properties to your `wurfl-ctx.xml`: ```xml device_os brand_name model_name release_date has_qwerty_keyboard ``` Finally, you can simply access the `WURFLEngine` instance with the code here (taken from a common Servlet method): ```java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WURFLEngine engine = (WURFLEngine)wac.getBean(WURFLEngine.class.getName()); [...] } ``` Generating a List of Possible Values ----------------------------------- Frequently, developers need to generate lists of possible values for WURFL capabilities. These lists can be useful in building filtering, drop-down lists, or selection interfaces in applications. Please contact our support for tools to generate such lists. Sample Applications ------------------- The Java OnSite API release contains two web application samples that can be helpful in understanding how you can setup your own WURFL based web application: - wurfl-helloworld-servlet.zip - wurfl-helloworld-spring.zip For the application to work, a `wurfl.xml` (.zip) file must be added in the application classpath before compiling it. Getting Support --------------- All current customers should contact us with technical/support questions through our [Enterprise Support Portal](https://support.scientiamobile.com). Customers receive enterprise-level ticketed support. Support specialists with over 30 years combined experience in device detection provide fast responses to developers. ScientiaMobile actively moderates and responds to the enterprise-level tickets. Change History -------------- > There were breaking changes in API release v1.8. For documentation of releases before API v1.8 please refer to the README file included with that version. If you need additional help for versions prior to 1.8 feel free to contact us at support@scientiamobile.com
> **Decommissioning of engine target options**: Prior to v1.9 of the API, users could choose between `set_engine_target_high_accuracy` and `set_engine_target_high_performance` engine optimization options.These options had been introduced years ago to manage the behavior of certain web browsers and their tendency to present "always different" User-Agent strings that would baffle strategies to cache similar WURFL queries in memory. As the problem has been solved by browser vendors, the need to adopt this strategy has diminished and ultimately disappeared (i.e. there was no longer much to be gained with the high-performance mode in most circumstances) and ScientiaMobile elected to "remove" this option to simplify configuration and go in the direction of uniform API behavior in different contexts. Configuring your Builds to work with ScientiaMobile's Private Maven Repository ------------------------------------------------------------------------------- ### If you are a commercial licensee of the WURFL OnSite Java API, it is as easy as adding your scientiamobile.com credentials into your settings.xml file and including WURFL as a dependency in your pom.xml file. You will first need to define an ID using a username and password that are associated with your license on scientiamobile.com and then place that into your settings.xml in our Maven installation. File settings.xml and the Maven dependency repository are usually located under the .m2 Maven directory, which is - by default - created under the user home directory (ie: if your home directory on Ubuntu Linux is jamie, the .m2 directory full path will be `/home/jamie/.m2`) ```xml com.scientiamobile.wurfl YourUsername YourPassword ``` Now that your credentials have been added, you will need to add the WURFL repository in your pom.xml file: ```xml (...) com.scientiamobile.wurfl com.scientiamobile.wurfl https://maven.scientiamobile.com/repository/wurfl-onsite/ ``` Please, note that in case depencies other than WURFL are needed, you may need to configure more than one repository. Let's also add WURFL as a dependency: ```xml (...) (...) com.scientiamobile.wurfl wurfl 1.11.0.0 (...) (...) ``` After everything has been configured with Maven, you are now ready to build your project and get started integrating WURFL! Building your own project with Maven ------------------------------------------------------------------------------- After you have completed the WURFL integration in your project, you can build it by using the following command in the project directory that contains the pom.xml file (usually the project's root directory) ``` mvn clean install ``` This command cleans the build output directory, downloads the needed dependencies, compiles the application, executes the unit tests - if any -, generates the executable (.jar or .war depending on the build configuration file) and store it in your local Maven repository. If you are new to Maven build manager and want to get started with it, you may want to take a look to [Maven website](https://maven.apache.org) ------- **© 2024 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.