io.gromit.uaparser.Parser.java Source code

Java tutorial

Introduction

Here is the source code for io.gromit.uaparser.Parser.java

Source

/**
 * Copyright 2016 gromit.it
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gromit.uaparser;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.MessageDigest;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

import io.gromit.uaparser.cache.Cache;
import io.gromit.uaparser.cache.NoCache;
import io.gromit.uaparser.model.Client;
import io.gromit.uaparser.model.Device;
import io.gromit.uaparser.model.OS;
import io.gromit.uaparser.parsers.DeviceParser;
import io.gromit.uaparser.parsers.OSParser;
import io.gromit.uaparser.parsers.UserAgentParser;
import io.gromit.uaparser.model.Browser;

/**
 * The Class Parser.
 */
public class Parser {

    /** The logger. */
    private static Logger logger = LoggerFactory.getLogger(Parser.class);

    /** The Constant FAIL_SAFE_URL. */
    public static final String FAIL_SAFE_URL = "io.gromit.uaparser.fail.safe.url";

    /** The scheduled executor service. */
    private ScheduledExecutorService scheduledExecutorService;

    /** The ua regex yaml. */
    private String uaRegexYaml = "https://raw.githubusercontent.com/ua-parser/uap-core/master/regexes.yaml";

    /** The ua regex yaml m d5. */
    private String uaRegexYamlMD5 = null;

    /** The cache. */
    private Cache cache = NoCache.NO_CACHE;

    /** The parsers. */
    private Parsers parsers = null;

    /** The clean cache on update. */
    private Boolean cleanCacheOnUpdate = false;

    /** The loader listener. */
    private LoaderListener loaderListener = new LoaderListener() {
        @Override
        public void success(String url) {
        }

        @Override
        public void failure(String url, Exception e) {
        }
    };

    /**
     * Instantiates a new parser.
     *
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     */
    public Parser() throws IOException {
        initialize(uaRegexYaml);
    }

    /**
     * Instantiates a new parser.
     *
     * @param regexYaml
     *            the regex yaml
     */
    public Parser(InputStream regexYaml) {
        initialize(regexYaml);
    }

    /**
     * Ua regex yaml.
     *
     * @param url the url
     * @return the parser
     */
    public Parser uaRegexYaml(String url) {
        this.uaRegexYaml = url;
        return this;
    }

    /**
     * Cache.
     *
     * @param cache the cache
     * @return the parser
     */
    public Parser cache(Cache cache) {
        this.cache = cache;
        return this;
    }

    /**
     * Loader listener.
     *
     * @param loaderListener the loader listener
     * @return the parser
     */
    public Parser loaderListener(LoaderListener loaderListener) {
        this.loaderListener = loaderListener;
        return this;
    }

    /**
     * Clean cache on update.
     *
     * @param cleanCacheOnUpdate the clean cache on update
     * @return the parser
     */
    public Parser cleanCacheOnUpdate(Boolean cleanCacheOnUpdate) {
        this.cleanCacheOnUpdate = cleanCacheOnUpdate;
        return this;
    }

    /**
     * Start schedule.
     *
     * @return the parser
     */
    public Parser startSchedule() {
        scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                initialize(uaRegexYaml);
            }
        }, 0, 1, TimeUnit.HOURS);
        return this;
    }

    /**
     * Stop schedule.
     *
     * @return the parser
     */
    public Parser stopSchedule() {
        scheduledExecutorService.shutdown();
        return this;
    }

    /**
     * Parses the.
     *
     * @param agentString
     *            the agent string
     * @return the client
     */
    public Client parse(String agentString) {
        Parsers parsersForCall = parsers;
        Browser browser = parsersForCall.uaParser.parse(agentString, cache);
        OS os = parsersForCall.osParser.parse(agentString, cache);
        Device device = parsersForCall.deviceParser.parse(agentString, cache);
        return new Client(agentString, browser, os, device);
    }

    /**
     * Parses the browser.
     *
     * @param agentString the agent string
     * @return the browser
     */
    public Browser parseBrowser(String agentString) {
        return parsers.uaParser.parse(agentString, cache);
    }

    /**
     * Parses the os.
     *
     * @param agentString the agent string
     * @return the os
     */
    public OS parseOS(String agentString) {
        return parsers.osParser.parse(agentString, cache);
    }

    /**
     * Parses the device.
     *
     * @param agentString the agent string
     * @return the device
     */
    public Device parseDevice(String agentString) {
        return parsers.deviceParser.parse(agentString, cache);
    }

    /**
     * Initialize.
     *
     * @param url the url
     */
    private void initialize(String url) {
        try {
            initialize(new URL(url).openStream());
            loaderListener.success(url);
            logger.info("reloaded ua-parser from remote url {}", url);
        } catch (Exception e) {
            logger.error("error loading from remote", e);
            loaderListener.failure(url, e);
            if (StringUtils.isNotBlank(System.getProperty(FAIL_SAFE_URL))
                    && !System.getProperty(FAIL_SAFE_URL).equalsIgnoreCase(url)) {
                logger.info("re-loading from ua-parser fail safe url {}", url);
                initialize(System.getProperty(FAIL_SAFE_URL));
            }
        }
    }

    /**
     * Initialize.
     *
     * @param regexYaml
     *            the regex yaml
     */
    private void initialize(InputStream regexYaml) {
        byte[] bytes = null;
        String newMD5 = null;
        try {
            bytes = IOUtils.toByteArray(regexYaml);
            newMD5 = new String(MessageDigest.getInstance("MD5").digest(bytes));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (newMD5.equals(uaRegexYamlMD5)) {
            logger.info("same MD5 content for both files, do not load it");
            return;
        }
        uaRegexYamlMD5 = newMD5;
        Yaml yaml = new Yaml(new SafeConstructor());
        @SuppressWarnings("unchecked")
        Map<String, List<Map<String, String>>> regexConfig = (Map<String, List<Map<String, String>>>) yaml
                .load(new ByteArrayInputStream(bytes));

        List<Map<String, String>> uaParserConfigs = regexConfig.get("user_agent_parsers");
        if (uaParserConfigs == null) {
            throw new IllegalArgumentException("user_agent_parsers is missing from yaml");
        }
        UserAgentParser uaParser = UserAgentParser.fromList(uaParserConfigs);

        List<Map<String, String>> osParserConfigs = regexConfig.get("os_parsers");
        if (osParserConfigs == null) {
            throw new IllegalArgumentException("os_parsers is missing from yaml");
        }
        OSParser osParser = OSParser.fromList(osParserConfigs);

        List<Map<String, String>> deviceParserConfigs = regexConfig.get("device_parsers");
        if (deviceParserConfigs == null) {
            throw new IllegalArgumentException("device_parsers is missing from yaml");
        }
        DeviceParser deviceParser = DeviceParser.fromList(deviceParserConfigs);
        parsers = new Parsers(uaParser, osParser, deviceParser);
        if (cleanCacheOnUpdate) {
            cache.clean();
        }
    }

    /**
     * The Class Parsers.
     */
    public static class Parsers {

        /** The ua parser. */
        public final UserAgentParser uaParser;

        /** The os parser. */
        public final OSParser osParser;

        /** The device parser. */
        public final DeviceParser deviceParser;

        /**
         * Instantiates a new parsers.
         *
         * @param uaParser the ua parser
         * @param osParser the os parser
         * @param deviceParser the device parser
         */
        public Parsers(UserAgentParser uaParser, OSParser osParser, DeviceParser deviceParser) {
            this.uaParser = uaParser;
            this.osParser = osParser;
            this.deviceParser = deviceParser;
        }

    }
}