From 47919ef5e6b1940f0682c91c5234b5b513e18afe Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 2 Sep 2024 20:22:42 +0200 Subject: [PATCH] =?utf8?q?=E2=9C=A8=20Add=20String-based=20dispatcher=20im?= =?utf8?q?plementation=20of=20FcpListener?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/fcp/DispatchingFcpAdapter.java | 243 ++++++++++++++++++++ .../fcp/DispatchingFcpAdapterTest.java | 250 +++++++++++++++++++++ 2 files changed, 493 insertions(+) create mode 100644 src/main/java/net/pterodactylus/fcp/DispatchingFcpAdapter.java create mode 100644 src/test/java/net/pterodactylus/fcp/DispatchingFcpAdapterTest.java diff --git a/src/main/java/net/pterodactylus/fcp/DispatchingFcpAdapter.java b/src/main/java/net/pterodactylus/fcp/DispatchingFcpAdapter.java new file mode 100644 index 0000000..e746316 --- /dev/null +++ b/src/main/java/net/pterodactylus/fcp/DispatchingFcpAdapter.java @@ -0,0 +1,243 @@ +package net.pterodactylus.fcp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; + +/** + * {@link FcpListener} implementation that dispatches messages to event + * listeners that are really just {@link Consumer}s. All methods named + * “receivedX” from the {@link FcpListener} interface can be dispatched by + * calling {@link #addListener(String, Consumer)} with “X” as the message + * name. Registering a listener for the method + * {@link FcpListener#connectionClosed(FcpConnection, Throwable)} uses + * {@code "ConnectionClosed"} as message name, and the given consumer + * will receive the throwable. + */ +public class DispatchingFcpAdapter implements FcpListener { + + public void addListener(String message, Consumer eventListener) { + eventListeners.merge(message, singletonList((Consumer) eventListener), (l1, l2) -> { + List> merged = new ArrayList<>(l1); + merged.addAll(l2); + return merged; + }); + } + + @Override + public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) { + dispatchEvent("NodeHello", nodeHello); + } + + @Override + public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) { + dispatchEvent("CloseConnectionDuplicateClientName", closeConnectionDuplicateClientName); + } + + @Override + public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) { + dispatchEvent("SSKKeypair", sskKeypair); + } + + @Override + public void receivedPeer(FcpConnection fcpConnection, Peer peer) { + dispatchEvent("Peer", peer); + } + + @Override + public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) { + dispatchEvent("EndListPeers", endListPeers); + } + + @Override + public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) { + dispatchEvent("PeerNote", peerNote); + } + + @Override + public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) { + dispatchEvent("EndListPeerNotes", endListPeerNotes); + } + + @Override + public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) { + dispatchEvent("PeerRemoved", peerRemoved); + } + + @Override + public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) { + dispatchEvent("NodeData", nodeData); + } + + @Override + public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) { + dispatchEvent("TestDDAReply", testDDAReply); + } + + @Override + public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) { + dispatchEvent("TestDDAComplete", testDDAComplete); + } + + @Override + public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) { + dispatchEvent("PersistentGet", persistentGet); + } + + @Override + public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) { + dispatchEvent("PersistentPut", persistentPut); + } + + @Override + public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) { + dispatchEvent("EndListPersistentRequests", endListPersistentRequests); + } + + @Override + public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) { + dispatchEvent("URIGenerated", uriGenerated); + } + + @Override + public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) { + dispatchEvent("DataFound", dataFound); + } + + @Override + public void receivedAllData(FcpConnection fcpConnection, AllData allData) { + dispatchEvent("AllData", allData); + } + + @Override + public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) { + dispatchEvent("SimpleProgress", simpleProgress); + } + + @Override + public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) { + dispatchEvent("StartedCompression", startedCompression); + } + + @Override + public void receivedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) { + dispatchEvent("FinishedCompression", finishedCompression); + } + + @Override + public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) { + dispatchEvent("UnknownPeerNoteType", unknownPeerNoteType); + } + + @Override + public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) { + dispatchEvent("UnknownNodeIdentifier", unknownNodeIdentifier); + } + + @Override + public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) { + dispatchEvent("ConfigData", configData); + } + + @Override + public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) { + dispatchEvent("GetFailed", getFailed); + } + + @Override + public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) { + dispatchEvent("PutFailed", putFailed); + } + + @Override + public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) { + dispatchEvent("IdentifierCollision", identifierCollision); + } + + @Override + public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) { + dispatchEvent("PersistentPutDir", persistentPutDir); + } + + @Override + public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) { + dispatchEvent("PersistentRequestRemoved", persistentRequestRemoved); + } + + @Override + public void receivedSubscribedUSK(FcpConnection fcpConnection, SubscribedUSK subscribedUSK) { + dispatchEvent("SubscribedUSK", subscribedUSK); + } + + @Override + public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) { + dispatchEvent("SubscribedUSKUpdate", subscribedUSKUpdate); + } + + @Override + public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) { + dispatchEvent("PluginInfo", pluginInfo); + } + + @Override + public void receivedPluginRemoved(FcpConnection fcpConnection, PluginRemoved pluginRemoved) { + dispatchEvent("PluginRemoved", pluginRemoved); + } + + @Override + public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) { + dispatchEvent("FCPPluginReply", fcpPluginReply); + } + + @Override + public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) { + dispatchEvent("PersistentRequestModified", persistentRequestModified); + } + + @Override + public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) { + dispatchEvent("PutSuccessful", putSuccessful); + } + + @Override + public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) { + dispatchEvent("PutFetchable", putFetchable); + } + + @Override + public void receivedSentFeed(FcpConnection source, SentFeed sentFeed) { + dispatchEvent("SentFeed", sentFeed); + } + + @Override + public void receivedBookmarkFeed(FcpConnection fcpConnection, ReceivedBookmarkFeed receivedBookmarkFeed) { + dispatchEvent("ReceivedBookmarkFeed", receivedBookmarkFeed); + } + + @Override + public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) { + dispatchEvent("ProtocolError", protocolError); + } + + @Override + public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) { + dispatchEvent("Message", fcpMessage); + } + + @Override + public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) { + dispatchEvent("ConnectionClosed", throwable); + } + + private void dispatchEvent(String name, Object message) { + eventListeners.getOrDefault(name, emptyList()).forEach(eventListener -> eventListener.accept(message)); + } + + private final Map>> eventListeners = new HashMap<>(); + +} diff --git a/src/test/java/net/pterodactylus/fcp/DispatchingFcpAdapterTest.java b/src/test/java/net/pterodactylus/fcp/DispatchingFcpAdapterTest.java new file mode 100644 index 0000000..6fdf0da --- /dev/null +++ b/src/test/java/net/pterodactylus/fcp/DispatchingFcpAdapterTest.java @@ -0,0 +1,250 @@ +package net.pterodactylus.fcp; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.equalTo; + +public class DispatchingFcpAdapterTest { + + @Test + public void nodeHelloIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("NodeHello", new NodeHello(null), (adapter, message) -> adapter.receivedNodeHello(null, message)); + } + + @Test + public void closedConnectionDuplicateClientNameIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("CloseConnectionDuplicateClientName", new CloseConnectionDuplicateClientName(null), (adapter, message) -> adapter.receivedCloseConnectionDuplicateClientName(null, message)); + } + + @Test + public void sskKeypairIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("SSKKeypair", new SSKKeypair(null), (adapter, message) -> adapter.receivedSSKKeypair(null, message)); + } + + @Test + public void peerIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("Peer", new Peer(null), (adapter, message) -> adapter.receivedPeer(null, message)); + } + + @Test + public void endListPeersIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("EndListPeers", new EndListPeers(null), (adapter, message) -> adapter.receivedEndListPeers(null, message)); + } + + @Test + public void peerNoteIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PeerNote", new PeerNote(null), (adapter, message) -> adapter.receivedPeerNote(null, message)); + } + + @Test + public void endListPeerNotesIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("EndListPeerNotes", new EndListPeerNotes(null), (adapter, message) -> adapter.receivedEndListPeerNotes(null, message)); + } + + @Test + public void peerRemovedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PeerRemoved", new PeerRemoved(null), (adapter, message) -> adapter.receivedPeerRemoved(null, message)); + } + + @Test + public void nodeDataIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("NodeData", createNodeDataMessage(), (adapter, message) -> adapter.receivedNodeData(null, message)); + } + + private NodeData createNodeDataMessage() { + FcpMessage receivedMessage = new FcpMessage(""); + receivedMessage.setField("ark.pubURI", ""); + receivedMessage.setField("ark.number", "0"); + receivedMessage.setField("auth.negTypes", "0"); + receivedMessage.setField("version", "0,1,2,3"); + receivedMessage.setField("lastGoodVersion", "0,1,2,3"); + return new NodeData(receivedMessage); + } + + @Test + public void testDDAReplyIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("TestDDAReply", new TestDDAReply(null), (adapter, message) -> adapter.receivedTestDDAReply(null, message)); + } + + @Test + public void testDDACompleteIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("TestDDAComplete", new TestDDAComplete(null), (adapter, message) -> adapter.receivedTestDDAComplete(null, message)); + } + + @Test + public void persistentGetIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PersistentGet", new PersistentGet(null), (adapter, message) -> adapter.receivedPersistentGet(null, message)); + } + + @Test + public void persistentPutIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PersistentPut", new PersistentPut(null), (adapter, message) -> adapter.receivedPersistentPut(null, message)); + } + + @Test + public void endListPersistentRequestsIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("EndListPersistentRequests", new EndListPersistentRequests(null), (adapter, message) -> adapter.receivedEndListPersistentRequests(null, message)); + } + + @Test + public void uriGeneratedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("URIGenerated", new URIGenerated(null), (adapter, message) -> adapter.receivedURIGenerated(null, message)); + } + + @Test + public void dataFoundIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("DataFound", new DataFound(null), (adapter, message) -> adapter.receivedDataFound(null, message)); + } + + @Test + public void allDataIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("AllData", new AllData(null, null), (adapter, message) -> adapter.receivedAllData(null, message)); + } + + @Test + public void simpleProgressIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("SimpleProgress", new SimpleProgress(null), (adapter, message) -> adapter.receivedSimpleProgress(null, message)); + } + + @Test + public void startedCompressionIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("StartedCompression", new StartedCompression(null), (adapter, message) -> adapter.receivedStartedCompression(null, message)); + } + + @Test + public void finishedCompressionIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("FinishedCompression", new FinishedCompression(null), (adapter, message) -> adapter.receivedFinishedCompression(null, message)); + } + + @Test + public void unknownPeerNoteTypeIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("UnknownPeerNoteType", new UnknownPeerNoteType(null), (adapter, message) -> adapter.receivedUnknownPeerNoteType(null, message)); + } + + @Test + public void unknownNodeIdentifierIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("UnknownNodeIdentifier", new UnknownNodeIdentifier(null), (adapter, message) -> adapter.receivedUnknownNodeIdentifier(null, message)); + } + + @Test + public void configDataIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("ConfigData", new ConfigData(null), (adapter, message) -> adapter.receivedConfigData(null, message)); + } + + @Test + public void getFailedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("GetFailed", new GetFailed(null), (adapter, message) -> adapter.receivedGetFailed(null, message)); + } + + @Test + public void putFailedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PutFailed", new PutFailed(null), (adapter, message) -> adapter.receivedPutFailed(null, message)); + } + + @Test + public void identifierCollisionIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("IdentifierCollision", new IdentifierCollision(null), (adapter, message) -> adapter.receivedIdentifierCollision(null, message)); + } + + @Test + public void persistentPutDirIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PersistentPutDir", new PersistentPutDir(null), (adapter, message) -> adapter.receivedPersistentPutDir(null, message)); + } + + @Test + public void persistentRequestRemovedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PersistentRequestRemoved", new PersistentRequestRemoved(null), (adapter, message) -> adapter.receivedPersistentRequestRemoved(null, message)); + } + + @Test + public void subscribedUSKIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("SubscribedUSK", new SubscribedUSK(null), (adapter, message) -> adapter.receivedSubscribedUSK(null, message)); + } + + @Test + public void subscribedUSKUpdateIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("SubscribedUSKUpdate", new SubscribedUSKUpdate(null), (adapter, message) -> adapter.receivedSubscribedUSKUpdate(null, message)); + } + + @Test + public void pluginInfoIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PluginInfo", new PluginInfo(null), (adapter, message) -> adapter.receivedPluginInfo(null, message)); + } + + @Test + public void pluginRemovedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PluginRemoved", new PluginRemoved(null), (adapter, message) -> adapter.receivedPluginRemoved(null, message)); + } + + @Test + public void fcpPluginReplyIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("FCPPluginReply", new FCPPluginReply(null, null), (adapter, message) -> adapter.receivedFCPPluginReply(null, message)); + } + + @Test + public void persistentRequestModifiedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PersistentRequestModified", new PersistentRequestModified(null), (adapter, message) -> adapter.receivedPersistentRequestModified(null, message)); + } + + @Test + public void putSuccessfulIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PutSuccessful", new PutSuccessful(null), (adapter, message) -> adapter.receivedPutSuccessful(null, message)); + } + + @Test + public void putFetchableIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("PutFetchable", new PutFetchable(null), (adapter, message) -> adapter.receivedPutFetchable(null, message)); + } + + @Test + public void sentFeedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("SentFeed", new SentFeed(null), (adapter, message) -> adapter.receivedSentFeed(null, message)); + } + + @Test + public void receivedBookmarkFeedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("ReceivedBookmarkFeed", new ReceivedBookmarkFeed(null), (adapter, message) -> adapter.receivedBookmarkFeed(null, message)); + } + + @Test + public void protocolErrorIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("ProtocolError", new ProtocolError(null), (adapter, message) -> adapter.receivedProtocolError(null, message)); + } + + @Test + public void messageIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("Message", new FcpMessage(null), (adapter, message) -> adapter.receivedMessage(null, message)); + } + + @Test + public void connectionClosedIsDispatchedCorrectly() { + dispatchMessageAndVerifyDispatch("ConnectionClosed", new Exception("Test"), (adapter, throwable) -> adapter.connectionClosed(null, throwable)); + } + + @Test + public void multipleListenersCanBeAttachedToOneMessage() { + DispatchingFcpAdapter adapter = new DispatchingFcpAdapter(); + List receivedMessages = new ArrayList<>(); + adapter.addListener("Message", receivedMessages::add); + adapter.addListener("Message", receivedMessages::add); + FcpMessage message = new FcpMessage("Test"); + adapter.receivedMessage(null, message); + assertThat(receivedMessages, contains(message, message)); + } + + private void dispatchMessageAndVerifyDispatch(String messageName, T message, BiConsumer messageDispatcher) { + DispatchingFcpAdapter adapter = new DispatchingFcpAdapter(); + AtomicReference receivedMessage = new AtomicReference<>(); + adapter.addListener(messageName, receivedMessage::set); + messageDispatcher.accept(adapter, message); + assertThat(receivedMessage.get(), equalTo(message)); + } + +} -- 2.7.4