From 0ff4e22a0214074627df54bbe085b7635b9ebb7a Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Sat, 14 Apr 2018 12:56:38 +0300 Subject: [PATCH] Fixed Update downloading, old jar deletion still broken --- .../djrapitops/plan/command/PlanCommand.java | 1 + .../plan/command/commands/UpdateCommand.java | 62 ++++++--- .../djrapitops/plan/system/PlanSystem.java | 2 +- .../system/update/ShutdownUpdateHook.java | 127 +++++++++++------- .../system/update/VersionCheckSystem.java | 5 +- .../system/update/ShutdownUpdateHookTest.java | 41 ++++++ 6 files changed, 168 insertions(+), 70 deletions(-) create mode 100644 Plan/src/test/java/com/djrapitops/plan/system/update/ShutdownUpdateHookTest.java diff --git a/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java index 6b2ca3f1c..50195b0ff 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/PlanCommand.java @@ -36,6 +36,7 @@ public class PlanCommand extends TreeCmdNode { new ListCommand(), new AnalyzeCommand(), new NetworkCommand(), + new ListServersCommand(plugin) }, new CommandNode[]{ new WebUserCommand(plugin, registerCommand, this), diff --git a/Plan/src/main/java/com/djrapitops/plan/command/commands/UpdateCommand.java b/Plan/src/main/java/com/djrapitops/plan/command/commands/UpdateCommand.java index ad5f473ad..080946e7d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/command/commands/UpdateCommand.java +++ b/Plan/src/main/java/com/djrapitops/plan/command/commands/UpdateCommand.java @@ -6,17 +6,22 @@ import com.djrapitops.plan.command.commands.manage.ManageConDebugCommand; import com.djrapitops.plan.system.database.databases.Database; import com.djrapitops.plan.system.database.databases.operation.FetchOperations; import com.djrapitops.plan.system.info.InfoSystem; -import com.djrapitops.plan.system.info.request.CheckConnectionRequest; import com.djrapitops.plan.system.info.request.UpdateCancelRequest; +import com.djrapitops.plan.system.info.request.UpdateRequest; import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.settings.Permissions; +import com.djrapitops.plan.system.settings.locale.Locale; +import com.djrapitops.plan.system.settings.locale.Msg; import com.djrapitops.plan.system.update.VersionCheckSystem; import com.djrapitops.plan.system.update.VersionInfo; import com.djrapitops.plan.system.webserver.WebServerSystem; import com.djrapitops.plugin.api.utility.log.Log; import com.djrapitops.plugin.command.CommandNode; import com.djrapitops.plugin.command.CommandType; +import com.djrapitops.plugin.command.CommandUtils; import com.djrapitops.plugin.command.ISender; +import com.djrapitops.plugin.task.AbsRunnable; +import com.djrapitops.plugin.task.RunnableFactory; import java.util.List; import java.util.Map; @@ -32,13 +37,13 @@ public class UpdateCommand extends CommandNode { public UpdateCommand() { super("update", Permissions.MANAGE.getPermission(), CommandType.ALL); - setArguments("[-update]/[cancel]"); + setArguments("[-u]/[cancel]"); setShortHelp("Get change log link or update plugin."); setInDepthHelp( "/plan update", " Used to update the plugin on the next shutdown\n", " /plan update - get change log link", - " /plan update -update - Schedule update to happen on all network servers that are online next time they reboot.", + " /plan update -u - Schedule update to happen on all network servers that are online next time they reboot.", " /plan update cancel - Cancel scheduled update on servers that haven't rebooted yet." ); } @@ -62,24 +67,40 @@ public class UpdateCommand extends CommandNode { } if (args.length == 0) { - sender.sendLink("Change Log v" + available.getVersion().toString() + ": ", "Click me", available.getChangeLogUrl()); + String message = "Change Log v" + available.getVersion().toString() + ": "; + String url = available.getChangeLogUrl(); + if (CommandUtils.isConsole(sender)) { + sender.sendMessage(message + url); + } else { + sender.sendMessage(message); + sender.sendLink(" ", Locale.get(Msg.CMD_INFO_CLICK_ME).toString(), url); + } return; } String firstArgument = args[0]; - if ("-update".equals(firstArgument)) { - handleUpdate(sender, args); - } else if ("cancel".equals(firstArgument)) { - cancel(sender); - } else { - throw new IllegalArgumentException("Unknown argument, use '-update' or 'cancel'"); - } + RunnableFactory.createNew("Update Command Task", new AbsRunnable() { + @Override + public void run() { + try { + if ("-u".equals(firstArgument)) { + handleUpdate(sender, args); + } else if ("cancel".equals(firstArgument)) { + handleCancel(sender); + } else { + throw new IllegalArgumentException("Unknown argument, use '-u' or 'cancel'"); + } + } finally { + cancel(); + } + } + }).runTaskAsynchronously(); } - private void cancel(ISender sender) { + private void handleCancel(ISender sender) { try { cancel(sender, Database.getActive().fetch().getServers()); - sender.sendMessage("§aUpdate has been cancelled."); + sender.sendMessage("§aCancel operation performed."); } catch (DBException e) { sender.sendMessage("§cDatabase error occurred, cancel could not be performed."); Log.toLog(this.getClass().getName(), e); @@ -90,24 +111,28 @@ public class UpdateCommand extends CommandNode { sender.sendMessage("§aYou can cancel the update on servers that haven't rebooted yet with /plan update cancel."); sender.sendMessage("Checking that all servers are online.."); if (!checkNetworkStatus(sender)) { - sender.sendMessage("§cNot all servers were online or accessible, you can still update available servers using -force as a 2nd argument."); - if (args.length <= 1 || !"-force".equals(args[1])) { + sender.sendMessage("§cNot all servers were online or accessible, you can still update available servers using /plan -update -force"); + if (args.length > 1 && "-force".equals(args[1])) { return; } } try { List servers = Database.getActive().fetch().getServers(); - update(sender, servers); + update(sender, servers, args); } catch (DBException e) { Log.toLog(this.getClass().getName(), e); } } - private void update(ISender sender, List servers) { + private void update(ISender sender, List servers, String[] args) { for (Server server : servers) { if (update(sender, server)) { sender.sendMessage("§a" + server.getName() + " scheduled for update."); } else { + if (args.length > 1 && "-force".equals(args[1])) { + sender.sendMessage("§e" + server.getName() + " failed to update, -force specified, continuing update."); + continue; + } sender.sendMessage("§cUpdate failed on a server, cancelling update on all servers.."); cancel(sender, servers); sender.sendMessage("§cUpdate cancelled."); @@ -149,7 +174,7 @@ public class UpdateCommand extends CommandNode { private boolean update(ISender sender, Server server) { try { - InfoSystem.getInstance().getConnectionSystem().sendInfoRequest(new CheckConnectionRequest(), server); + InfoSystem.getInstance().getConnectionSystem().sendInfoRequest(new UpdateRequest(), server); return true; } catch (BadRequestException e) { sender.sendMessage("§c" + server.getName() + " has Allow-Update set to false, aborting update."); @@ -186,6 +211,7 @@ public class UpdateCommand extends CommandNode { FetchOperations fetch = Database.getActive().fetch(); Optional bungeeInformation = fetch.getBungeeInformation(); if (!bungeeInformation.isPresent()) { + sender.sendMessage("Bungee address not found in the database, assuming this is not a network."); return true; } Map bukkitServers = fetch.getBukkitServers(); diff --git a/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java index d2a42788e..516dd7b02 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/PlanSystem.java @@ -81,9 +81,9 @@ public abstract class PlanSystem implements SubSystem { checkSubSystemInitialization(); SubSystem[] systems = new SubSystem[]{ - versionCheckSystem, fileSystem, configSystem, + versionCheckSystem, databaseSystem, webServerSystem, processing, diff --git a/Plan/src/main/java/com/djrapitops/plan/system/update/ShutdownUpdateHook.java b/Plan/src/main/java/com/djrapitops/plan/system/update/ShutdownUpdateHook.java index 99f5b1ee6..0e64ab73d 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/update/ShutdownUpdateHook.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/update/ShutdownUpdateHook.java @@ -5,12 +5,10 @@ import com.djrapitops.plugin.api.Check; import com.djrapitops.plugin.api.utility.Version; import com.djrapitops.plugin.api.utility.log.Log; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; +import java.util.HashSet; +import java.util.Set; /** * Shutdown hook that updates the plugin on server shutdown. @@ -22,64 +20,59 @@ import java.nio.channels.ReadableByteChannel; public class ShutdownUpdateHook extends Thread { private static boolean activated = false; + private static File newJar; + + private static Set toDelete = new HashSet<>(); private static boolean isActivated() { return activated; } - private static void activate(ShutdownUpdateHook hook) { + public static void activate() { activated = true; - Runtime.getRuntime().addShutdownHook(hook); - } - - public static void deActivate() { - activated = false; - Log.infoColor("§aUpdate has been cancelled."); - } - - public void register() { - if (isActivated()) { - return; - } - Log.infoColor("§aUpdate has been scheduled, The new jar will be downloaded on server shutdown."); - activate(this); - } - - @Override - public void run() { - if (!activated) { - return; - } - activated = false; VersionInfo available = VersionCheckSystem.getInstance().getNewVersionAvailable(); if (!Version.isNewVersionAvailable(new Version(VersionCheckSystem.getCurrentVersion()), available.getVersion())) { return; } - - File dataFolder = PlanPlugin.getInstance().getDataFolder(); - File pluginsFolder = Check.isSpongeAvailable() - ? dataFolder.getParentFile() - : new File(dataFolder.getParentFile().getParentFile(), "mods"); - if (pluginsFolder == null || !pluginsFolder.isDirectory()) { - System.out.println("Could not get plugin folder for Plan."); - return; - } - File newFileLocation = new File(pluginsFolder, "Plan-" + available.getVersion() + ".jar"); - try { - downloadNewJar(available, newFileLocation); - deleteOldJar(pluginsFolder, newFileLocation); + File pluginsFolder = getPluginsFolder(); + newJar = new File(pluginsFolder, "Plan-" + available.getVersion() + ".jar"); + + downloadNewJar(available, newJar); + registerOldJarForDeletion(pluginsFolder, newJar); } catch (IOException e) { - Log.toLog(this.getClass().getName(), e); + e.printStackTrace(); } } - private void deleteOldJar(File pluginsFolder, File newFileLocation) { + private static File getPluginsFolder() throws FileNotFoundException { + File dataFolder = PlanPlugin.getInstance().getDataFolder(); + File pluginsFolder = Check.isSpongeAvailable() + ? new File(dataFolder.getParentFile().getParentFile().getPath(), "mods") + : new File(dataFolder.getParentFile().getPath()); + if (!pluginsFolder.isDirectory()) { + throw new FileNotFoundException("Could not get plugin folder for Plan."); + } + return pluginsFolder; + } + + public static void deActivate() { + activated = false; + Log.infoColor("§aUpdate has been cancelled."); + + if (newJar != null && newJar.exists()) { + if (!newJar.delete()) { + newJar.deleteOnExit(); + } + } + toDelete.clear(); + } + + public static void registerOldJarForDeletion(File pluginsFolder, File newFileLocation) throws FileNotFoundException { File[] files = pluginsFolder.listFiles(); if (files == null) { - System.out.println("Could not delete old jar."); - return; + throw new FileNotFoundException("Could not delete old jar."); } for (File file : files) { String fileName = file.getName(); @@ -88,17 +81,51 @@ public class ShutdownUpdateHook extends Thread { || fileName.equals("Plan.jar"); boolean isNewJar = fileName.equals(newFileLocation.getName()); if (isPlanJar && !isNewJar) { - file.deleteOnExit(); + toDelete.add(file); } } } - private void downloadNewJar(VersionInfo available, File newFileLocation) throws IOException { + public static void downloadNewJar(VersionInfo available, File newFileLocation) throws IOException { URL downloadFrom = new URL(available.getDownloadUrl()); + BufferedInputStream in = null; + FileOutputStream fout = null; + try { + in = new BufferedInputStream(downloadFrom.openStream()); + fout = new FileOutputStream(newFileLocation); - ReadableByteChannel rbc = Channels.newChannel(downloadFrom.openStream()); - FileOutputStream fos = new FileOutputStream(newFileLocation); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + final byte data[] = new byte[1024]; + int count; + while ((count = in.read(data, 0, 1024)) != -1) { + fout.write(data, 0, count); + } + } finally { + if (in != null) { + in.close(); + } + if (fout != null) { + fout.close(); + } + } + } + + public void register() { + if (isActivated()) { + return; + } + Log.infoColor("§aUpdate has been scheduled, Downloading new jar.. Restart server to take effect."); + activate(); + Runtime.getRuntime().addShutdownHook(this); + } + + @Override + public void run() { + for (File f : toDelete + ) { + if (!f.delete()) { + f.deleteOnExit(); + } + } } } \ No newline at end of file diff --git a/Plan/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java b/Plan/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java index fe1cd2e8d..b52f9f21c 100644 --- a/Plan/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java +++ b/Plan/src/main/java/com/djrapitops/plan/system/update/VersionCheckSystem.java @@ -55,10 +55,13 @@ public class VersionCheckSystem implements SubSystem { } VersionInfo newestVersion = versions.get(0); if (Version.isNewVersionAvailable(new Version(currentVersion), newestVersion.getVersion())) { + newVersionAvailable = newestVersion; String notification = "New Release (" + newestVersion.getVersion().toString() + ") is available and can be updated " + "to using update subcommand." + (newestVersion.isRelease() ? "" : " This is a DEV release."); - Log.info(notification); + Log.infoColor("§a----------------------------------------"); + Log.infoColor("§a" + notification); + Log.infoColor("§a----------------------------------------"); NotificationCenter.addNotification(newestVersion.isRelease() ? Priority.HIGH : Priority.MEDIUM, notification); } else { Log.info("You're using the latest version."); diff --git a/Plan/src/test/java/com/djrapitops/plan/system/update/ShutdownUpdateHookTest.java b/Plan/src/test/java/com/djrapitops/plan/system/update/ShutdownUpdateHookTest.java new file mode 100644 index 000000000..f162ecd6c --- /dev/null +++ b/Plan/src/test/java/com/djrapitops/plan/system/update/ShutdownUpdateHookTest.java @@ -0,0 +1,41 @@ +package com.djrapitops.plan.system.update; + +import com.djrapitops.plugin.api.utility.Version; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * //TODO Class Javadoc Comment + * + * @author Rsl1122 + */ +public class ShutdownUpdateHookTest { + + @ClassRule + public static TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void downloadNewJar() throws IOException { + File newJar = new File(temporaryFolder.getRoot(), "Plan-4.2.0.jar"); + ShutdownUpdateHook.downloadNewJar(new VersionInfo( + true, + new Version("4.2.0"), + "https://github.com/Rsl1122/Plan-PlayerAnalytics/releases/download/4.2.0/Plan-4.2.0.jar", + "" + ), newJar + ); + + assertTrue(newJar.exists()); + + ShutdownUpdateHook.registerOldJarForDeletion(temporaryFolder.getRoot(), new File(temporaryFolder.getRoot(), "Plan-4.2.0-b1.jar")); + + assertFalse(newJar.exists()); + } +} \ No newline at end of file