com.bdaum.zoom.ui.internal.job.UpdateJob.java Source code

Java tutorial

Introduction

Here is the source code for com.bdaum.zoom.ui.internal.job.UpdateJob.java

Source

/*
 * This file is part of the ZoRa project: http://www.photozora.org.
 *
 * ZoRa 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 2 of the License, or
 * (at your option) any later version.
 *
 * ZoRa 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 ZoRa; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * (c) 2009 Berthold Daum  
 */

package com.bdaum.zoom.ui.internal.job;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.osgi.framework.Version;

import com.bdaum.zoom.core.Constants;
import com.bdaum.zoom.core.Core;
import com.bdaum.zoom.ui.dialogs.AcousticMessageDialog;
import com.bdaum.zoom.ui.internal.UiActivator;

public class UpdateJob extends AbstractUpdateJob {

    private static final String ALGORITHM = "SHA-512"; //$NON-NLS-1$
    private static final String UPDATE = Messages.Updater_update;
    private static final String VERSION = "{version}"; //$NON-NLS-1$
    private File downloadFile;
    private List<String> packages = new ArrayList<String>(4);
    private final Version publishedVersion;
    MessageDigest sha1;
    protected int result;

    public UpdateJob(Version publishedVersion, File downloadFile) {
        super(Messages.Updater_updater);
        this.publishedVersion = publishedVersion;
        this.downloadFile = downloadFile;
        String os = Platform.getOS();
        String ws = Platform.getWS();
        String osArch = Platform.getOSArch();
        String prefix = "ZoRa_" + publishedVersion + '_' + os + '.'; //$NON-NLS-1$
        if (Platform.OS_WIN32.equals(os)) {
            if (Platform.WS_WPF.equals(ws)) {
                packages.add(prefix + ws + '.' + Platform.ARCH_X86);
                packages.add(prefix + Platform.WS_WIN32 + '.' + Platform.ARCH_X86);
            } else if (Platform.WS_WIN32.equals(ws)) {
                if (Platform.ARCH_X86_64.equals(osArch)) {
                    packages.add(prefix + ws + '.' + osArch);
                    packages.add(prefix + ws + '.' + Platform.ARCH_X86);
                } else
                    packages.add(prefix + ws + '.' + osArch);
            }
        } else if (Platform.OS_LINUX.equals(os)) {
            prefix = os + '.';
            if (Platform.WS_GTK.equals(ws) || Platform.WS_MOTIF.equals(ws)) {
                if (Platform.ARCH_X86_64.equals(osArch)) {
                    packages.add(prefix + ws + '.' + osArch);
                    packages.add(prefix + ws + '.' + Platform.ARCH_X86);
                } else
                    packages.add(prefix + ws + '.' + osArch);
            } else if (Platform.WS_MOTIF.equals(ws))
                packages.add(prefix + ws + '.' + Platform.ARCH_X86);
        } else if (Platform.OS_MACOSX.equals(os)) {
            prefix = os + '.';
            if (Platform.WS_CARBON.equals(ws))
                packages.add(prefix + ws + '.' + Platform.ARCH_X86);
            else if (Platform.WS_COCOA.equals(ws)) {
                if (Platform.ARCH_X86_64.equals(osArch)) {
                    packages.add(prefix + ws + '.' + osArch);
                    packages.add(prefix + ws + '.' + Platform.ARCH_X86);
                    packages.add(prefix + Platform.WS_CARBON + '.' + osArch);
                    packages.add(prefix + Platform.WS_CARBON + '.' + Platform.ARCH_X86);
                } else {
                    packages.add(prefix + ws + '.' + osArch);
                    packages.add(prefix + Platform.WS_CARBON + '.' + osArch);
                }
            }
        }
    }

    private File download(String pack, IProgressMonitor monitor) {
        if (sha1 == null)
            try {
                sha1 = MessageDigest.getInstance(ALGORITHM);
            } catch (NoSuchAlgorithmException e) {
                // should never happen
            }
        String filename = pack + "_install.jar"; //$NON-NLS-1$
        String sha1name = filename + ".sha-1.txt"; //$NON-NLS-1$
        File file = null;
        StringTokenizer st = new StringTokenizer(System.getProperty("com.bdaum.zoom.mirrors"), ","); //$NON-NLS-1$ //$NON-NLS-2$
        while (st.hasMoreTokens() && !monitor.isCanceled()) {
            String token = st.nextToken();
            token = replaceVersion(token, publishedVersion);
            String uri = token + filename;
            try {
                monitor.subTask(Messages.Updater_downloading);
                file = File.createTempFile(Constants.APPNAME + "Installer.JAR", ".jar"); //$NON-NLS-1$ //$NON-NLS-2$
                URLConnection uc = new URI(uri).toURL().openConnection();
                int contentLength = uc.getContentLength();
                if (!"application/x-java-archive".equals(uc.getContentType()) || contentLength == -1) //$NON-NLS-1$
                    continue;
                if (monitor.isCanceled())
                    break;
                try (InputStream bin = new BufferedInputStream(uc.getInputStream())) {
                    int len = Core.copyBytesToFile(bin, file, monitor);
                    if (len <= 0 || len != contentLength) {
                        UiActivator.getDefault().logError(NLS.bind(Messages.Updater_download_failed,
                                new Object[] { file, contentLength, len }), null);
                        continue;
                    }
                }
                if (monitor.isCanceled())
                    break;
                UiActivator.getDefault().logInfo(NLS.bind(Messages.Updater_download_successful, file));
                if (sha1 != null) {
                    monitor.subTask(Messages.Updater_checking);
                    String filepath = file.getAbsolutePath();
                    try {
                        String hash = calculateHash(sha1, filepath);
                        if (monitor.isCanceled())
                            break;
                        String property = System.getProperty("com.bdaum.zoom.hash"); //$NON-NLS-1$
                        property = replaceVersion(property, publishedVersion);
                        try (Reader reader = new InputStreamReader(
                                new URI(property + sha1name).toURL().openStream())) {
                            char[] cbuf = new char[40];
                            int read = reader.read(cbuf);
                            if (read < 40) {
                                UiActivator.getDefault().logError(Messages.Updater_bad_hash_key, null);
                                return null;
                            }
                            if (monitor.isCanceled())
                                break;
                            if (!new String(cbuf).equals(hash)) {
                                UiActivator.getDefault()
                                        .logError(NLS.bind(Messages.Updater_key_does_not_match, filepath), null);
                                continue;
                            }
                            return file;
                        } catch (URISyntaxException e) {
                            UiActivator.getDefault().logError(NLS.bind(Messages.Updater_illegal_sha1, uri), e);
                            return null;
                        } catch (IOException e) {
                            UiActivator.getDefault()
                                    .logError(NLS.bind(Messages.Updater_download_sha1_failed, property), e);
                            return null;
                        }
                    } catch (Exception e) {
                        UiActivator.getDefault()
                                .logError(NLS.bind(Messages.Updater_computation_sha1_failed, filepath), e);
                        continue;
                    }
                }
                break;
            } catch (IOException e1) {
                UiActivator.getDefault()
                        .logError(NLS.bind(Messages.Updater_download_from_update_site_failed, token), e1);
            } catch (URISyntaxException e1) {
                UiActivator.getDefault().logError(NLS.bind(Messages.Updater_illegal_uri_during_update, uri), e1);
            }
        }
        return null;
    }

    private static String replaceVersion(String property, Version version) {
        int p = property.indexOf(VERSION);
        if (p < 0)
            return property;
        return property.substring(0, p) + version.toString() + property.substring(p + VERSION.length());
    }

    public static String calculateHash(MessageDigest algorithm, String fileName) throws Exception {
        try (DigestInputStream dis = new DigestInputStream(new BufferedInputStream(new FileInputStream(fileName)),
                algorithm)) {
            // read the file and update the hash calculation
            while (dis.read() != -1) {
                // do nothing
            }
            return byteArray2Hex(algorithm.digest());
        }
    }

    private static String byteArray2Hex(byte[] hash) {
        try (Formatter formatter = new Formatter()) {
            for (byte b : hash)
                formatter.format("%02x", b); //$NON-NLS-1$
            return formatter.toString();
        }
    }

    @Override
    protected IStatus runJob(IProgressMonitor monitor) {
        monitor.beginTask(Messages.Updater_updating, IProgressMonitor.UNKNOWN);
        if (downloadFile == null)
            for (String pack : packages) {
                if (monitor.isCanceled())
                    break;
                downloadFile = download(pack, monitor);
                if (downloadFile != null)
                    break;
            }
        if (monitor.isCanceled())
            return Status.CANCEL_STATUS;
        Display display = Display.getDefault();
        if (display.isDisposed())
            return Status.CANCEL_STATUS;
        if (downloadFile == null) {
            display.syncExec(() -> MessageDialog.openError(null, Constants.APPNAME + UPDATE,
                    Messages.Updater_download_of_new_version_failed));
            return Status.CANCEL_STATUS;
        }
        if (monitor.isCanceled())
            return Status.CANCEL_STATUS;
        display.syncExec(() -> {
            AcousticMessageDialog dialog = new AcousticMessageDialog(null, Constants.APPNAME + UPDATE, null,
                    NLS.bind(Messages.Updater_installer_package_successfully_downloaded, downloadFile.getName()),
                    MessageDialog.QUESTION, new String[] { Messages.Updater_yes, Messages.Updater_no }, 0);
            result = dialog.open();
        });
        if (monitor.isCanceled())
            return Status.CANCEL_STATUS;
        switch (result) {
        case 0:
            Location installLocation = Platform.getInstallLocation();
            URL url = installLocation == null ? null : installLocation.getURL();
            URI uri = null;
            if (url != null)
                try {
                    uri = url.toURI();
                } catch (URISyntaxException e) {
                    // ignore
                }
            if (monitor.isCanceled())
                return Status.CANCEL_STATUS;
            String[] cmdarray = null;
            if (url != null) {
                String instancepath = Platform.getInstanceLocation().getURL().getPath();
                String installpath = Platform.getInstallLocation().getURL().getPath();
                String installType = (instancepath.startsWith(installpath)) ? "install" : "user"; //$NON-NLS-1$ //$NON-NLS-2$
                Properties props = new Properties();
                props.put("INSTALL_PATH", new File(uri).getParentFile().getPath()); //$NON-NLS-1$
                props.put("configuration.installFor", installType); //$NON-NLS-1$
                try {
                    File propertyFile = File.createTempFile(Constants.APPNAME + "Installer.Properties", //$NON-NLS-1$
                            ".properties"); //$NON-NLS-1$
                    try (FileOutputStream out = new FileOutputStream(propertyFile)) {
                        props.store(out, null);
                    }
                    cmdarray = new String[] { downloadFile.getAbsolutePath(), "-options", propertyFile.getPath() };//$NON-NLS-1$
                } catch (IOException e) {
                    // do nothing
                }
            }
            if (cmdarray == null)
                cmdarray = new String[] { downloadFile.getAbsolutePath() };
            UiActivator.getDefault().setUpdaterCommand(cmdarray);
            return Status.OK_STATUS;
        default:
            return Status.CANCEL_STATUS;
        }
    }

}