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);
77 private class ClientHelloReplySequence extends FcpReplySequence<Void> {
79 private final AtomicReference<NodeHello> receivedNodeHello;
80 private final AtomicBoolean receivedClosed;
82 public ClientHelloReplySequence(FcpConnection connection) {
83 super(DefaultFcpClient.this.threadPool, connection);
84 receivedNodeHello = new AtomicReference<>();
85 receivedClosed = new AtomicBoolean();
89 protected boolean isFinished() {
90 return receivedNodeHello.get() != null || receivedClosed.get();
94 protected void consumeNodeHello(NodeHello nodeHello) {
95 receivedNodeHello.set(nodeHello);
99 protected void consumeCloseConnectionDuplicateClientName(
100 CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
101 receivedClosed.set(true);