Package cz.mallat.uasparser

Source Code of cz.mallat.uasparser.OnlineUpdater

package cz.mallat.uasparser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

import cz.mallat.uasparser.fileparser.PHPFileParser;
import cz.mallat.uasparser.fileparser.Section;

/**
* An updater which runs in a separate background thread and will update once per day.
*
* <p>The updated UA strings are cached on disk. If no cached copy is found on start,
* one will be fetched immediately. If this initial update fails, it will fallback
* to an included copy.</p>
*
* @author chetan
*
*/
public class OnlineUpdater extends Thread {

    public static final String CACHE_FILENAME = "user_agent_strings.txt";
    public static final String PROPERTIES_FILENAME = "user_agent_strings-version.txt";

    protected static final String DATA_RETRIVE_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=ini";
    protected static final String VERSION_CHECK_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=ini&ver=y";

    protected final long updateInterval;
    protected int currentVersion;

    protected UASparser parser;

    protected File cacheFile;
    protected File propsFile;

    /**
     * Create a new updater with the default interval of 1 day
     *
     * @param parser        Parser instance to update
     */
    public OnlineUpdater(UASparser parser) {
        this(parser, null, 1, TimeUnit.DAYS);
    }

    /**
     * Create a new updater
     *
     * @param parser        Parser instance to update
     * @param cacheDir      directory where file should be cached. If null, uses system temp dir
     * @param interval      number of intervals for the given units
     * @param units         unit type
     */
    public OnlineUpdater(UASparser parser, String cacheDir, long interval, TimeUnit units) {
        this.parser = parser;

        if (cacheDir == null) {
            cacheDir = System.getProperty("java.io.tmpdir");
        }
        if (!new File(cacheDir).canWrite()) {
            throw new RuntimeException("Can't write to cacheDir: " + cacheDir);
        }
        this.cacheFile = new File(cacheDir, CACHE_FILENAME);
        this.propsFile = new File(cacheDir, PROPERTIES_FILENAME);
        this.currentVersion = 0;

        updateInterval = units.toMillis(interval);

        init();
        start();
    }

    /**
     * Initialize the parser with cached data. Falls back to vendored copy if no cache is available.
     */
    public void init() {

        if (this.cacheFile.exists()) {
            try {
                parser.loadDataFromFile(cacheFile);
                this.currentVersion =
                        Integer.parseInt(new BufferedReader(new FileReader(propsFile)).readLine());
                return;
            } catch (Throwable t) {
                this.currentVersion = 0;
            }
        }

        try {
            // fall back to vendored copy so we don't block on startup
            parser.loadDataFromFile(getVendoredInputStream());
        } catch (IOException e) {
        }
    }

    /**
     * Retrieve an {@link InputStream} to the vendored copy of the UA strings file.
     * @return {@link InputStream}
     */
    public static InputStream getVendoredInputStream() {
        return OnlineUpdater.class.getClassLoader().getResourceAsStream(CACHE_FILENAME);
    }

    /**
     * Fetch latest UA file if a newer one is available
     *
     * @return boolean True if parser was updated.
     */
    public boolean update() {
        try {
            int versionOnServer = getVersionFromServer();
            if (currentVersion == 0 || versionOnServer > currentVersion) {
                parser.createInternalDataStructure(loadDataFromInternet());

                // if reached this far then we loaded it correctly, store new version #
                currentVersion = versionOnServer;
                FileWriter writer = new FileWriter(propsFile);
                writer.write(Integer.toString(currentVersion));
                writer.close();

                return true;
            }
        } catch (Throwable t) {
        }
        return false;
    }

    /**
     * Update loop
     */
    @Override
    public void run() {
        while (true) {
            update();
            try {
                // add up to 300sec of jitter to interval
                Thread.sleep(updateInterval + (new Random().nextInt(300) * 1000));
            } catch (InterruptedException e) {
                return;
            }
        }
    }

    /**
     * Loads the data file from user-agent-string.info and caches it on disk
     * @return
     *
     * @throws IOException
     */
    protected List<Section> loadDataFromInternet() throws IOException {

        File tmpFile = File.createTempFile("uas", ".txt");

        try {

            // Download file to temp location
            BufferedReader reader = null;
            FileWriter writer = null;
            try {
                URL url = new URL(DATA_RETRIVE_URL);
                reader = new BufferedReader(new InputStreamReader(url.openStream()));
                writer = new FileWriter(tmpFile);
                String line = null;
                while ((line = reader.readLine()) != null) {
                    writer.write(line);
                    writer.write(System.getProperty("line.separator"));
                }

            } finally {
                if (reader != null) {
                    reader.close();
                }
                if (writer != null) {
                    writer.close();
                }
            }

            // Try to parse it
            try {
                PHPFileParser fp = new PHPFileParser(tmpFile);
                List<Section> sections = fp.getSections();

                // now that we've finished parsing, we can save the temp copy
                if (cacheFile.exists()) {
                    cacheFile.delete();
                }

                if (!tmpFile.renameTo(cacheFile)) {
                    // was across filesystems or target exists, or something else.
                    // Try other another way
                    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4017593
                    copyFile(tmpFile, cacheFile);
                }

                return sections;

            } catch (Throwable t) {
                if (t instanceof IOException) {
                    throw (IOException) t;
                }
                throw new IOException(t);
            }

        } finally {
            if (tmpFile.compareTo(cacheFile) != 0) {
                tmpFile.delete();
            }
        }
    }

    /**
     * Copy source file to destination
     *
     * @param src
     * @param dest
     *
     * @throws FileNotFoundException
     * @throws IOException
     */
    protected void copyFile(File src, File dest) throws FileNotFoundException, IOException {
        InputStream inStream = new FileInputStream(src);
        OutputStream outStream = new FileOutputStream(dest);

        byte[] buffer = new byte[4096];

        int length;
        while ((length = inStream.read(buffer)) > 0) {
            outStream.write(buffer, 0, length);
        }

        inStream.close();
        outStream.close();
    }

    /**
     * Gets the current version from user-agent-string.info
     *
     * @return long version number (e.g., 2013012301)
     * @throws IOException
     * @throws {@link NumberFormatException}
     */
    protected int getVersionFromServer() throws IOException {
        URL url = new URL(VERSION_CHECK_URL);
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(url.openStream()));
            String ver = reader.readLine();
            if (ver == null || ver.isEmpty()) {
                throw new IOException("Failed to read version number");
            }
            return Integer.parseInt(ver.replace("-", ""));

        } finally {
            if (reader != null) {
                reader.close();
            }
        }
    }

}
TOP

Related Classes of cz.mallat.uasparser.OnlineUpdater

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.