/** * Copyright 2009 Marc Stogaitis and Mimi Sun * * 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 org.gmote.server.updater; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; import javax.swing.JOptionPane; import org.jdesktop.swingworker.SwingWorker; public class Updater { private static final String CONFIRM_UPDATE_TITLE = "Confirm Gmote Server Update"; private static final Logger LOGGER = Logger.getLogger(FileExtractor.class.getName()); private static final String VERSION = "1.0.0"; /** * @param args * currentServerVersion updateUrl outputDirectory * [onlyDownloadIfMajorUpdate] * @throws IOException * @throws SecurityException */ public static void main(String[] args) throws SecurityException, IOException { Logger defaultLogger = Logger.getLogger(""); for (Handler handler : defaultLogger.getHandlers()) { defaultLogger.removeHandler(handler); } FileHandler logFileHandler = new FileHandler("gmoteupdater.log"); logFileHandler.setFormatter(new SimpleFormatter()); logFileHandler.setLevel(Level.INFO); defaultLogger.addHandler(logFileHandler); Handler consoleHandler = new ConsoleHandler(); consoleHandler.setEncoding("UTF-8"); defaultLogger.addHandler(consoleHandler); if (args.length == 1 && args[0].equalsIgnoreCase("getversion")) { System.out.println(VERSION); System.exit(0); } else if (args.length < 3) { System.out .println("Invalid params: Usage: java Updater currentServerVersion updateUrl outputDirectory [onlyDownloadIfMajorUpdate]"); System.exit(1); } String currentServerVersion = args[0]; String updateUrl = args[1]; String outputDirectory = args[2]; // A major update is defined as a change in the second digit of a 3 digit // version number. // ex: 1.2.0 is a major update from 1.1.0, but 1.1.5 is not a major update // from 1.1.0. boolean onlyDownloadIfMajorUpdate = false; // Indicates that we should wait a bit before downloading, to allow the // server to close properly. boolean sleepBeforeDownload = false; boolean ignoreConfirmDialog = false; for (int i = 3; i < args.length; i++) { if (args[i].equalsIgnoreCase("onlyDownloadIfMajorUpdate")) { onlyDownloadIfMajorUpdate = true; } else if (args[i].equalsIgnoreCase("sleepBeforeDownload")) { sleepBeforeDownload = true; } else if (args[i].equalsIgnoreCase("ignoreConfirmDialog")) { ignoreConfirmDialog = true; } } if (sleepBeforeDownload) { try { LOGGER.info("Making sure that the Gmote Server program was closed properly..."); Thread.sleep(5000); } catch (InterruptedException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); } } Updater updater = new Updater(); try { updater.updateSoftware(currentServerVersion, updateUrl, new File(outputDirectory), onlyDownloadIfMajorUpdate, ignoreConfirmDialog); } catch (MalformedURLException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, e.getMessage()); } catch (IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, e.getMessage()); } catch (Exception e) { // Catching exceptions here to prevent them from getting displayed to the // user. LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, e.getMessage()); } } public static String getLatestVersionNumber(String updateUrl) throws IOException { List<String> updateInfo = downloadUpdateInformation(updateUrl); if (updateInfo.isEmpty()) { return null; } else { return updateInfo.get(0); } } private void updateSoftware(String currentServerVersion, String updateUrl, File outputDirectory, boolean onlyDownloadIfMajorUpdate, boolean ignoreConfirmDialog) throws IOException { LOGGER.info("Attempting to update: " + currentServerVersion + " " + updateUrl + " " + outputDirectory + " OnlyDownloadIfMajorUpdate=" + onlyDownloadIfMajorUpdate); List<String> updateInfo = downloadUpdateInformation(updateUrl); if (updateInfo == null || updateInfo.size() < 2) { throw new IOException( "Content was not retrieved from update website properly. Please try again."); } // The first line of the file is the version number. String latestVersion = updateInfo.get(0); if (!ignoreConfirmDialog && !askUserIfShouldUpdate(currentServerVersion, latestVersion, onlyDownloadIfMajorUpdate)) { LOGGER .warning("The version on the server is not different enough from the current version. Will not update. CurrentVerison=" + currentServerVersion + " LatestVersion=" + latestVersion + " OnlyDownloadIfMajorUpdate=" + onlyDownloadIfMajorUpdate); return; } // TODO(mstogaitis): Make sure the server is closed before we attempt the // update. ProgressDialog progressDialog = ProgressDialog.getInstance(); ProgressDialog.showProgressDialog(); UpdateTask task = new UpdateTask(updateInfo, outputDirectory); task.addPropertyChangeListener(progressDialog); task.execute(); } public static boolean askUserIfShouldUpdate(String currentServerVersion, String latestServerVersion, boolean onlyDownloadIfMajorUpdate) { boolean shouldUpdate = Updater.shouldUpdate(currentServerVersion, latestServerVersion, onlyDownloadIfMajorUpdate); int answer; if (shouldUpdate) { answer = JOptionPane.showConfirmDialog(null, "A newer version of the server is available. Would you like to install it now?", CONFIRM_UPDATE_TITLE, JOptionPane.YES_NO_OPTION); } else { answer = JOptionPane.showConfirmDialog(null, "You do not need an udpate at this time. The version that you currenty have is " + currentServerVersion + " and the latest available version is " + latestServerVersion + ". Would you like to update anyway?", CONFIRM_UPDATE_TITLE, JOptionPane.YES_NO_OPTION); } return (answer == JOptionPane.YES_OPTION); } /** * Returns true if the server should be updated based on version numbers. * * @return */ private static boolean shouldUpdate(String currentServerVersion, String latestServerVersion, boolean onlyDownloadIfMajorUpdate) { int[] currentVersion = convertVersion(currentServerVersion); int[] latestVersion = convertVersion(latestServerVersion); if (currentVersion[0] > latestVersion[0]) { return false; } else if (currentVersion[0] < latestVersion[0]) { return true; } if (currentVersion[1] > latestVersion[1]) { return false; } else if (currentVersion[1] < latestVersion[1]) { return true; } else if (onlyDownloadIfMajorUpdate) { return false; } if (currentVersion[2] < latestVersion[2]) { return true; } // The versions are identical. return false; } /** * Converts a string version number into ints. * * @param versionNumber * a version number in the form 1.2.3 or 1.2 * @return an array of 3 integers representing the version number. */ private static int[] convertVersion(String versionNumber) { String versionSplit[] = versionNumber.split("\\."); int version[] = new int[3]; version[0] = Integer.parseInt(versionSplit[0]); version[1] = Integer.parseInt(versionSplit[1]); if (versionSplit.length == 3) { // Some older version of the server only had two digits (ex: 1.2) version[2] = Integer.parseInt(versionSplit[2]); } else { version[2] = 0; } return version; } /** * Downloads a page from the server that will contain information about the * current update. * * @throws IOException */ static List<String> downloadUpdateInformation(String updateUrl) throws IOException { URL serverUrl = new URL(updateUrl); BufferedReader reader = new BufferedReader(new InputStreamReader(serverUrl.openStream())); List<String> content = new ArrayList<String>(); String line; while ((line = reader.readLine()) != null) { content.add(line); } reader.close(); return content; } public class UpdateTask extends SwingWorker<Void, Void> { List<String> addresses; File outputDirectory; public UpdateTask(List<String> addresses, File outputDirectory) { this.addresses = addresses; this.outputDirectory = outputDirectory; } @Override protected Void doInBackground() { List<File> downloadedFiles = new ArrayList<File>(); updateStatusLabel("Step 1: Downloading files from web..."); for (int i = 1; i < addresses.size(); i++) { String[] addressFields = addresses.get(i).split(" "); String url = addressFields[0]; long totalBytes = Long.parseLong(addressFields[1]); File downloadedFile; try { downloadedFile = FileDownloader.download(url, totalBytes, this); } catch (IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, e.getMessage()); return null; } if (downloadedFile != null) { downloadedFiles.add(downloadedFile); } else { String message = "Received a null file when downloading an update. Aborting..."; LOGGER.severe(message); JOptionPane.showMessageDialog(null, message); return null; } } updateStatusLabel("Step 2: Extracting files..."); for (File downloadedFile : downloadedFiles) { if (downloadedFile.getName().toLowerCase().endsWith(".zip")) { // Extract the zip file. try { FileExtractor.unzipArchive(downloadedFile, outputDirectory, this); } catch (Exception e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, "Error while extracting zip file. Please make " + "sure that the GmoteServer is closed and then visit www.gmote.org to" + " download the update manually.\n" + e.getMessage()); return null; } } else { // This is an installer. Simply launch it. String fullCommand = "cmd /c start /D" + downloadedFile.getParentFile().getAbsolutePath() + " " + downloadedFile.getName(); try { Runtime.getRuntime().exec(fullCommand); System.exit(0); } catch (IOException e) { LOGGER.log(Level.SEVERE, e.getMessage(), e); JOptionPane.showMessageDialog(null, "Error while starting gmote update package. Please " + "visit www.gmote.org to download the update manually. " + e.getMessage()); return null; } return null; } } updateStatusLabel("Done :). Please start the Gmote Server."); return null; } void updateStatusLabel(String text) { firePropertyChange("updatestatuslabel", null, text); } void setProgressForTask(int progress) { setProgress(progress); } int getProgressForTask() { return getProgress(); } public void setProgressBarSize(int size) { firePropertyChange("changeprogressbarsize", null, size); } } }