From cd753d3f782e0024fd3edaa97b40f8e86bc3a8b3 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 22 Aug 2015 11:44:14 +0200 Subject: [PATCH] Add command to subscribe to USK updates --- .../java/net/pterodactylus/fcp/FcpAdapter.java | 5 ++ .../java/net/pterodactylus/fcp/FcpConnection.java | 2 + .../java/net/pterodactylus/fcp/FcpListener.java | 2 + .../net/pterodactylus/fcp/FcpListenerManager.java | 6 ++ .../java/net/pterodactylus/fcp/SubscribeUSK.java | 20 ++--- .../java/net/pterodactylus/fcp/SubscribedUSK.java | 45 +++++++++++ .../fcp/quelaton/DefaultFcpClient.java | 5 ++ .../net/pterodactylus/fcp/quelaton/FcpClient.java | 2 + .../net/pterodactylus/fcp/quelaton/FcpDialog.java | 8 ++ .../fcp/quelaton/SubscribeUskCommand.java | 14 ++++ .../fcp/quelaton/SubscribeUskCommandImpl.java | 88 ++++++++++++++++++++++ .../fcp/quelaton/UskSubscription.java | 12 +++ .../fcp/quelaton/DefaultFcpClientTest.java | 42 ++++++++--- 13 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 src/main/java/net/pterodactylus/fcp/SubscribedUSK.java create mode 100644 src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommand.java create mode 100644 src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommandImpl.java create mode 100644 src/main/java/net/pterodactylus/fcp/quelaton/UskSubscription.java diff --git a/src/main/java/net/pterodactylus/fcp/FcpAdapter.java b/src/main/java/net/pterodactylus/fcp/FcpAdapter.java index fc2b578..06c3fa1 100644 --- a/src/main/java/net/pterodactylus/fcp/FcpAdapter.java +++ b/src/main/java/net/pterodactylus/fcp/FcpAdapter.java @@ -253,6 +253,11 @@ public class FcpAdapter implements FcpListener { /* empty. */ } + @Override + public void receivedSubscribedUSK(FcpConnection fcpConnection, SubscribedUSK subscribedUSK) { + /* empty. */ + } + /** * {@inheritDoc} */ diff --git a/src/main/java/net/pterodactylus/fcp/FcpConnection.java b/src/main/java/net/pterodactylus/fcp/FcpConnection.java index ac4d17b..26fa65f 100644 --- a/src/main/java/net/pterodactylus/fcp/FcpConnection.java +++ b/src/main/java/net/pterodactylus/fcp/FcpConnection.java @@ -265,6 +265,8 @@ public class FcpConnection implements Closeable { fcpListenerManager.fireReceivedDataFound(new DataFound(fcpMessage)); } else if ("SubscribedUSKUpdate".equals(messageName)) { fcpListenerManager.fireReceivedSubscribedUSKUpdate(new SubscribedUSKUpdate(fcpMessage)); + } else if ("SubscribedUSK".equals(messageName)) { + fcpListenerManager.fireReceivedSubscribedUSK(new SubscribedUSK(fcpMessage)); } else if ("IdentifierCollision".equals(messageName)) { fcpListenerManager.fireReceivedIdentifierCollision(new IdentifierCollision(fcpMessage)); } else if ("AllData".equals(messageName)) { diff --git a/src/main/java/net/pterodactylus/fcp/FcpListener.java b/src/main/java/net/pterodactylus/fcp/FcpListener.java index 91e1f9c..b96885f 100644 --- a/src/main/java/net/pterodactylus/fcp/FcpListener.java +++ b/src/main/java/net/pterodactylus/fcp/FcpListener.java @@ -309,6 +309,8 @@ public interface FcpListener extends EventListener { */ public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved); + void receivedSubscribedUSK(FcpConnection fcpConnection, SubscribedUSK subscribedUSK); + /** * Notifies a listener that a “SubscribedUSKUpdate” message was received. * diff --git a/src/main/java/net/pterodactylus/fcp/FcpListenerManager.java b/src/main/java/net/pterodactylus/fcp/FcpListenerManager.java index a5c27af..a25f3bf 100644 --- a/src/main/java/net/pterodactylus/fcp/FcpListenerManager.java +++ b/src/main/java/net/pterodactylus/fcp/FcpListenerManager.java @@ -438,6 +438,12 @@ public class FcpListenerManager { } } + public void fireReceivedSubscribedUSK(SubscribedUSK subscribedUSK) { + for (FcpListener fcpListener : getListeners()) { + fcpListener.receivedSubscribedUSK(getSource(), subscribedUSK); + } + } + /** * Notifies all listeners that a “SubscribedUSKUpdate” message was * received. diff --git a/src/main/java/net/pterodactylus/fcp/SubscribeUSK.java b/src/main/java/net/pterodactylus/fcp/SubscribeUSK.java index 6a18333..b0c5e7e 100644 --- a/src/main/java/net/pterodactylus/fcp/SubscribeUSK.java +++ b/src/main/java/net/pterodactylus/fcp/SubscribeUSK.java @@ -26,20 +26,20 @@ package net.pterodactylus.fcp; */ public class SubscribeUSK extends FcpMessage { - /** - * Creates a new “SubscribeUSK” message. - * - * @param uri - * The URI to watch for changes - * @param identifier - * The identifier of the request - */ - public SubscribeUSK(String uri, String identifier) { + public SubscribeUSK(String identifier) { super("SubscribeUSK"); - setField("URI", uri); setField("Identifier", identifier); } + public SubscribeUSK(String uri, String identifier) { + this(identifier); + setField("URI", uri); + } + + public void setUri(String uri) { + setField("URI", uri); + } + /** * Sets whether updates for the USK are actively searched. * diff --git a/src/main/java/net/pterodactylus/fcp/SubscribedUSK.java b/src/main/java/net/pterodactylus/fcp/SubscribedUSK.java new file mode 100644 index 0000000..f4c5d4d --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/SubscribedUSK.java @@ -0,0 +1,45 @@ +/* + * jFCPlib - SubscribedUSKUpdate.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; + +/** + * A “SubscribedUSK” message is sent when a {@link SubscribeUSK} was succesfully processed. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class SubscribedUSK extends BaseMessage implements Identifiable { + + public SubscribedUSK(FcpMessage receivedMessage) { + super(receivedMessage); + } + + @Override + public String getIdentifier() { + return getField("Identifier"); + } + + public String getURI() { + return getField("URI"); + } + + public boolean isDontPoll() { + return Boolean.parseBoolean(getField("DontPoll")); + } + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java index fe7300d..255549c 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java @@ -134,5 +134,10 @@ public class DefaultFcpClient implements FcpClient { return new GetPluginInfoCommandImpl(threadPool, this::connect); } + @Override + public SubscribeUskCommand subscribeUsk() { + return new SubscribeUskCommandImpl(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 b799f27..3a1043a 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java @@ -29,4 +29,6 @@ public interface FcpClient { RemovePluginCommand removePlugin(); GetPluginInfoCommand getPluginInfo(); + SubscribeUskCommand subscribeUsk(); + } diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/FcpDialog.java b/src/main/java/net/pterodactylus/fcp/quelaton/FcpDialog.java index 5be102c..f218610 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/FcpDialog.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/FcpDialog.java @@ -46,6 +46,7 @@ import net.pterodactylus.fcp.SSKKeypair; import net.pterodactylus.fcp.SentFeed; import net.pterodactylus.fcp.SimpleProgress; import net.pterodactylus.fcp.StartedCompression; +import net.pterodactylus.fcp.SubscribedUSK; import net.pterodactylus.fcp.SubscribedUSKUpdate; import net.pterodactylus.fcp.TestDDAComplete; import net.pterodactylus.fcp.TestDDAReply; @@ -356,6 +357,13 @@ public abstract class FcpDialog implements AutoCloseable, FcpListener { protected void consumePersistentRequestRemoved(PersistentRequestRemoved persistentRequestRemoved) { } @Override + public final void receivedSubscribedUSK(FcpConnection fcpConnection, SubscribedUSK subscribedUSK) { + consume(this::consumeSubscribedUSK, subscribedUSK); + } + + protected void consumeSubscribedUSK(SubscribedUSK subscribedUSK) { } + + @Override public final void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) { consume(this::consumeSubscribedUSKUpdate, subscribedUSKUpdate); } diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommand.java b/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommand.java new file mode 100644 index 0000000..25767f0 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommand.java @@ -0,0 +1,14 @@ +package net.pterodactylus.fcp.quelaton; + +import java.util.Optional; + +/** + * Subscribes to a USK. + * + * @author David ‘Bombe’ Roden + */ +public interface SubscribeUskCommand { + + Executable> uri(String uri); + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommandImpl.java b/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommandImpl.java new file mode 100644 index 0000000..cf9d10b --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/SubscribeUskCommandImpl.java @@ -0,0 +1,88 @@ +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.IdentifierCollision; +import net.pterodactylus.fcp.SubscribeUSK; +import net.pterodactylus.fcp.SubscribedUSK; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +/** + * Default {@link SubscribeUskCommand} implementation based on {@link FcpDialog}. + * + * @author David ‘Bombe’ Roden + */ +public class SubscribeUskCommandImpl implements SubscribeUskCommand { + + private static final RandomIdentifierGenerator IDENTIFIER = new RandomIdentifierGenerator(); + private final ListeningExecutorService threadPool; + private final ConnectionSupplier connectionSupplier; + private final SubscribeUSK subscribeUSK = new SubscribeUSK(IDENTIFIER.generate()); + + public SubscribeUskCommandImpl(ExecutorService threadPool, ConnectionSupplier connectionSupplier) { + this.threadPool = MoreExecutors.listeningDecorator(threadPool); + this.connectionSupplier = connectionSupplier; + } + + @Override + public Executable> uri(String uri) { + subscribeUSK.setUri(uri); + return this::execute; + } + + private ListenableFuture> execute() { + return threadPool.submit(this::executeDialog); + } + + private Optional executeDialog() throws IOException, ExecutionException, InterruptedException { + try (SubscribeUskDialog subscribeUskDialog = new SubscribeUskDialog()) { + return subscribeUskDialog.send(subscribeUSK).get(); + } + } + + private class SubscribeUskDialog extends FcpDialog> { + + private final AtomicBoolean finished = new AtomicBoolean(); + private final AtomicReference subscribedUSK = new AtomicReference<>(); + + public SubscribeUskDialog() throws IOException { + super(threadPool, connectionSupplier.get()); + } + + @Override + protected boolean isFinished() { + return finished.get(); + } + + @Override + protected Optional getResult() { + return Optional.ofNullable(subscribedUSK.get()).map(subscribedUSK -> new UskSubscription() { + @Override + public String getUri() { + return subscribedUSK.getURI(); + } + }); + } + + @Override + protected void consumeSubscribedUSK(SubscribedUSK subscribedUSK) { + this.subscribedUSK.set(subscribedUSK); + finished.set(true); + } + + @Override + protected void consumeIdentifierCollision(IdentifierCollision identifierCollision) { + finished.set(true); + } + + } + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/UskSubscription.java b/src/main/java/net/pterodactylus/fcp/quelaton/UskSubscription.java new file mode 100644 index 0000000..3ab2863 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/UskSubscription.java @@ -0,0 +1,12 @@ +package net.pterodactylus.fcp.quelaton; + +/** + * USK subscription object that is returned to the client application. + * + * @author David ‘Bombe’ Roden + */ +public interface UskSubscription { + + String getUri(); + +} diff --git a/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java b/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java index 557350b..4fad0ea 100644 --- a/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java +++ b/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java @@ -1993,20 +1993,20 @@ public class DefaultFcpClientTest { assertThat(newConfigData.get().getCurrent("foo.bar"), is("baz")); } - public class PluginCommands { + private List lines; + private String identifier; - private static final String CLASS_NAME = "foo.plugin.Plugin"; + private void connectAndAssert(Supplier>> requestMatcher) + throws InterruptedException, ExecutionException, IOException { + connectNode(); + lines = fcpServer.collectUntil(is("EndMessage")); + identifier = extractIdentifier(lines); + assertThat(lines, requestMatcher.get()); + } - private List lines; - private String identifier; + public class PluginCommands { - private void connectAndAssert(Supplier>> requestMatcher) - throws InterruptedException, ExecutionException, IOException { - connectNode(); - lines = fcpServer.collectUntil(is("EndMessage")); - identifier = extractIdentifier(lines); - assertThat(lines, requestMatcher.get()); - } + private static final String CLASS_NAME = "foo.plugin.Plugin"; private void replyWithPluginInfo() throws IOException { fcpServer.writeLine( @@ -2285,4 +2285,24 @@ public class DefaultFcpClientTest { } + public class UskSubscriptionCommands { + + private static final String URI = "SSK@some,uri/file.txt"; + + @Test + public void subscriptionWorks() throws InterruptedException, ExecutionException, IOException { + Future> uskSubscription = fcpClient.subscribeUsk().uri(URI).execute(); + connectAndAssert(() -> matchesFcpMessage("SubscribeUSK", "URI=" + URI, "EndMessage")); + fcpServer.writeLine( + "SubscribedUSK", + "Identifier=" + identifier, + "URI=" + URI, + "DontPoll=false", + "EndMessage" + ); + assertThat(uskSubscription.get().get().getUri(), is(URI)); + } + + } + } -- 2.7.4