From: David ‘Bombe’ Roden Date: Fri, 21 Aug 2015 05:10:11 +0000 (+0200) Subject: Add command that loads a plugin X-Git-Url: https://git.pterodactylus.net/?p=jFCPlib.git;a=commitdiff_plain;h=ab9b04e29add4d86544ca9ec4ee40adc89c5359b Add command that loads a plugin --- diff --git a/src/main/java/net/pterodactylus/fcp/LoadPlugin.java b/src/main/java/net/pterodactylus/fcp/LoadPlugin.java new file mode 100644 index 0000000..9eb2d2d --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/LoadPlugin.java @@ -0,0 +1,47 @@ +package net.pterodactylus.fcp; + +/** + * The “LoadPlugin” message is used to load a plugin. + * + * @author David ‘Bombe’ Roden + */ +public class LoadPlugin extends FcpMessage { + + public enum UrlType { + + OFFICIAL, + FILE, + FREENET, + URL + + } + + public enum OfficialSource { + + FREENET, + HTTPS + + } + + public LoadPlugin(String identifier) { + super("LoadPlugin"); + setField("Identifier", identifier); + } + + public void setPluginUrl(String pluginUrl) { + setField("PluginURL", pluginUrl); + } + + public void setUrlType(UrlType urlType) { + setField("URLType", urlType.toString().toLowerCase()); + } + + public void setStore(boolean store) { + setField("Store", String.valueOf(store)); + } + + public void setOfficialSource(OfficialSource officialSource) { + setField("OfficialSource", officialSource.toString().toLowerCase()); + } + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java index 4cc7328..be26779 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/DefaultFcpClient.java @@ -114,5 +114,10 @@ public class DefaultFcpClient implements FcpClient { return new ModifyPeerNoteCommandImpl(threadPool, this::connect); } + @Override + public LoadPluginCommand loadPlugin() { + return new LoadPluginCommandImpl(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 ab25d61..5b3b2b1 100644 --- a/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java +++ b/src/main/java/net/pterodactylus/fcp/quelaton/FcpClient.java @@ -24,4 +24,6 @@ public interface FcpClient { ListPeerNotesCommand listPeerNotes(); ModifyPeerNoteCommand modifyPeerNote(); + LoadPluginCommand loadPlugin(); + } diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommand.java b/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommand.java new file mode 100644 index 0000000..16686a4 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommand.java @@ -0,0 +1,17 @@ +package net.pterodactylus.fcp.quelaton; + +import java.util.Optional; + +import net.pterodactylus.fcp.PluginInfo; + +/** + * Loads a plugin. + * + * @author David ‘Bombe’ Roden + */ +public interface LoadPluginCommand { + + LoadPluginCommand addToConfig(); + Executable> officialFromFreenet(String pluginIdentifier); + +} diff --git a/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommandImpl.java b/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommandImpl.java new file mode 100644 index 0000000..3109e8a --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/quelaton/LoadPluginCommandImpl.java @@ -0,0 +1,93 @@ +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.LoadPlugin; +import net.pterodactylus.fcp.LoadPlugin.OfficialSource; +import net.pterodactylus.fcp.LoadPlugin.UrlType; +import net.pterodactylus.fcp.PluginInfo; +import net.pterodactylus.fcp.ProtocolError; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +/** + * Default {@link LoadPluginCommand} implementation based on {@link FcpDialog}. + * + * @author David ‘Bombe’ Roden + */ +public class LoadPluginCommandImpl implements LoadPluginCommand { + + private static final RandomIdentifierGenerator IDENTIFIER = new RandomIdentifierGenerator(); + private final ListeningExecutorService threadPool; + private final ConnectionSupplier connectionSupplier; + private final LoadPlugin loadPlugin = new LoadPlugin(IDENTIFIER.generate()); + + public LoadPluginCommandImpl(ExecutorService threadPool, ConnectionSupplier connectionSupplier) { + this.threadPool = MoreExecutors.listeningDecorator(threadPool); + this.connectionSupplier = connectionSupplier; + } + + @Override + public LoadPluginCommand addToConfig() { + loadPlugin.setStore(true); + return this; + } + + @Override + public Executable> officialFromFreenet(String pluginIdentifier) { + loadPlugin.setUrlType(UrlType.OFFICIAL); + loadPlugin.setOfficialSource(OfficialSource.FREENET); + loadPlugin.setPluginUrl(pluginIdentifier); + return this::execute; + } + + private ListenableFuture> execute() { + return threadPool.submit(this::executeDialog); + } + + private Optional executeDialog() throws IOException, ExecutionException, InterruptedException { + try (LoadPluginDialog loadPluginDialog = new LoadPluginDialog()) { + return loadPluginDialog.send(loadPlugin).get(); + } + } + + private class LoadPluginDialog extends FcpDialog> { + + private final AtomicBoolean finished = new AtomicBoolean(); + private final AtomicReference pluginInfo = new AtomicReference<>(); + + public LoadPluginDialog() 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 35a2857..77e3588 100644 --- a/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java +++ b/src/test/java/net/pterodactylus/fcp/quelaton/DefaultFcpClientTest.java @@ -33,6 +33,7 @@ import net.pterodactylus.fcp.NodeData; import net.pterodactylus.fcp.NodeRef; import net.pterodactylus.fcp.Peer; import net.pterodactylus.fcp.PeerNote; +import net.pterodactylus.fcp.PluginInfo; import net.pterodactylus.fcp.Priority; import net.pterodactylus.fcp.fake.FakeTcpServer; import net.pterodactylus.fcp.quelaton.ClientGetCommand.Data; @@ -1986,4 +1987,90 @@ public class DefaultFcpClientTest { assertThat(newConfigData.get().getCurrent("foo.bar"), is("baz")); } + @Test + public void defaultFcpClientCanLoadPluginFromFreenet() throws ExecutionException, InterruptedException, + IOException { + Future> pluginInfo = fcpClient.loadPlugin().officialFromFreenet("superPlugin").execute(); + connectNode(); + List lines = fcpServer.collectUntil(is("EndMessage")); + String identifier = extractIdentifier(lines); + assertThat(lines, matchesFcpMessage( + "LoadPlugin", + "Identifier=" + identifier, + "PluginURL=superPlugin", + "URLType=official", + "OfficialSource=freenet", + "EndMessage" + )); + assertThat(lines, not(contains(startsWith("Store=")))); + fcpServer.writeLine( + "PluginInfo", + "Identifier=" + identifier, + "PluginName=superPlugin", + "IsTalkable=true", + "LongVersion=1.2.3", + "Version=42", + "OriginUri=superPlugin", + "Started=true", + "EndMessage" + ); + 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)); + } + + @Test + public void defaultFcpClientCanLoadPersistentPluginFromFreenet() throws ExecutionException, InterruptedException, + IOException { + Future> pluginInfo = + fcpClient.loadPlugin().addToConfig().officialFromFreenet("superPlugin").execute(); + connectNode(); + List lines = fcpServer.collectUntil(is("EndMessage")); + String identifier = extractIdentifier(lines); + assertThat(lines, matchesFcpMessage( + "LoadPlugin", + "Identifier=" + identifier, + "PluginURL=superPlugin", + "URLType=official", + "OfficialSource=freenet", + "Store=true", + "EndMessage" + )); + fcpServer.writeLine( + "PluginInfo", + "Identifier=" + identifier, + "PluginName=superPlugin", + "IsTalkable=true", + "LongVersion=1.2.3", + "Version=42", + "OriginUri=superPlugin", + "Started=true", + "EndMessage" + ); + 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)); + } + + @Test + public void failedLoadingPluginIsRecognized() throws ExecutionException, InterruptedException, + IOException { + Future> pluginInfo = fcpClient.loadPlugin().officialFromFreenet("superPlugin").execute(); + connectNode(); + List lines = fcpServer.collectUntil(is("EndMessage")); + String identifier = extractIdentifier(lines); + fcpServer.writeLine( + "ProtocolError", + "Identifier=" + identifier, + "EndMessage" + ); + assertThat(pluginInfo.get().isPresent(), is(false)); + } + }