/*
* Copyright 2004 - 2008 Christian Sprajc. All rights reserved.
*
* This file is part of PowerFolder.
*
* PowerFolder 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.
*
* PowerFolder 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 PowerFolder. If not, see <http://www.gnu.org/licenses/>.
*
* $Id: Updater.java 6236 2008-12-31 15:44:10Z tot $
*/
package de.dal33t.powerfolder.util.update;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import de.dal33t.powerfolder.Constants;
import de.dal33t.powerfolder.Controller;
import de.dal33t.powerfolder.util.FileUtils;
import de.dal33t.powerfolder.util.Reject;
import de.dal33t.powerfolder.util.StreamCallback;
import de.dal33t.powerfolder.util.StringUtils;
import de.dal33t.powerfolder.util.Util;
/**
* A Thread that checks for updates on powerfolder
*
* @author <a href="mailto:totmacher@powerfolder.com">Christian Sprajc </a>
* @version $Revision: 1.27 $
*/
public class Updater extends Thread {
private static Logger LOG = Logger.getLogger(Updater.class.getName());
protected Controller controller;
protected UpdateSetting settings;
private UpdaterHandler handler;
public Updater(Controller controller, UpdateSetting settings,
UpdaterHandler handler)
{
super("Update checker");
Reject.ifNull(controller, "Controller is null");
Reject.ifNull(settings, "Settings are null");
Reject.ifNull(handler, "Handler is null");
this.controller = controller;
this.settings = settings;
this.handler = handler;
}
public void run() {
checkForNewRelease();
}
/**
* Installs a periodical (usually once per hour) update check with the given
* handler.
*
* @param controller
* @param updateHandler
*/
public static void installPeriodicalUpdateCheck(
final Controller controller, final UpdaterHandler updateHandler)
{
Reject.ifNull(controller, "Controller is null");
TimerTask updateCheckTask = new TimerTask() {
@Override
public void run() {
// Check for an update
if (controller.getUpdateSettings() != null) {
new Updater(controller, controller.getUpdateSettings(),
updateHandler).start();
}
}
};
// Check for shortly after start.
controller.scheduleAndRepeat(updateCheckTask,
Controller.getWaitTime() * 3,
1000L * 60 * Constants.UPDATE_CHECK_PERIOD_MINUTES);
}
/**
* Checks for new application release at the remote location
*/
private void checkForNewRelease() {
LOG.info("Checking for newer version");
if (!handler.shouldCheckForNewVersion()) {
return;
}
final String newerVersion = newerReleaseVersionAvailable();
if (newerVersion != null) {
handler.newReleaseAvailable(new UpdaterEvent(this, newerVersion,
getReleaseExeURL()));
} else {
handler.noNewReleaseAvailable(new UpdaterEvent(this));
}
}
/**
* Method that downloads and installs the version of PowerFolder from the
* given URL.
*
* @param url
* @param progressCallback
* @param silentUpdate
* @return the updater Process or null if failed.
*/
public Process downloadAndUpdate(URL url, StreamCallback progressCallback,
boolean silentUpdate)
{
File releaseExe = download(url, progressCallback);
if (releaseExe == null) {
return null;
}
return openReleaseExe(releaseExe, silentUpdate);
}
/**
* Downloads a new powerfolder release file from a URL.
*
* @param url
* the url
* @param progressCallback
* the callback to monitor the download.
* @return the downloaded file if succeeded or null if failed
*/
public File download(URL url, StreamCallback progressCallback) {
URLConnection con;
String filename = url.getFile();
if (StringUtils.isBlank(filename)) {
filename = "PowerFolder_Latest_Win32_Installer.exe";
}
File targetFile = new File(Controller.getTempFilesLocation(), filename);
try {
con = url.openConnection();
con.connect();
} catch (IOException e) {
LOG.log(Level.SEVERE, "Unable to download from " + url, e);
return null;
}
LOG.log(Level.WARNING, "Downloading latest version from "
+ con.getURL());
File tempFile = new File(targetFile.getParentFile(), "(downloading) "
+ targetFile.getName());
try {
// Copy/Download from URL
con.connect();
FileUtils.copyFromStreamToFile(con.getInputStream(), tempFile,
progressCallback != null ? progressCallback : null, con
.getContentLength());
} catch (IOException e) {
LOG.log(Level.WARNING, "Unable to download from " + url, e);
return null;
}
// Rename file and set modified/build time
targetFile.delete();
tempFile.renameTo(targetFile);
targetFile.setLastModified(con.getLastModified());
if (targetFile.getName().toLowerCase().endsWith("jar")) {
// Additional jar check
if (!FileUtils.isValidZipFile(targetFile)) {
// Invalid file downloaded
targetFile.delete();
return null;
}
}
return targetFile;
}
/**
* @return the newer program version available on the net. Otherwise returns
* null.
*/
private String newerReleaseVersionAvailable() {
String latestVersion = latestReleaseVersionAvailable();
if (latestVersion == null) {
LOG.warning("Unable to retrieve latest version from "
+ settings.versionCheckURL);
return null;
}
if (Util.compareVersions(latestVersion, Controller.PROGRAM_VERSION)) {
LOG.info("Latest version is newer than this one");
return latestVersion;
}
LOG.info("This version is up-to-date");
return null;
}
/**
* @return the latest program version available on the net.
* @private public because of test
*/
public String latestReleaseVersionAvailable() {
URL url;
try {
url = new URL(settings.versionCheckURL);
} catch (MalformedURLException e) {
LOG.log(Level.FINER, e.toString(), e);
return null;
}
try {
InputStream in = (InputStream) url.getContent();
String latestVersion = "";
int read;
while ((read = in.read()) >= 0) {
latestVersion += (char) read;
}
if (latestVersion != null) {
latestVersion = latestVersion.trim();
if (latestVersion.length() > 50) {
LOG.log(Level.SEVERE,
"Received illegal response while checking latest available version from "
+ settings.versionCheckURL);
return null;
}
LOG.fine("Latest available version: " + latestVersion + " @ "
+ settings.versionCheckURL);
return latestVersion;
}
} catch (IOException e) {
LOG.log(Level.WARNING,
"Unable to retrieve latest available version for: "
+ settings.versionCheckURL);
LOG.log(Level.FINER, e.toString(), e);
}
return null;
}
/**
* Returns the download URL for the latest program version
*
* @return
*/
private URL getReleaseExeURL() {
URL releaseExeURL = null;
try {
if (StringUtils.isNotBlank(settings.downloadLinkInfoURL)) {
URL url = new URL(settings.downloadLinkInfoURL);
InputStream in = (InputStream) url.getContent();
StringBuilder b = new StringBuilder();
while (in.available() > 0) {
b.append((char) in.read());
}
in.close();
releaseExeURL = new URL(b.toString());
LOG.info("Latest available version download: "
+ releaseExeURL.toExternalForm());
}
} catch (MalformedURLException e) {
LOG.log(Level.FINER, e.toString(), e);
} catch (IOException e) {
LOG.log(Level.FINER, e.toString(), e);
}
if (releaseExeURL == null) {
// Fallback to standart settings
try {
releaseExeURL = new URL(settings.windowsExeURL);
} catch (MalformedURLException e) {
LOG.log(Level.SEVERE, "Invalid release exec download location",
e);
}
}
return releaseExeURL;
}
private Process openReleaseExe(File file, boolean updateSilently) {
try {
String c = "cmd.exe";
c += " /c ";
c += '"';
c += file.getAbsolutePath();
if (updateSilently) {
c += " /S";
}
c += '"';
LOG.info("Executing: " + c);
return Runtime.getRuntime().exec(c);
} catch (IOException e) {
LOG.log(Level.SEVERE, "Unable to start update exe at "
+ file.getAbsolutePath() + ". " + e, e);
return null;
}
}
}