From 9433acf6896eeea49e4c72a4acdf11cc619f8d44 Mon Sep 17 00:00:00 2001 From: snowleo Date: Sun, 23 Oct 2011 21:45:01 +0200 Subject: Statemachine for installation wizard (WIP) --- EssentialsUpdate/nbproject/pmd.settings | 1 + .../com/earth2me/essentials/update/GetFile.java | 6 +- .../earth2me/essentials/update/UpdateProcess.java | 62 +++++++---- .../essentials/update/UpdatesDownloader.java | 23 ++-- .../earth2me/essentials/update/VersionInfo.java | 13 +-- .../earth2me/essentials/update/WorkListener.java | 38 +++++++ .../essentials/update/states/AbstractState.java | 67 ++++++++++++ .../update/states/AbstractYesNoState.java | 49 +++++++++ .../essentials/update/states/EssentialsChat.java | 46 ++++++++ .../earth2me/essentials/update/states/Modules.java | 7 -- .../essentials/update/states/StateMachine.java | 118 +++++++++++++++++++++ .../essentials/update/tasks/InstallChat.java | 12 +++ .../essentials/update/tasks/InstallModule.java | 61 +++++++++++ .../com/earth2me/essentials/update/tasks/Task.java | 7 ++ 14 files changed, 470 insertions(+), 40 deletions(-) create mode 100644 EssentialsUpdate/nbproject/pmd.settings create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/WorkListener.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractState.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractYesNoState.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/EssentialsChat.java delete mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/states/StateMachine.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallChat.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallModule.java create mode 100644 EssentialsUpdate/src/com/earth2me/essentials/update/tasks/Task.java diff --git a/EssentialsUpdate/nbproject/pmd.settings b/EssentialsUpdate/nbproject/pmd.settings new file mode 100644 index 000000000..6a34e356c --- /dev/null +++ b/EssentialsUpdate/nbproject/pmd.settings @@ -0,0 +1 @@ +DoNotUseThreads diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java index 888950f34..8727d2f3b 100644 --- a/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/GetFile.java @@ -22,7 +22,11 @@ public class GetFile public GetFile(final String urlString) throws MalformedURLException, IOException { - final URL url = new URL(urlString); + this(new URL(urlString)); + } + + public GetFile(final URL url) throws IOException + { this.connection = url.openConnection(); this.connection.setConnectTimeout(1000); this.connection.setReadTimeout(5000); diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java index 95898bcb6..80434480f 100644 --- a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdateProcess.java @@ -1,5 +1,6 @@ package com.earth2me.essentials.update; +import com.earth2me.essentials.update.states.StateMachine; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; @@ -18,31 +19,41 @@ public class UpdateProcess extends PlayerListener private transient Player currentPlayer; private final transient Plugin plugin; private final transient UpdateCheck updateCheck; - + private transient StateMachine stateMachine; + public UpdateProcess(final Plugin plugin, final UpdateCheck updateCheck) { this.plugin = plugin; this.updateCheck = updateCheck; } - + public void registerEvents() { final PluginManager pluginManager = plugin.getServer().getPluginManager(); pluginManager.registerEvent(Type.PLAYER_QUIT, this, Priority.Low, plugin); pluginManager.registerEvent(Type.PLAYER_CHAT, this, Priority.Lowest, plugin); } - + @Override public void onPlayerChat(final PlayerChatEvent event) { if (event.getPlayer() == currentPlayer) { - reactOnMessage(event.getMessage()); + final StateMachine.MachineResult result = stateMachine.reactOnMessage(event.getMessage()); + if (result == StateMachine.MachineResult.ABORT) + { + currentPlayer.sendMessage("Installation wizard aborted. You can restart it using /essentialsupdate."); + currentPlayer = null; + } + if (result == StateMachine.MachineResult.DONE) + { + startWork(); + } event.setCancelled(true); return; } } - + @Override public void onPlayerJoin(final PlayerJoinEvent event) { @@ -70,26 +81,26 @@ public class UpdateProcess extends PlayerListener } } } - - void doAutomaticUpdate() + + public void doAutomaticUpdate() { - final UpdatesDownloader downloader = new UpdatesDownloader(); + final VersionInfo info = updateCheck.getNewVersionInfo(); final List changelog = info.getChangelog(); Bukkit.getLogger().info("Essentials changelog " + updateCheck.getNewVersion().toString()); for (String line : changelog) { - Bukkit.getLogger().info(" - "+line); + Bukkit.getLogger().info(" - " + line); } - downloader.start(plugin.getServer().getUpdateFolderFile(), info); + final UpdatesDownloader downloader = new UpdatesDownloader(plugin, info); + downloader.start(); } - - void doManualUpdate() + + public void doManualUpdate() { - } - - void onCommand(CommandSender sender) + + public void onCommand(final CommandSender sender) { if (sender instanceof Player && sender.hasPermission("essentials.install")) { @@ -107,7 +118,12 @@ public class UpdateProcess extends PlayerListener sender.sendMessage("Your answers will be saved for a later update."); sender.sendMessage("Please answer the messages with yes or no, if not otherwise stated."); sender.sendMessage("Write bye/exit/quit if you want to exit the wizard at anytime."); - + stateMachine = new StateMachine(plugin, currentPlayer, updateCheck.getNewVersionInfo()); + final StateMachine.MachineResult result = stateMachine.askQuestion(); + if (result == StateMachine.MachineResult.DONE) + { + startWork(); + } } } if (!currentPlayer.equals(sender)) @@ -120,9 +136,17 @@ public class UpdateProcess extends PlayerListener sender.sendMessage("Please run the command as op from in game."); } } - - private void reactOnMessage(String message) + + private void startWork() { - throw new UnsupportedOperationException("Not yet implemented"); + currentPlayer.sendMessage("Installation wizard done. Starting installation."); + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() + { + @Override + public void run() + { + stateMachine.startWork(); + } + }); } } diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java index 28ffdfe3c..717163726 100644 --- a/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/UpdatesDownloader.java @@ -1,19 +1,28 @@ package com.earth2me.essentials.update; -import java.io.File; +import org.bukkit.plugin.Plugin; -public class UpdatesDownloader +public class UpdatesDownloader extends WorkListener { + public UpdatesDownloader(final Plugin plugin, final VersionInfo newVersionInfo) + { + super(plugin, newVersionInfo); + } + + public void start() + { + } - UpdatesDownloader() + @Override + public void onWorkAbort(String message) { - + throw new UnsupportedOperationException("Not supported yet."); } - void start(File updateFolderFile, VersionInfo newVersion) + @Override + public void onWorkDone(String message) { - + throw new UnsupportedOperationException("Not supported yet."); } - } diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java index 9cd1e5edb..c34e40ae5 100644 --- a/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/VersionInfo.java @@ -1,9 +1,10 @@ package com.earth2me.essentials.update; -import java.util.ArrayList; import org.bukkit.configuration.Configuration; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class VersionInfo @@ -11,18 +12,18 @@ public class VersionInfo private final transient List changelog; private final transient int minBukkit; private final transient int maxBukkit; - private final transient List modules; + private final transient Map modules; public VersionInfo(final Configuration updateConfig, final String path) { changelog = updateConfig.getList(path + ".changelog", Collections.emptyList()); minBukkit = updateConfig.getInt(path + ".min-bukkit", 0); maxBukkit = updateConfig.getInt(path + ".max-bukkit", 0); - modules = new ArrayList(); + modules = new HashMap(); final String modulesPath = path + ".modules"; for (String module : updateConfig.getKeys(false)) { - modules.add(new ModuleInfo(updateConfig, modulesPath + module)); + modules.put(module, new ModuleInfo(updateConfig, modulesPath + module)); } } @@ -41,8 +42,8 @@ public class VersionInfo return maxBukkit; } - public List getModules() + public Map getModules() { - return Collections.unmodifiableList(modules); + return Collections.unmodifiableMap(modules); } } diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/WorkListener.java b/EssentialsUpdate/src/com/earth2me/essentials/update/WorkListener.java new file mode 100644 index 000000000..da6bdb978 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/WorkListener.java @@ -0,0 +1,38 @@ +package com.earth2me.essentials.update; + +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + + +public abstract class WorkListener +{ + public WorkListener(final Plugin plugin, final VersionInfo newVersionInfo) + { + this.plugin = plugin; + this.newVersionInfo = newVersionInfo; + } + private final transient Plugin plugin; + private final transient VersionInfo newVersionInfo; + + public final void onWorkAbort() { + onWorkAbort(null); + } + + public abstract void onWorkAbort(String message); + + public final void onWorkDone() { + onWorkDone(null); + } + + public abstract void onWorkDone(String message); + + public VersionInfo getNewVersionInfo() + { + return newVersionInfo; + } + + public Plugin getPlugin() + { + return plugin; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractState.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractState.java new file mode 100644 index 000000000..b217ebd4b --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractState.java @@ -0,0 +1,67 @@ +package com.earth2me.essentials.update.states; + +import com.earth2me.essentials.update.WorkListener; +import org.bukkit.entity.Player; + + +public abstract class AbstractState +{ + private transient boolean abortion = false; + + public abstract AbstractState getNextState(); + + /** + * Check if we already know the answer, so the user does not have to answer the question. + * + * @return true, if the answer could be guessed. + */ + public boolean guessAnswer() + { + return false; + } + + /** + * Ask the user the question. + * @param sender + */ + public abstract void askQuestion(Player sender); + + /** + * React on the answer and set internal variables + * @param answer + * @return true, if the answer could be recognized as a valid answer + */ + public abstract boolean reactOnAnswer(String answer); + + public final AbstractState reactOnAnswer(final Player sender, final String answer) + { + final String trimmedAnswer = answer.trim(); + if (trimmedAnswer.equalsIgnoreCase("quit") + || trimmedAnswer.equalsIgnoreCase("bye") + || trimmedAnswer.equalsIgnoreCase("abort")) + { + abortion = true; + return null; + } + final boolean found = reactOnAnswer(trimmedAnswer); + if (found) + { + return getNextState(); + } + else + { + sender.sendMessage("Answer not recognized."); + return this; + } + } + + /** + * Do something based on the answer, that the user gave. + */ + public abstract void doWork(WorkListener workListener); + + public boolean isAbortion() + { + return abortion; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractYesNoState.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractYesNoState.java new file mode 100644 index 000000000..67701c8db --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/AbstractYesNoState.java @@ -0,0 +1,49 @@ +package com.earth2me.essentials.update.states; + + +public abstract class AbstractYesNoState extends AbstractState +{ + private boolean answer = false; + private final transient AbstractState yesState; + private final transient AbstractState noState; + + public AbstractYesNoState(final AbstractState yesState, final AbstractState noState) + { + this.yesState = yesState; + this.noState = noState; + } + + @Override + public AbstractState getNextState() + { + return answer ? yesState : noState; + } + + @Override + public boolean reactOnAnswer(final String answer) + { + if (answer.equalsIgnoreCase("yes") + || answer.equalsIgnoreCase("y")) + { + this.answer = true; + return true; + } + if (answer.equalsIgnoreCase("no") + || answer.equalsIgnoreCase("n")) + { + this.answer = false; + return true; + } + return false; + } + + public boolean getAnswer() + { + return answer; + } + + protected void setAnswer(final boolean answer) + { + this.answer = answer; + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/EssentialsChat.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/EssentialsChat.java new file mode 100644 index 000000000..6669161d4 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/EssentialsChat.java @@ -0,0 +1,46 @@ +package com.earth2me.essentials.update.states; + +import com.earth2me.essentials.update.WorkListener; +import com.earth2me.essentials.update.tasks.InstallChat; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + + +public class EssentialsChat extends AbstractYesNoState +{ + public EssentialsChat(final AbstractState next) + { + super(next, next); + } + + @Override + public boolean guessAnswer() + { + final Plugin plugin = Bukkit.getPluginManager().getPlugin("EssentialsChat"); + if (plugin != null) + { + setAnswer(true); + return true; + } + return false; + } + + @Override + public void askQuestion(final Player sender) + { + sender.sendMessage("Do you want to install EssentialsChat? (yes/no)"); + sender.sendMessage("Short descriptive text about what EssentialsChat does."); + } + + @Override + public void doWork(final WorkListener listener) + { + if (getAnswer()) + { + new InstallChat(listener).start(); + return; + } + listener.onWorkDone(); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java deleted file mode 100644 index 56d2445c9..000000000 --- a/EssentialsUpdate/src/com/earth2me/essentials/update/states/Modules.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.earth2me.essentials.update.states; - - -public class Modules -{ - -} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/states/StateMachine.java b/EssentialsUpdate/src/com/earth2me/essentials/update/states/StateMachine.java new file mode 100644 index 000000000..b69ee14a9 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/states/StateMachine.java @@ -0,0 +1,118 @@ +package com.earth2me.essentials.update.states; + +import com.earth2me.essentials.update.WorkListener; +import com.earth2me.essentials.update.VersionInfo; +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + + +public class StateMachine extends WorkListener +{ + public enum MachineResult + { + ABORT, WAIT, DONE + } + private final transient List states = new ArrayList(); + private transient AbstractState current; + private final transient Player player; + + public StateMachine(final Plugin plugin, final Player player, final VersionInfo newVersionInfo) + { + super(plugin, newVersionInfo); + this.player = player; + states.clear(); + final AbstractState state = new EssentialsChat(null); + states.add(state); + current = state; + } + + public MachineResult askQuestion() + { + while (current.guessAnswer()) + { + current = current.getNextState(); + if (current == null) + { + return MachineResult.DONE; + } + } + current.askQuestion(player); + return MachineResult.WAIT; + } + + public MachineResult reactOnMessage(final String message) + { + final AbstractState next = current.reactOnAnswer(player, message); + if (next == null) + { + if (current.isAbortion()) + { + return MachineResult.ABORT; + } + else + { + return MachineResult.DONE; + } + } + current = next; + return askQuestion(); + } + private int position = 0; + + public void startWork() + { + callStateWork(); + } + + private void callStateWork() + { + if (position > states.size()) + { + if (player.isOnline()) + { + player.sendMessage("Installation done."); + } + return; + } + final AbstractState state = states.get(position); + state.doWork(this); + } + + @Override + public void onWorkAbort(final String message) + { + position = 0; + Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() + { + @Override + public void run() + { + if (message != null && !message.isEmpty() && StateMachine.this.player.isOnline()) + { + StateMachine.this.player.sendMessage(message); + } + } + }); + } + + @Override + public void onWorkDone(final String message) + { + position++; + Bukkit.getScheduler().scheduleSyncDelayedTask(getPlugin(), new Runnable() + { + @Override + public void run() + { + if (message != null && !message.isEmpty() && StateMachine.this.player.isOnline()) + { + StateMachine.this.player.sendMessage(message); + } + StateMachine.this.callStateWork(); + } + }); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallChat.java b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallChat.java new file mode 100644 index 000000000..44b4e245d --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallChat.java @@ -0,0 +1,12 @@ +package com.earth2me.essentials.update.tasks; + +import com.earth2me.essentials.update.WorkListener; + + +public class InstallChat extends InstallModule +{ + public InstallChat(final WorkListener listener) + { + super(listener, "EssentialsChat"); + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallModule.java b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallModule.java new file mode 100644 index 000000000..b97991a04 --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/InstallModule.java @@ -0,0 +1,61 @@ +package com.earth2me.essentials.update.tasks; + +import com.earth2me.essentials.update.GetFile; +import com.earth2me.essentials.update.ModuleInfo; +import com.earth2me.essentials.update.VersionInfo; +import com.earth2me.essentials.update.WorkListener; +import java.io.File; +import java.net.URL; +import java.util.logging.Level; +import org.bukkit.Bukkit; + + +public class InstallModule implements Runnable, Task +{ + protected final transient WorkListener listener; + private final transient String moduleName; + private final transient String fileName; + + public InstallModule(final WorkListener listener, final String moduleName) + { + this(listener, moduleName, moduleName + ".jar"); + } + + public InstallModule(final WorkListener listener, final String moduleName, final String fileName) + { + this.listener = listener; + this.moduleName = moduleName; + this.fileName = fileName; + } + + @Override + public void start() + { + Bukkit.getScheduler().scheduleAsyncDelayedTask(listener.getPlugin(), this); + } + + @Override + public void run() + { + final VersionInfo info = listener.getNewVersionInfo(); + final ModuleInfo module = info.getModules().get(moduleName); + if (module == null) + { + listener.onWorkAbort("Module " + moduleName + " not found in VersionInfo."); + return; + } + try + { + final URL downloadUrl = module.getUrl(); + final GetFile getFile = new GetFile(downloadUrl); + getFile.saveTo(new File(listener.getPlugin().getServer().getUpdateFolderFile(), fileName), module.getHash()); + listener.onWorkDone("Module " + moduleName + " downloaded."); + } + catch (Exception ex) + { + Bukkit.getLogger().log(Level.SEVERE, "Failed to download module " + moduleName + " to " + fileName, ex); + listener.onWorkAbort("An error occured, please check your server log."); + return; + } + } +} diff --git a/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/Task.java b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/Task.java new file mode 100644 index 000000000..3f1d572ed --- /dev/null +++ b/EssentialsUpdate/src/com/earth2me/essentials/update/tasks/Task.java @@ -0,0 +1,7 @@ +package com.earth2me.essentials.update.tasks; + + +public interface Task +{ + void start(); +} -- cgit v1.2.3