# 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 2022. All Rights Reserved. _
_ https://deviceatlas.com _