Add command that removes a plugin
authorDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Fri, 21 Aug 2015 21:51:01 +0000 (23:51 +0200)
committerDavid ‘Bombe’ Roden <bombe@freenetproject.org>
Fri, 21 Aug 2015 21:51:01 +0000 (23:51 +0200)
12 files changed:
src/main/java/net/pterodactylus/fcp/FcpAdapter.java
src/main/java/net/pterodactylus/fcp/FcpConnection.java
src/main/java/net/pterodactylus/fcp/FcpListener.java
src/main/java/net/pterodactylus/fcp/FcpListenerManager.java
src/main/java/net/pterodactylus/fcp/PluginRemoved.java [new file with mode: 0644]
src/main/java/net/pterodactylus/fcp/RemovePlugin.java [new file with mode: 0644]
src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java
src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java
src/main/java/net/pterodactylus/fcp/quelaton/FcpDialog.java
src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommand.java [new file with mode: 0644]
src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommandImpl.java [new file with mode: 0644]
src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java

index 063d363..fc2b578 100644 (file)
@@ -273,6 +273,14 @@ public class FcpAdapter implements FcpListener {
         * {@inheritDoc}
         */
        @Override
+       public void receivedPluginRemoved(FcpConnection fcpConnection, PluginRemoved pluginRemoved) {
+               /* empty. */
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
        public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
                /* empty. */
        }
index cc13d4a..ac4d17b 100644 (file)
@@ -301,6 +301,8 @@ public class FcpConnection implements Closeable {
                        }
                } else if ("PluginInfo".equals(messageName)) {
                        fcpListenerManager.fireReceivedPluginInfo(new PluginInfo(fcpMessage));
+               } else if ("PluginRemoved".equals(messageName)) {
+                       fcpListenerManager.fireReceivedPluginRemoved(new PluginRemoved(fcpMessage));
                } else if ("NodeData".equals(messageName)) {
                        fcpListenerManager.fireReceivedNodeData(new NodeData(fcpMessage));
                } else if ("TestDDAReply".equals(messageName)) {
index 02eeed5..91e1f9c 100644 (file)
@@ -329,6 +329,8 @@ public interface FcpListener extends EventListener {
         */
        public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo);
 
+       void receivedPluginRemoved(FcpConnection fcpConnection, PluginRemoved pluginRemoved);
+
        /**
         * Notifies a listener that an “FCPPluginReply“ message was received.
         *
index 7969da1..a5c27af 100644 (file)
@@ -466,6 +466,12 @@ public class FcpListenerManager {
                }
        }
 
+       public void fireReceivedPluginRemoved(PluginRemoved pluginRemoved) {
+               for (FcpListener fcpListener : getListeners()) {
+                       fcpListener.receivedPluginRemoved(getSource(), pluginRemoved);
+               }
+       }
+
        /**
         * Notifies all listeners that an “FCPPluginReply” message was received.
         *
diff --git a/src/main/java/net/pterodactylus/fcp/PluginRemoved.java b/src/main/java/net/pterodactylus/fcp/PluginRemoved.java
new file mode 100644 (file)
index 0000000..7492954
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * jFCPlib - PluginInfo.java - Copyright © 2008 David Roden
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.fcp;
+
+/**
+ * The “PluginRemoved” message is a reply to the {@link RemovePlugin} request.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ */
+public class PluginRemoved extends BaseMessage implements Identifiable {
+
+       /**
+        * Creates a new “PluginRemoved” message that wraps the received message.
+        *
+        * @param receivedMessage
+        *      The received message
+        */
+       public PluginRemoved(FcpMessage receivedMessage) {
+               super(receivedMessage);
+       }
+
+       /**
+        * Returns the name of the plugin.
+        *
+        * @return The name of the plugin
+        */
+       public String getPluginName() {
+               return getField("PluginName");
+       }
+
+       /**
+        * Returns the identifier of the request.
+        *
+        * @return The identifier of the request
+        */
+       @Override
+       public String getIdentifier() {
+               return getField("Identifier");
+       }
+
+}
diff --git a/src/main/java/net/pterodactylus/fcp/RemovePlugin.java b/src/main/java/net/pterodactylus/fcp/RemovePlugin.java
new file mode 100644 (file)
index 0000000..44fa9a7
--- /dev/null
@@ -0,0 +1,27 @@
+package net.pterodactylus.fcp;
+
+/**
+ * The “RemovePlugin” message is used to remove a plugin.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class RemovePlugin extends FcpMessage {
+
+       public RemovePlugin(String identifier) {
+               super("RemovePlugin");
+               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));
+       }
+
+}
index 2f0cc35..3f485c0 100644 (file)
@@ -124,5 +124,10 @@ public class DefaultFcpClient implements FcpClient {
                return new ReloadPluginCommandImpl(threadPool, this::connect);
        }
 
+       @Override
+       public RemovePluginCommand removePlugin() {
+               return new RemovePluginCommandImpl(threadPool, this::connect);
+       }
+
 }
 
index 0091866..67c11ee 100644 (file)
@@ -26,5 +26,6 @@ public interface FcpClient {
 
        LoadPluginCommand loadPlugin();
        ReloadPluginCommand reloadPlugin();
+       RemovePluginCommand removePlugin();
 
 }
index 47319f0..5be102c 100644 (file)
@@ -36,6 +36,7 @@ import net.pterodactylus.fcp.PersistentPutDir;
 import net.pterodactylus.fcp.PersistentRequestModified;
 import net.pterodactylus.fcp.PersistentRequestRemoved;
 import net.pterodactylus.fcp.PluginInfo;
+import net.pterodactylus.fcp.PluginRemoved;
 import net.pterodactylus.fcp.ProtocolError;
 import net.pterodactylus.fcp.PutFailed;
 import net.pterodactylus.fcp.PutFetchable;
@@ -369,6 +370,13 @@ public abstract class FcpDialog<R> implements AutoCloseable, FcpListener {
        protected void consumePluginInfo(PluginInfo pluginInfo) { }
 
        @Override
+       public final void receivedPluginRemoved(FcpConnection fcpConnection, PluginRemoved pluginRemoved) {
+               consume(this::consumePluginRemoved, pluginRemoved);
+       }
+
+       protected void consumePluginRemoved(PluginRemoved pluginRemoved) { }
+
+       @Override
        public final void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
                consume(this::consumeFCPPluginReply, fcpPluginReply);
        }
diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommand.java b/src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommand.java
new file mode 100644 (file)
index 0000000..c221592
--- /dev/null
@@ -0,0 +1,12 @@
+package net.pterodactylus.fcp.quelaton;
+
+/**
+ * Removes a plugin.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public interface RemovePluginCommand {
+
+       Executable<Boolean> plugin(String pluginClass);
+
+}
diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommandImpl.java b/src/main/java/net/pterodactylus/fcp/quelaton/RemovePluginCommandImpl.java
new file mode 100644 (file)
index 0000000..b4a6b79
--- /dev/null
@@ -0,0 +1,81 @@
+package net.pterodactylus.fcp.quelaton;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import net.pterodactylus.fcp.PluginRemoved;
+import net.pterodactylus.fcp.ProtocolError;
+import net.pterodactylus.fcp.RemovePlugin;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * Default {@link RemovePluginCommand} implementation based on {@link FcpDialog}.
+ *
+ * @author <a href="mailto:bombe@freenetproject.org">David ‘Bombe’ Roden</a>
+ */
+public class RemovePluginCommandImpl implements RemovePluginCommand {
+
+       private static final RandomIdentifierGenerator IDENTIFIER = new RandomIdentifierGenerator();
+       private final ListeningExecutorService threadPool;
+       private final ConnectionSupplier connectionSupplier;
+       private final RemovePlugin removePlugin = new RemovePlugin(IDENTIFIER.generate());
+
+       public RemovePluginCommandImpl(ExecutorService threadPool, ConnectionSupplier connectionSupplier) {
+               this.threadPool = MoreExecutors.listeningDecorator(threadPool);
+               this.connectionSupplier = connectionSupplier;
+       }
+
+       @Override
+       public Executable<Boolean> plugin(String pluginClass) {
+               removePlugin.setPluginName(pluginClass);
+               return this::execute;
+       }
+
+       private ListenableFuture<Boolean> execute() {
+               return threadPool.submit(this::executeDialog);
+       }
+
+       private boolean executeDialog() throws IOException, ExecutionException, InterruptedException {
+               try (RemovePluginDialog removePluginDialog = new RemovePluginDialog()) {
+                       return removePluginDialog.send(removePlugin).get();
+               }
+       }
+
+       private class RemovePluginDialog extends FcpDialog<Boolean> {
+
+               private final AtomicBoolean finished = new AtomicBoolean();
+               private final AtomicBoolean pluginRemoved = new AtomicBoolean();
+
+               public RemovePluginDialog() throws IOException {
+                       super(threadPool, connectionSupplier.get());
+               }
+
+               @Override
+               protected boolean isFinished() {
+                       return finished.get();
+               }
+
+               @Override
+               protected Boolean getResult() {
+                       return pluginRemoved.get();
+               }
+
+               @Override
+               protected void consumePluginRemoved(PluginRemoved pluginRemoved) {
+                       this.pluginRemoved.set(true);
+                       finished.set(true);
+               }
+
+               @Override
+               protected void consumeProtocolError(ProtocolError protocolError) {
+                       finished.set(true);
+               }
+
+       }
+
+}
index 6cc61d6..fafee42 100644 (file)
@@ -1995,6 +1995,8 @@ public class DefaultFcpClientTest {
 
        public class PluginCommands {
 
+               private static final String CLASS_NAME = "foo.plugin.Plugin";
+
                private List<String> lines;
                private String identifier;
 
@@ -2141,8 +2143,6 @@ public class DefaultFcpClientTest {
 
                public class ReloadPlugin {
 
-                       private static final String CLASS_NAME = "foo.plugin.Plugin";
-
                        @Test
                        public void reloadingPluginWorks() throws InterruptedException, ExecutionException, IOException {
                                Future<Optional<PluginInfo>> pluginInfo = fcpClient.reloadPlugin().plugin(CLASS_NAME).execute();
@@ -2192,6 +2192,36 @@ public class DefaultFcpClientTest {
 
                }
 
+               public class RemovePlugin {
+
+                       @Test
+                       public void removingPluginWorks() throws InterruptedException, ExecutionException, IOException {
+                               Future<Boolean> pluginRemoved = fcpClient.removePlugin().plugin(CLASS_NAME).execute();
+                               connectAndAssert(() -> matchPluginRemovedMessage());
+                               replyWithPluginRemoved();
+                               assertThat(pluginRemoved.get(), is(true));
+                       }
+
+                       private void replyWithPluginRemoved() throws IOException {
+                               fcpServer.writeLine(
+                                       "PluginRemoved",
+                                       "Identifier=" + identifier,
+                                       "PluginName=" + CLASS_NAME,
+                                       "EndMessage"
+                               );
+                       }
+
+                       private Matcher<List<String>> matchPluginRemovedMessage() {
+                               return matchesFcpMessage(
+                                       "RemovePlugin",
+                                       "Identifier=" + identifier,
+                                       "PluginName=" + CLASS_NAME,
+                                       "EndMessage"
+                               );
+                       }
+
+               }
+
        }
 
 }