From 6ca400777e801ad1b4e26460b58950dc04fb39c4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 21 Aug 2015 22:27:43 +0200 Subject: [PATCH] Add command that reloads a plugin --- .../java/net/pterodactylus/fcp/ReloadPlugin.java | 31 ++++++++ .../fcp/quelaton/DefaultFcpClient.java | 5 ++ .../net/pterodactylus/fcp/quelaton/FcpClient.java | 1 + .../fcp/quelaton/ReloadPluginCommand.java | 16 +++++ .../fcp/quelaton/ReloadPluginCommandImpl.java | 83 ++++++++++++++++++++++ .../fcp/quelaton/DefaultFcpClientTest.java | 53 ++++++++++++++ 6 files changed, 189 insertions(+) create mode 100644 src/main/java/net/pterodactylus/fcp/ReloadPlugin.java create mode 100644 src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommand.java create mode 100644 src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommandImpl.java diff --git a/src/main/java/net/pterodactylus/fcp/ReloadPlugin.java b/src/main/java/net/pterodactylus/fcp/ReloadPlugin.java new file mode 100644 index 0000000..bcdcde9 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/ReloadPlugin.java @@ -0,0 +1,31 @@ +package net.pterodactylus.fcp; + +/** + * The “ReloadPlugin” message is used to reload a plugin. + * + * @author David ‘Bombe’ Roden + */ +public class ReloadPlugin extends FcpMessage { + + public ReloadPlugin(String identifier) { + super("ReloadPlugin"); + setField("Identifier", identifier); + } + + public void setPluginName(String pluginName) { + setField("PluginName", pluginName); + } + + public void setMaxWaitTime(int maxWaitTime) { + setField("MaxWaitTime", String.valueOf(maxWaitTime)); + } + + public void setPurge(boolean purge) { + setField("Purge", String.valueOf(purge)); + } + + public void setStore(boolean store) { + setField("Store", String.valueOf(store)); + } + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java index be26779..2f0cc35 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java @@ -119,5 +119,10 @@ public class DefaultFcpClient implements FcpClient { return new LoadPluginCommandImpl(threadPool, this::connect); } + @Override + public ReloadPluginCommand reloadPlugin() { + return new ReloadPluginCommandImpl(threadPool, this::connect); + } + } diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java b/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java index 5b3b2b1..0091866 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java @@ -25,5 +25,6 @@ public interface FcpClient { ModifyPeerNoteCommand modifyPeerNote(); LoadPluginCommand loadPlugin(); + ReloadPluginCommand reloadPlugin(); } diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommand.java b/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommand.java new file mode 100644 index 0000000..ef25380 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommand.java @@ -0,0 +1,16 @@ +package net.pterodactylus.fcp.quelaton; + +import java.util.Optional; + +import net.pterodactylus.fcp.PluginInfo; + +/** + * Reloads a plugin. + * + * @author David ‘Bombe’ Roden + */ +public interface ReloadPluginCommand { + + Executable> plugin(String pluginClassName); + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommandImpl.java b/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommandImpl.java new file mode 100644 index 0000000..c1a33fe --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/ReloadPluginCommandImpl.java @@ -0,0 +1,83 @@ +package net.pterodactylus.fcp.quelaton; + +import java.io.IOException; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import net.pterodactylus.fcp.PluginInfo; +import net.pterodactylus.fcp.ProtocolError; +import net.pterodactylus.fcp.ReloadPlugin; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +/** + * Default {@link ReloadPluginCommand} implementation based on {@link FcpDialog}. + * + * @author David ‘Bombe’ Roden + */ +public class ReloadPluginCommandImpl implements ReloadPluginCommand { + + private static final RandomIdentifierGenerator IDENTIFIER = new RandomIdentifierGenerator(); + private final ListeningExecutorService threadPool; + private final ConnectionSupplier connectionSupplier; + private final ReloadPlugin reloadPlugin = new ReloadPlugin(IDENTIFIER.generate()); + + public ReloadPluginCommandImpl(ExecutorService threadPool, ConnectionSupplier connectionSupplier) { + this.threadPool = MoreExecutors.listeningDecorator(threadPool); + this.connectionSupplier = connectionSupplier; + } + + @Override + public Executable> plugin(String pluginClassName) { + reloadPlugin.setPluginName(pluginClassName); + return this::execute; + } + + private ListenableFuture> execute() { + return threadPool.submit(this::executeDialog); + } + + private Optional executeDialog() throws IOException, ExecutionException, InterruptedException { + try (ReloadPluginDialog loadPluginDialog = new ReloadPluginDialog()) { + return loadPluginDialog.send(reloadPlugin).get(); + } + } + + private class ReloadPluginDialog extends FcpDialog> { + + private final AtomicBoolean finished = new AtomicBoolean(); + private final AtomicReference pluginInfo = new AtomicReference<>(); + + public ReloadPluginDialog() throws IOException { + super(threadPool, connectionSupplier.get()); + } + + @Override + protected boolean isFinished() { + return finished.get(); + } + + @Override + protected Optional getResult() { + return Optional.ofNullable(pluginInfo.get()); + } + + @Override + protected void consumePluginInfo(PluginInfo pluginInfo) { + this.pluginInfo.set(pluginInfo); + finished.set(true); + } + + @Override + protected void consumeProtocolError(ProtocolError protocolError) { + finished.set(true); + } + + } + +} diff --git a/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java b/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java index c555f20..4fcd3fe 100644 --- a/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java +++ b/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java @@ -2136,4 +2136,57 @@ public class DefaultFcpClientTest { } + public class ReloadPlugin { + + private static final String CLASS_NAME = "foo.plugin.Plugin"; + private List lines; + private String identifier; + + private void connectAndAssert(Supplier>> requestMatcher) + throws InterruptedException, ExecutionException, IOException { + connectNode(); + lines = fcpServer.collectUntil(is("EndMessage")); + identifier = extractIdentifier(lines); + assertThat(lines, requestMatcher.get()); + } + + @Test + public void reloadingPluginWorks() throws InterruptedException, ExecutionException, IOException { + Future> pluginInfo = fcpClient.reloadPlugin().plugin(CLASS_NAME).execute(); + connectAndAssert(() -> matchesFcpMessage( + "ReloadPlugin", + "Identifier=" + identifier, + "PluginName=" + CLASS_NAME, + "EndMessage" + )); + replyWithPluginInfo(); + verifyPluginInfo(pluginInfo); + } + + private void replyWithPluginInfo() throws IOException { + fcpServer.writeLine( + "PluginInfo", + "Identifier=" + identifier, + "PluginName=superPlugin", + "IsTalkable=true", + "LongVersion=1.2.3", + "Version=42", + "OriginUri=superPlugin", + "Started=true", + "EndMessage" + ); + } + + private void verifyPluginInfo(Future> pluginInfo) + throws InterruptedException, ExecutionException { + assertThat(pluginInfo.get().get().getPluginName(), is("superPlugin")); + assertThat(pluginInfo.get().get().getOriginalURI(), is("superPlugin")); + assertThat(pluginInfo.get().get().isTalkable(), is(true)); + assertThat(pluginInfo.get().get().getVersion(), is("42")); + assertThat(pluginInfo.get().get().getLongVersion(), is("1.2.3")); + assertThat(pluginInfo.get().get().isStarted(), is(true)); + } + + } + } -- 2.7.4