1 package net.pterodactylus.fcp.quelaton;
3 import java.io.IOException;
4 import java.util.concurrent.ExecutionException;
5 import java.util.concurrent.ExecutorService;
6 import java.util.concurrent.atomic.AtomicBoolean;
7 import java.util.concurrent.atomic.AtomicReference;
8 import java.util.function.Supplier;
10 import net.pterodactylus.fcp.ClientHello;
11 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
12 import net.pterodactylus.fcp.FcpConnection;
13 import net.pterodactylus.fcp.NodeHello;
15 import com.google.common.util.concurrent.ListeningExecutorService;
16 import com.google.common.util.concurrent.MoreExecutors;
19 * Default {@link FcpClient} implementation.
21 * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
23 public class DefaultFcpClient implements FcpClient {
25 private final ListeningExecutorService threadPool;
26 private final String hostname;
27 private final int port;
28 private final AtomicReference<FcpConnection> fcpConnection = new AtomicReference<>();
29 private final Supplier<String> clientName;
31 public DefaultFcpClient(ExecutorService threadPool, String hostname, int port, Supplier<String> clientName) {
32 this.threadPool = MoreExecutors.listeningDecorator(threadPool);
33 this.hostname = hostname;
35 this.clientName = clientName;
38 private FcpConnection connect() throws IOException {
39 FcpConnection fcpConnection = this.fcpConnection.get();
40 if (fcpConnection != null) {
43 fcpConnection = createConnection();
44 this.fcpConnection.compareAndSet(null, fcpConnection);
48 private FcpConnection createConnection() throws IOException {
49 FcpConnection connection = new FcpConnection(hostname, port);
51 FcpReplySequence<?> nodeHelloSequence = new ClientHelloReplySequence(connection);
52 ClientHello clientHello = new ClientHello(clientName.get(), "2.0");
54 nodeHelloSequence.send(clientHello).get();
55 } catch (InterruptedException | ExecutionException e) {
57 throw new IOException(String.format("Could not connect to %s:%d.", hostname, port), e);
63 public GenerateKeypairCommand generateKeypair() {
64 return new GenerateKeypairCommandImpl(threadPool, this::connect);
68 public ClientGetCommand clientGet() {
69 return new ClientGetCommandImpl(threadPool, this::connect);
73 public ClientPutCommand clientPut() {
74 return new ClientPutCommandImpl(threadPool, this::connect);
78 public ListPeersCommand listPeers() {
79 return new ListPeersCommandImpl(threadPool, this::connect);
82 private class ClientHelloReplySequence extends FcpReplySequence<Void> {
84 private final AtomicReference<NodeHello> receivedNodeHello;
85 private final AtomicBoolean receivedClosed;
87 public ClientHelloReplySequence(FcpConnection connection) {
88 super(DefaultFcpClient.this.threadPool, connection);
89 receivedNodeHello = new AtomicReference<>();
90 receivedClosed = new AtomicBoolean();
94 protected boolean isFinished() {
95 return receivedNodeHello.get() != null || receivedClosed.get();
99 protected void consumeNodeHello(NodeHello nodeHello) {
100 receivedNodeHello.set(nodeHello);
104 protected void consumeCloseConnectionDuplicateClientName(
105 CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
106 receivedClosed.set(true);