Use listenable future instead of plain ones
[jFCPlib.git] / src / main / java / net / pterodactylus / fcp / quelaton / DefaultFcpClient.java
1 package net.pterodactylus.fcp.quelaton;
2
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;
9
10 import net.pterodactylus.fcp.ClientHello;
11 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
12 import net.pterodactylus.fcp.FcpConnection;
13 import net.pterodactylus.fcp.NodeHello;
14
15 import com.google.common.util.concurrent.ListeningExecutorService;
16 import com.google.common.util.concurrent.MoreExecutors;
17
18 /**
19  * Default {@link FcpClient} implementation.
20  *
21  * @author <a href="bombe@freenetproject.org">David ‘Bombe’ Roden</a>
22  */
23 public class DefaultFcpClient implements FcpClient {
24
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;
31
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;
36                 this.port = port;
37                 this.clientName = clientName;
38                 this.expectedVersion = expectedVersion;
39         }
40
41         private FcpConnection connect() throws IOException {
42                 FcpConnection fcpConnection = this.fcpConnection.get();
43                 if (fcpConnection != null) {
44                         return fcpConnection;
45                 }
46                 fcpConnection = createConnection();
47                 this.fcpConnection.compareAndSet(null, fcpConnection);
48                 return fcpConnection;
49         }
50
51         private FcpConnection createConnection() throws IOException {
52                 FcpConnection connection = new FcpConnection(hostname, port);
53                 connection.connect();
54                 FcpReplySequence<?> nodeHelloSequence = new ClientHelloReplySequence(connection);
55                 ClientHello clientHello = new ClientHello(clientName.get(), expectedVersion.get());
56                 try {
57                         nodeHelloSequence.send(clientHello).get();
58                 } catch (InterruptedException | ExecutionException e) {
59                         connection.close();
60                         throw new IOException(String.format("Could not connect to %s:%d.", hostname, port), e);
61                 }
62                 return connection;
63         }
64
65         @Override
66         public GenerateKeypairCommand generateKeypair() {
67                 return new GenerateKeypairCommandImpl(threadPool, this::connect);
68         }
69
70         @Override
71         public ClientGetCommand clientGet() {
72                 return new ClientGetCommandImpl(threadPool, this::connect);
73         }
74
75         private class ClientHelloReplySequence extends FcpReplySequence<Void> {
76
77                 private final AtomicReference<NodeHello> receivedNodeHello;
78                 private final AtomicBoolean receivedClosed;
79
80                 public ClientHelloReplySequence(FcpConnection connection) {
81                         super(DefaultFcpClient.this.threadPool, connection);
82                         receivedNodeHello = new AtomicReference<>();
83                         receivedClosed = new AtomicBoolean();
84                 }
85
86                 @Override
87                 protected boolean isFinished() {
88                         return receivedNodeHello.get() != null || receivedClosed.get();
89                 }
90
91                 @Override
92                 protected void consumeNodeHello(NodeHello nodeHello) {
93                         receivedNodeHello.set(nodeHello);
94                 }
95
96                 @Override
97                 protected void consumeCloseConnectionDuplicateClientName(
98                         CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
99                         receivedClosed.set(true);
100                 }
101
102         }
103
104 }
105