51Degrees Device Detection Java  4.4

Device detection services for 51Degrees Pipeline


This example shows how to use 51Degrees device detection to process a file containing many User-Agents.

This example is available in full on GitHub.

This example requires a local data file. The free 'Lite' data file can be acquired by pulling the git submodules under this repository (run `git submodule update --recursive`) or from the device-detection-data GitHub repository.

The Lite data file is only used for illustration, and has limited accuracy and capabilities. Find out about the more capable data files that are available on our pricing page

* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2022 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
package fiftyone.devicedetection.examples.console;
import fiftyone.devicedetection.DeviceDetectionPipelineBuilder;
import fiftyone.devicedetection.examples.shared.DataFileHelper;
import fiftyone.devicedetection.hash.engine.onpremise.flowelements.DeviceDetectionHashEngine;
import fiftyone.devicedetection.shared.DeviceData;
import fiftyone.pipeline.core.data.FlowData;
import fiftyone.pipeline.core.flowelements.Pipeline;
import fiftyone.pipeline.engines.Constants;
import fiftyone.pipeline.engines.data.AspectPropertyValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.stream.Collectors;
import static fiftyone.common.testhelpers.LogbackHelper.configureLogback;
import static fiftyone.devicedetection.examples.shared.PropertyHelper.asString;
import static fiftyone.pipeline.util.FileFinder.getFilePath;
public class OfflineProcessing {
static final Logger logger = LoggerFactory.getLogger(OfflineProcessing.class);
// This 51degrees "Lite" file (distributed with the source) needs to
// be somewhere in the project space
// Note that the Lite data file is only used for illustration, and has
// limited accuracy and capabilities. Find out about the Enterprise data
// file here: https://51degrees.com/pricing
public static final String LITE_V_4_1_HASH =
// This 51degrees file of 20,000 examples (distributed with the source)
// needs to be somewhere in the project space
public static final String HEADER_EVIDENCE_YML =
"device-detection-data/20000 Evidence Records.yml";
public static void main(String[] args) throws Exception {
File evidenceFile = getFilePath(HEADER_EVIDENCE_YML);
run(LITE_V_4_1_HASH, Files.newInputStream(evidenceFile.toPath()), System.out);
public static void run(String dataFile, InputStream is, OutputStream os) throws Exception {
String detectionFile;
try {
detectionFile = getFilePath(dataFile).getAbsolutePath();
} catch (Exception e) {
throw e;
---- configure YAML for getting evidence and saving the results ----
DumperOptions dumperOptions = new DumperOptions();
// get a YAML loader to iterate over the device evidence
Yaml yaml = new Yaml(dumperOptions);
Iterator<Object> evidenceIterator = yaml.loadAll(is).iterator();
---- Build a pipeline ----
logger.info("Constructing pipeline with on-premise hash " +
"engine from file " + dataFile);
// Build a new on-premise Hash engine in a try/resources so
// that the pipeline is disposed when done
try (Pipeline pipeline = new DeviceDetectionPipelineBuilder()
.useOnPremise(detectionFile, false)
// inhibit sharing usage for this test, usually
// this should be set "true"
// inhibit auto-update of the data file for this test
// -- Setting the Profile
// For information on profiles see
// https://51degrees.com/documentation/_device_detection__features__performance_options.html
// Low memory profile has detection data streamed from disk on
// demand and is conservative in its use of memory, but
// slower because of disk access
// -- Setting the Graph
// see https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_DataSetProduction
// -- Setting Predictive Power
// see https://51degrees.com/documentation/_device_detection__hash.html#DeviceDetection_Hash_PredictivePower
.build()) {
// get the details of the detection engine from the pipeline,
// to find out what data file we are using
DeviceDetectionHashEngine engine = pipeline.getElement(DeviceDetectionHashEngine.class);
logger.info("Device data file was created {}", engine.getDataFilePublishedDate());
---- Iterate over the evidence ----
// open a writer to collect the results
try (Writer writer = new OutputStreamWriter(os)) {
// read a batch of device data from the stream
int count = 0;
while (evidenceIterator.hasNext() && count < 20) {
// Flow data is the container for inputs and outputs that
// flow through the pipeline a flowdata instance is
// created by the pipeline factory method it's important
// to dispose flowdata - so wrap in a try/resources
try (FlowData flowData = pipeline.createFlowData()) {
// the evidence values in the test YAML data are read
// as a Map<String, String> - add the evidence to the
// flowData
filterEvidence((Map<String, String>) evidenceIterator.next(),
---- Do the detection ----
// carry out device-detection (and other
// pipeline actions) on the evidence
// extract device data from the flowData
DeviceData device = flowData.get(DeviceData.class);
---- use the device data - output to YAML in this case
Map<String, ? super Object> resultMap = new HashMap<>();
resultMap.put("device.ismobile", asString(device.getIsMobile()));
resultMap.put("device.platformname", asString(device.getPlatformName()));
resultMap.put("device.platformversion", asString(device.getPlatformVersion()));
// to look at all device detection properties use the following:
// resultMap.putAll(getPopulatedProperties(device, "device."));
// write document to output stream as a YAML document
yaml.dump(flowData.getEvidence().asKeyMap(), writer);
yaml.dump(resultMap, writer);
// finish the last YAML document
logger.info("Finished processing {} records", count);
if (engine.getDataSourceTier().equals("Lite")) {
logger.warn("You have used a Lite data file which has " +
"limited properties and is of limited accuracy");
logger.info("The example requires an Enterprise data file " +
"to work fully. Find out about the Enterprise " +
"data file here: https://51degrees.com/pricing");
private static Map<String, String> filterEvidence(Map<String, String> evidence, String prefix) {
return evidence.entrySet()
.filter(e -> e.getKey().startsWith(prefix))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
@SuppressWarnings({"SameParameterValue", "unused"})
private static Map<String, Object> getPopulatedProperties(DeviceData data, String prefix) {
return data.asKeyMap().entrySet()
.filter(e -> ((AspectPropertyValue<?>) e.getValue()).hasValue())
.collect(Collectors.toMap(e -> prefix + e.getKey(),
e -> ((AspectPropertyValue<?>)e.getValue()).getValue()));