/*
* Copyright 2011 Tyler Blair. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and contributors and should not be interpreted as representing official policies,
* either expressed or implied, of anybody else.
*/
package com.griefcraft.util;
import com.griefcraft.lwc.LWC;
import com.griefcraft.lwc.LWCInfo;
import com.griefcraft.sql.Database;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class Updater {
/**
* URL to the base update site
*/
public final static String UPDATE_SITE = "http://update.griefcraft.com";
/**
* Location of the plugin on the website
*/
public final static String PLUGIN_LOCATION = "/lwc/";
/**
* The folder where libraries are stored
*/
public final static String DEST_LIBRARY_FOLDER = "plugins/LWC/lib/";
/**
* The queue of files that need to be downloaded
*/
private final Queue<UpdaterFile> fileQueue = new ConcurrentLinkedQueue<UpdaterFile>();
public void init() {
// verify we have local files (e.g sqlite.jar, etc)
verifyFiles();
downloadFiles();
final LWC lwc = LWC.getInstance();
if (lwc.getConfiguration().getBoolean("core.updateNotifier", true)) {
lwc.getPlugin().getServer().getScheduler().scheduleAsyncDelayedTask(lwc.getPlugin(), new Runnable() {
public void run() {
Version latest = getLatestVersion();
if (latest != null && latest.newerThan(LWCInfo.FULL_VERSION)) {
lwc.log("An update is available. You are on version \"" + LWCInfo.FULL_VERSION.toString() + "\". The latest version is: \"" + latest.toString() + "\"");
lwc.log("LWC updates can be found on the Bukkit Dev page at: http://dev.bukkit.org/server-mods/lwc/");
}
}
});
}
}
/**
* Verify all required files exist
*/
private void verifyFiles() {
// SQLite libraries
if (Database.DefaultType == Database.Type.SQLite) {
// sqlite.jar
this.verifyFile(new UpdaterFile(DEST_LIBRARY_FOLDER + "sqlite.jar", UPDATE_SITE + "/shared/lib/sqlite.jar"));
String nativeLibraryPath = getFullNativeLibraryPath();
if (nativeLibraryPath != null) {
// Native library
this.verifyFile(new UpdaterFile(getFullNativeLibraryPath(), UPDATE_SITE + "/shared/lib/" + nativeLibraryPath.replaceAll(DEST_LIBRARY_FOLDER, "")));
} else {
// XXX backwards compat:- nuke any old Linux binaries so that SQLite does not load them and then crash the JVM
File file = new File(DEST_LIBRARY_FOLDER + "native/Linux/amd64/libsqlitejdbc.so");
if (file.exists()) {
file.delete();
}
}
}
}
/**
* Verify a file and if it does not exist, download it
*
* @param updaterFile
* @return true if the file was queued to be downloaded
*/
private boolean verifyFile(UpdaterFile updaterFile) {
if (updaterFile == null) {
return false;
}
File file = new File(updaterFile.getLocalLocation());
// Does it exist on the FS?
if (file.exists()) {
// So it does!
return false;
}
// It does not exist ..
fileQueue.offer(updaterFile);
return true;
}
/**
* @return the full path to the native library for sqlite. null if none exists
*/
public String getFullNativeLibraryPath() {
String osFolder = getOSSpecificFolder();
String osFileName = getOSSpecificFileName();
if (osFolder == null || osFileName == null) {
return null;
}
return osFolder + osFileName;
}
/**
* @return the os/arch specific file name for sqlite's native library. null if none exists
*/
public String getOSSpecificFileName() {
String osname = System.getProperty("os.name").toLowerCase();
if (osname.contains("windows")) {
return "sqlitejdbc.dll";
} else if (osname.contains("mac")) {
return "libsqlitejdbc.jnilib";
} else if (osname.contains("bsd")) {
return null;
} else { /* We assume linux */
return "libsqlitejdbc.so";
}
}
/**
* @return the os/arch specific folder location for SQLite's native library. null if none exists
*/
public String getOSSpecificFolder() {
String osname = System.getProperty("os.name").toLowerCase();
String arch = System.getProperty("os.arch").toLowerCase();
if (osname.contains("windows")) {
return DEST_LIBRARY_FOLDER + "native/Windows/" + arch + "/";
} else if (osname.contains("mac")) {
return DEST_LIBRARY_FOLDER + "native/Mac/" + arch + "/";
} else if (osname.contains("bsd")) {
return null;
} else { /* We assume linux */
return DEST_LIBRARY_FOLDER + "native/Linux/" + arch + "/";
}
}
/**
* Load the latest version
*/
public Version getLatestVersion() {
// by default, use the LATEST file
try {
URL url = new URL(UPDATE_SITE + PLUGIN_LOCATION + "branch/stable/LATEST");
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
// read in the first line
String line = reader.readLine();
reader.close();
// parse it and we are done
return new Version(line);
} catch (MalformedURLException e) {
exceptionCaught(e);
} catch (IOException e) {
exceptionCaught(e);
}
return null;
}
/**
* Download all the files in the queue
*/
public void downloadFiles() {
synchronized (fileQueue) {
UpdaterFile updaterFile = null;
LWC lwc = LWC.getInstance();
while ((updaterFile = fileQueue.poll()) != null) {
try {
File local = new File(updaterFile.getLocalLocation());
String remote = updaterFile.getRemoteLocation();
lwc.log("Downloading file " + local.getName());
// check for LWC folder
File folder = new File("plugins/LWC/");
if (!folder.exists()) {
folder.mkdir();
}
// check native folders
String nativeLibraryFolder = getOSSpecificFolder();
if (nativeLibraryFolder != null) {
folder = new File(nativeLibraryFolder);
if (!folder.exists()) {
folder.mkdirs();
}
}
if (local.exists()) {
local.delete();
}
// create the local file
local.createNewFile();
// open the file
OutputStream outputStream = new FileOutputStream(local);
// Connect to the server
URL url = new URL(remote);
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
// hopefully, the content length provided isn't -1
int contentLength = connection.getContentLength();
// Keep a running tally
int bytesTransffered = 0;
long lastUpdate = 0L;
// begin transferring
byte[] buffer = new byte[1024];
int read;
while ((read = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, read);
bytesTransffered += read;
if (contentLength > 0) {
if (System.currentTimeMillis() - lastUpdate > 500L) {
int percentTransferred = (int) (((float) bytesTransffered / contentLength) * 100);
lastUpdate = System.currentTimeMillis();
// omit 100% ..
if (percentTransferred != 100) {
lwc.log(percentTransferred + "%");
}
}
}
}
// ok!
outputStream.close();
inputStream.close();
} catch (IOException e) {
exceptionCaught(e);
}
}
}
}
/**
* Called when an exception is caught
*
* @param e
*/
private void exceptionCaught(Exception e) {
LWC lwc = LWC.getInstance();
lwc.log("[LWC] The updater ran into a minor issue: " + e.getMessage());
lwc.log("[LWC] This can probably be ignored.");
}
}