org.sleeksnap.updater.Updater.java Source code

Java tutorial

Introduction

Here is the source code for org.sleeksnap.updater.Updater.java

Source

/**
 * Sleeksnap, the open source cross-platform screenshot uploader
 * Copyright (C) 2012 Nikki <nikki@nikkii.us>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.sleeksnap.updater;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.WindowConstants;

import org.json.JSONException;
import org.json.JSONObject;
import org.sleeksnap.Constants.Application;
import org.sleeksnap.Constants.Version;
import org.sleeksnap.Launcher;
import org.sleeksnap.http.HttpUtil;
import org.sleeksnap.updater.Downloader.DownloadAdapter;
import org.sleeksnap.util.DesktopEntryBuilder;
import org.sleeksnap.util.Util;
import org.sleeksnap.util.Utils.FileUtils;
import org.sleeksnap.util.WinRegistry;

import com.sun.jna.Platform;

/**
 * An automatic Updater for Sleeksnap
 * 
 * Well documented to explain how it works and that it is not malicious.
 * 
 * @author Nikki
 * 
 */
public class Updater {

    public enum VerificationMode {
        INSERT, REMOVE, VERIFY
    }

    private static final Logger logger = Logger.getLogger(Updater.class.getName());

    /**
     * If on windows or linux, verify that the registry entry is intact and
     * pointing to the correct version.
     * 
     * @param file
     *            The file to verify against
     * @param mode
     *            The mode to use, REMOVE will remove the entry, VERIFY will
     *            update it, and INSERT will insert if it does not exist.
     */
    public static void verifyAutostart(final File file, final VerificationMode mode) {
        if (Platform.isWindows()) {
            try {
                final String current = WinRegistry.readString(WinRegistry.HKEY_CURRENT_USER, WinRegistry.RUN_PATH,
                        Application.NAME);
                if (mode == VerificationMode.REMOVE) {
                    WinRegistry.deleteValue(WinRegistry.HKEY_CURRENT_USER, WinRegistry.RUN_PATH, Application.NAME);
                } else if (mode == VerificationMode.INSERT && current == null
                        || current != null && !current.equals(file.getAbsolutePath())) {
                    WinRegistry.writeStringValue(WinRegistry.HKEY_CURRENT_USER, WinRegistry.RUN_PATH,
                            Application.NAME, file.getAbsolutePath());
                }
            } catch (final Exception e) {
                // Ignore it.
            }
        } else if (Platform.isX11()) {
            final File autostartDir = new File(
                    System.getenv("XDG_CONFIG_HOME") != null ? System.getenv("XDG_CONFIG_HOME")
                            : System.getProperty("user.home") + "/.config/autostart");
            if (autostartDir.exists()) {
                final File autostart = new File(autostartDir, Application.NAME.toLowerCase() + ".desktop");

                if (mode == VerificationMode.REMOVE) {
                    autostart.delete();
                } else {
                    final String contents = new DesktopEntryBuilder().addEntry("Type", "Application")
                            .addEntry("Categories", "Accessories").addEntry("Name", Application.NAME)
                            .addEntry("Comment", "Sleeksnap - Java Screenshot Program").addEntry("Terminal", false)
                            .addEntry("Exec", FileUtils.getJavaExecutable().getAbsoluteFile() + " -jar \""
                                    + file.getAbsolutePath() + "\"")
                            .build();

                    try {
                        final FileWriter writer = new FileWriter(autostart);
                        try {
                            writer.write(contents);
                        } finally {
                            writer.close();
                        }
                    } catch (final IOException e) {
                    }
                }
            }
        }
    }

    /**
     * The binary directory to use.
     */
    private final File binDirectory;

    /**
     * Construct a new updater
     */
    public Updater() {
        binDirectory = new File(Util.getWorkingDirectory(), "bin");
        binDirectory.mkdirs();
    }

    /**
     * Apply an update from the specified URL
     * 
     * @param version
     *            The version which we are downloading
     * @param url
     *            The URL to download from
     * @throws IOException
     *             If an error occurred while downloading
     */
    public void applyUpdate(final String version, final URL url) throws IOException {
        // Construct the new path
        final File out = new File(binDirectory, "Sleeksnap-v" + version + ".jar");

        logger.info("Creating new file...");
        // Create the new file
        out.createNewFile();

        logger.info("Downloading " + url + "...");
        // Download the new file
        download(url, out);
    }

    /**
     * Check for updates
     */
    public boolean checkUpdate(final UpdaterReleaseType type, final boolean prompt) {
        // Check for an update.
        try {
            logger.info("Checking for updates...");

            final String data = HttpUtil.executeGet(Application.UPDATE_URL + type.getFeedPath());

            final JSONObject obj = new JSONObject(data);

            // Compare versions
            final String ver = obj.getString("version");

            final String[] s = ver.split("\\.");
            final int major = Integer.parseInt(s[0]), minor = Integer.parseInt(s[1]),
                    patch = Integer.parseInt(s[2]);
            if (major > Version.MAJOR || major == Version.MAJOR && minor > Version.MINOR
                    || major == Version.MAJOR && minor == Version.MINOR && patch > Version.PATCH) {
                logger.info("A new version is available. Current version: " + Version.getVersionString()
                        + ", new version: " + major + "." + minor + "." + patch);
                if (prompt) {
                    final StringBuilder message = new StringBuilder();
                    message.append("There is a new version of ").append(Application.NAME).append(" available!")
                            .append("\n");
                    message.append("Your version: ").append(Version.getVersionString()).append("\n");
                    message.append("Latest version: ").append(ver).append("\n");
                    message.append("Click OK to download it, or Cancel to be prompted the next time you start ")
                            .append(Application.NAME);

                    final int choice = JOptionPane.showOptionDialog(null, message, "Update Available",
                            JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null,
                            new String[] { "OK", "Cancel" }, "OK");
                    if (choice == 0) {
                        logger.info("User confirmed the update.");

                        applyUpdate(ver, new URL(obj.getString("file")));

                        return true;
                    } else {
                        logger.info("User declined the update.");
                    }
                } else {
                    logger.info("Automatically applying update...");

                    applyUpdate(ver, new URL(obj.getString("file")));

                    return true;
                }
            } else {
                logger.info("No updates available.");
            }
        } catch (final JSONException e) {
            logger.severe("Unable to check for update due to web service error.");
        } catch (final IOException e) {
            // Unable to update
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Download the specified file with a ProgressPanel to show progress.
     * 
     * @param url
     *            The URL to download from
     * @param file
     *            The file to download to
     * @throws IOException
     *             If a problem occurred while starting the download
     */
    public void download(final URL url, final File file) throws IOException {
        final JFrame frame = new JFrame("Sleeksnap Update");
        final ProgressPanel panel = new ProgressPanel();
        frame.add(panel);
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        try {
            frame.setIconImage(ImageIO.read(Util.getResourceByName("/icon32x32.png")));
        } catch (final IOException e1) {
            e1.printStackTrace();
        }

        Util.centerFrame(frame);

        final Downloader downloader = new Downloader(url, new FileOutputStream(file));
        downloader.addListener(panel);
        downloader.addListener(new DownloadAdapter() {
            @Override
            public void downloadFinished(final Downloader downloader) {
                updateFinished(file);
            }
        });
        downloader.start();
    }

    /**
     * Called when the update is finished to re-launch Sleeksnap
     * 
     * @param file
     *            The newly downloaded jar file
     */
    public void updateFinished(final File file) {
        logger.info("Checking autostart...");
        verifyAutostart(file, VerificationMode.VERIFY);

        if (Platform.isWindows()) {
            logger.info("Checking start menu icon...");
            try {
                WindowsUpdater.checkStartMenu(file);
            } catch (final Exception e) {
                logger.log(Level.WARNING, "Unable to update start menu icon", e);
            }
        }

        logger.info("Launching new file...");
        try {
            Launcher.launch(file, Launcher.class.getName());

            System.exit(0);
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }
}