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;
30 private final Supplier<String> expectedVersion;
32 public DefaultFcpClient(ExecutorService threadPool, String hostname, int port, Supplier<String> clientName,
33 Supplier<String> expectedVersion) {
34 this.threadPool = MoreExecutors.listeningDecorator(threadPool);
35 this.hostname = hostname;
37 this.clientName = clientName;
38 this.expectedVersion = expectedVersion;
41 private FcpConnection connect() throws IOException {
42 FcpConnection fcpConnection = this.fcpConnection.get();
43 if (fcpConnection != null) {
46 fcpConnection = createConnection();
47 this.fcpConnection.compareAndSet(null, fcpConnection);
51 private FcpConnection createConnection() throws IOException {
52 FcpConnection connection = new FcpConnection(hostname, port);
54 FcpReplySequence<?> nodeHelloSequence = new ClientHelloReplySequence(connection);
55 ClientHello clientHello = new ClientHello(clientName.get(), expectedVersion.get());
57 nodeHelloSequence.send(clientHello).get();
58 } catch (InterruptedException | ExecutionException e) {
60 throw new IOException(String.format("Could not connect to %s:%d.", hostname, port), e);
66 public GenerateKeypairCommand generateKeypair() {
67 return new GenerateKeypairCommandImpl(threadPool, this::connect);
71 public ClientGetCommand clientGet() {
72 return new ClientGetCommandImpl(threadPool, this::connect);
75 private class ClientHelloReplySequence extends FcpReplySequence<Void> {
77 private final AtomicReference<NodeHello> receivedNodeHello;
78 private final AtomicBoolean receivedClosed;
80 public ClientHelloReplySequence(FcpConnection connection) {
81 super(DefaultFcpClient.this.threadPool, connection);
82 receivedNodeHello = new AtomicReference<>();
83 receivedClosed = new AtomicBoolean();
87 protected boolean isFinished() {
88 return receivedNodeHello.get() != null || receivedClosed.get();
92 protected void consumeNodeHello(NodeHello nodeHello) {
93 receivedNodeHello.set(nodeHello);
97 protected void consumeCloseConnectionDuplicateClientName(
98 CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
99 receivedClosed.set(true);