# DeviceAtlas API Upgrade # This document is intended for users of APIs prior to version 2.1. It has three main sections: 1. Why upgrade? 2. Upgrading from 2.0.x to 2.1 3. Upgrading from 1.x to 2.x ## Why upgrade? ## Version 2.1 of the API has higher identification speed, reduced memory and improved identification capabilities. It is a recommended upgrade for all users. The API is compatible with all existing DeviceAtlas data files. Version 2.1 adds support for additional improvements to the data file format. To enable these improvements please follow the below steps: 1. Login into your account at deviceatlas.com 2. Go to "Data File Options" under "MY ACCOUNT" page 3. Un-tick the "API 2.0 compatible JSON file" option 4. Save Options 5. Download the new data file 6. Use new data file with the API. Note: It is recommended to only use the updated data file with APIs of version 2.1 and above. ## Upgrading from 2.0.x to 2.1 ## The 2.1 API is a drop-in replacement for the 2.0.x APIs. It should not cause any compatibility issues except for two minor cases described below. To upgrade to version 2.1 replace all DeviceAtlas jar files currently in use with the updated versions. ## API behaviour changes ## API changes between 2.0.x and 2.1 APIs are as follows: ### Change 1 - Property.value() ### The internal representation of some data types was corrected. This means that calls to Property.value() will return an object of the correct type (Boolean, Integer or String). Note: the recommended way to get a Property value in both 2.0.x and 2.1 is to use the appropriate method depending on the return type. e.g. .asBoolean() or .asInteger() ### Examples of change ### In 2.0.1: ```java Properties properties = deviceApi.getProperties(userAgent); Property isMobile = properties.get("mobileDevice") // result is 0 or 1 when used as String Object isMobileValue = isMobile.value() // result is 0 or 1 when used as String ``` In 2.1: ```java Properties properties = deviceApi.getProperties(userAgent); Property isMobile = properties.get("mobileDevice") // result is 0 or 1 when used as String Object isMobileValue = isMobile.value() // result is 'false' or 'true' when used as String ``` The **recommended** way to get a property value in both 2.0.x and 2.1: ```java Properties properties = deviceApi.getProperties(userAgent); boolean isMobile = properties.get("mobileDevice").asBoolean(); int yearReleased = properties.get("yearReleased").asInteger(); String model = properties.get("model").asString(); ``` ### Change 2 - Config class ### In 2.0.x it was possible to change a value in the config class after the API had been initialised. In some rare situations this could cause unexpected behavior. Version 2.1 adds a new method DeviceApi.setConfig() to apply changes after the API has been initialised. Any change made to the config without called DeviceApi.setConfig() will no longer have any effect. #### Example of 2.0.x behaviour #### Change to config directly affects API behaviour: ```java Config config = new Config(); config.setIncludeUaProps(false); DeviceApi deviceApi = new DeviceApi(config); Properties properties = deviceApi.getProperties(userAgent); // properties does not contain dynamic properties // in 2.0.x API changing a config parameter after instantiation // _will_ affect API behaviour. config.setIncludeUaProps(true); Properties properties = deviceApi.getProperties(userAgent); // properties contain dynamic properties ``` #### Example of new 2.1 behaviour #### Change to config does not change API behaviour unless config is passed to DeviceApi.setConfig(): ```java Config config = new Config(); config.setIncludeUaProps(false); DeviceApi deviceApi = new DeviceApi(config); Properties properties = deviceApi.getProperties(userAgent); // properties does not contain dynamic properties // in 2.1 API changing a config parameter after instantiation // will not affect API behaviour. config.setIncludeUaProps(true); Properties properties = deviceApi.getProperties(userAgent); // properties still do not contain dynamic properties deviceApi.setConfig(config); // apply config Properties properties = deviceApi.getProperties(userAgent); // properties now contain dynamic properties as per config ``` ## Upgrading from 1.x to 2.x ## Version 2.x of the API introduces a new better interface and improved performance. The following sections detail upgrading from v1.x versions. Version 1.x of the API has been deprecated. There are three options: ### 1. Do nothing ### The new data file delivered as part of the 2.x upgrade will work with API versions 1.5, 1.6 and 1.7. Please see section "Why upgrade?". ### 2. Adopt new API, utilising the deprecated 1.x API interface ### This option has the benefit of avoiding any interface changes, but does not deliver the benefits of the 2.x API. ### 3. Adopt new API, utilising new interface ### This is the recommended approach, and it delivers improved accuracy of identification for third party browsers. It is an improved architecture and will form the basis of future API evolutions. ### 1.x API Interface ### The interface used in the DeviceAtlas Device Identification API versions prior to 2.x is marked as deprecated. It is highly recommended to upgrade your source code to work with the new interface instead. As it may be inconvenient for some current users to upgrade their source code to use the new interface, the old interface is supported by DeviceAtlas but limited to bug fixes. The new libraries include the old interface (marked as deprecated) as "mobi.mtld.da.Api". Existing source code will work without the need to be changed simply by replacing the old jar file with the three new jar files. However, new users should avoid using the 1.x interface and current users are encouraged to upgrade to the new interface. ### Upgrading to 2.x ### The DeviceAtlas Device Identification API version 2.x and higher is shipped with a different interface than previous versions. This section shows the steps to upgrade a system which is currently using a DeviceAtlas Device Identification API prior to version 2.x. 1. Replace the previous DeviceAtlas Device Identification API jar file with the three jar files shipped in this package. 2. Package name changes. The previous imports must be replaced with the ones shown below. ```java // this package contains the common DeviceAtlas classes such as Properties // and Property which are required by the API classes import mobi.mtld.da.*; // if you want to handle DeviceAtlas exceptions, use this import. See the // API documentation to see which exceptions are thrown import mobi.mtld.da.exception.*; // the API class // import DeviceApi when outside a servlet container import mobi.mtld.da.device.DeviceApi; // import DeviceApiWeb when inside servlet container import mobi.mtld.da.device.DeviceApiWeb; // if you want to change the default API configs import Config import mobi.mtld.da.device.Config; ``` 3. Creating an instance. Unlike the 1.x interface, which was a set of static methods, accessing the methods of the new interface requires an instance. ```java // in a servlet container DeviceApiWeb deviceApi = new DeviceApiWeb(); ``` ```java // outside a servlet container DeviceApi deviceApi = new DeviceApi(); ``` You can also change the default API configs: ```java // in a servlet container Config configWeb = new Config(); // in case there is another package has a class named "Config": // mobi.mtld.da.device.Config configWeb = new mobi.mtld.da.device.Config(); // configure the api with the "configWeb" object // find the available configs in the API documentations DeviceApiWeb deviceApi = new DeviceApiWeb(configWeb); ``` ```java // outside a servlet container Config config = new Config(); // configure the api with the "config" object // find the available configs in the API documentations DeviceApi deviceApi = new DeviceApi(config); ``` 4. Loading the data. In the 1.x interface it was necessary to load the data and get the tree (as a HashMap) from the loadTreeFromFile() or loadTreeFromString() and manually pass it to all the other related methods, however this is not the case in the new interface. The data may be loaded into the API instance from a file, input stream or class path and no tree will be returned and the tree does not have to be passed to other methods. This interface keeps the tree encapsulated. ```java deviceApi.loadDataFromFile("/path/to/datafile.json"); ``` 5. Getting properties. There is only one getProperties() method for getting the properties. The getPropertiesAsTyped(), and getProperty() calls must be replaced with the code shown below: ```java // Previously HashMap properties = Api.getProperties(tree, userAgent); if (properties.containsKey("model")) { String value = (String)properties.get("model"); } // Now Properties properties = deviceApi.getProperties(userAgent); if (properties.containsKey("model")) { String value = properties.get("model").asString(); } ``` ```java // Previously HashMap properties = Api.getPropertiesAsTyped(tree, userAgent); if (properties.containsKey("mobileDevice")) { boolean value = ((Boolean)properties.get("mobileDevice")).booleanValue(); } // Now Properties properties = deviceApi.getProperties(userAgent); if (properties.containsKey("mobileDevice")) { boolean value = properties.get("mobileDevice").asBoolean(); } ``` ```java // Previously try { boolean isMobile = Api.getPropertyAsBoolean(tree, userAgent, "mobileDevice"); if (isMobile) { } } catch (... // Now - method 1 Properties properties = deviceApi.getProperties(userAgent); if (properties.contains("mobileDevice", true)) { } // Now - method 2 Properties properties = deviceApi.getProperties(userAgent); if (properties.containsKey("mobileDevice")) { boolean isMobile = properties.get("mobileDevice").asBoolean(); if (isMobile) { } } ``` ```java // Previously HashMap properties = Api.getProperties(tree, userAgent, clientSideProperties); if (properties.containsKey("model")) { String value = (String)properties.get("model"); } // Now Properties properties = deviceApi.getProperties(userAgent, clientSideProperties); if (properties.containsKey("model")) { String value = properties.get("model").asString(); } // Now - when using DeviceApiWeb Properties properties = deviceApi.getProperties(request); if (properties.containsKey("model")) { String value = properties.get("model").asString(); } ``` ### API instantiation ### The Java API before version 2.x kept a static variable with the tree loaded inside the API but. Version 2.x puts more control in your hands in terms of how the tree is loaded in memory. The caveat is that multiple calls to loadDataFromFile() will reload the datafile and slow down the application. The solution is to only load the datafile once before you need it. #### DeviceApiWeb instantiation in a servlet #### It is highly recommended to create only one DeviceApiWeb instance inside the servlet context and load the datafile only once, then use the instance as an object in the application and shared across the requests. For example you can instantiate a DeviceApiWeb object inside contextInitialized listener and load the data file there: ```java import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import mobi.mtld.da.device.DeviceApiWeb; public class ServletContextExample implements ServletContextListener { public void contextInitialized(ServletContextEvent contextEvent) { try { // Create a DeviceApiWeb instance DeviceApiWeb deviceApi = new DeviceApiWeb(); // Load the device atlas JSON data file deviceApi.loadDataFromFile("/path/to/datafile.json"); // Store the DeviceApiWeb instance on the servlet context context.setAttribute("DeviceApiWeb", deviceApi); } catch (Exception ex) { System.out.println(ex.getMessage()); } } public void contextDestroyed(ServletContextEvent contextEvent) { } } ``` The DeviceApiWeb instance can be accessed in the application sources as show below: ```java DeviceApiWeb deviceApi = (DeviceApiWeb)context.getAttribute("DeviceApiWeb"); if (deviceApi == null) { // things are not right maybe throw an exception } Properties properties = deviceApi.getProperties(request); // use the properties . . . ``` Note! as the datafile is only loaded when the servlet starts, replacing the datafile with a new one (e.g. new datafile downloaded from http://deviceatlas.com) will not take effect unless the new data file is reloaded into the API, this can be done easily as shown below: ```java // this lines can be put inside an admin page to run after datafile updates DeviceApiWeb deviceApi = (DeviceApiWeb)context.getAttribute("DeviceApiWeb"); if (deviceApi != null) { deviceApi.loadDataFromFile("/path/to/datafile.json"); } ``` #### DeviceApi instantiation in a wrapper #### Create a wrapper to restrict the instantiation of DeviceApi to one object. The datafile is loaded into the API immediately after creating the instance. ```java public class DeviceApiWrapper { private static DeviceApi deviceApi = null; public static synchronized DeviceApi getApi(String filePath) throws IOException, DataReadException, FileNotFoundException, JsonException { if (deviceApi == null) { deviceApi = new DeviceApi(); deviceApi.loadDataFromFile(filePath); } return deviceApi; } public static synchronized DeviceApi reloadApi(String filePath) throws IOException, DataReadException, FileNotFoundException, JsonException { deviceApi = new DeviceApi(); deviceApi.loadDataFromFile(filePath); return deviceApi; } } ``` The DeviceApi instance can be accessed in the application sources as show below: ```java // if a DeviceApi instance already exists there wont be another // instantiation and datafile loading String dataFile = "/path/to/datafile.json"; DeviceApi deviceApi = DeviceApiWrapper.getApi("/path/to/datafile.json"); // use the api . . . Properties properties = deviceApi.getProperties(request); ``` Note! this will not always be useful. For example this will not help a servlet because each request will independently create a separated instance and load the datafile while we need a single shared instance across the threads. Note! as the datafile is only loaded only once when creating an instance, replacing the datafile with a new one (e.g. new datafile downloaded from http://deviceatlas.com) will not have effect on the instance. In a situation like this calling "DeviceApiWrapper.reloadApi("/path/to/datafile.json");" will reload the datafile into the API. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _ Copyright (c) DeviceAtlas Limited 2021. All Rights Reserved. _ _ https://deviceatlas.com _ <!-- HTML+JS for document formatting when opened in browser --> <div class="btn-group" id="main-menu" style="float:right"><a class="btn dropdown-toggle" data-toggle="dropdown" href="#">Menu<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="README.html">Main</a></li><li><a href="README.Installation.html">Enterprise API Installation</a></li><li><a href="README.DataFile.html">Data File Configuration</a></li><li><a href="README.DeviceApi.html">Device Detection</a></li><li><a href="README.DeviceApi-Config.html">Device Identification API Config</a></li><li><a href="README.CarrierApi.html">Carrier Identification API</a></li><li class="divider"></li><li><a href="./DeviceAtlasApiDocs/index.html">DeviceAtlas API Docs</a></li><li class="disabled"><a href="README.Upgrade.html">API Upgrade</a></li></ul></div>