Always expect version 2.0
[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
31         public DefaultFcpClient(ExecutorService threadPool, String hostname, int port, Supplier<String> clientName) {
32                 this.threadPool = MoreExecutors.listeningDecorator(threadPool);
33                 this.hostname = hostname;
34                 this.port = port;
35                 this.clientName = clientName;
36         }
37
38         private FcpConnection connect() throws IOException {
39                 FcpConnection fcpConnection = this.fcpConnection.get();
40                 if (fcpConnection != null) {
41                         return fcpConnection;
42                 }
43                 fcpConnection = createConnection();
44                 this.fcpConnection.compareAndSet(null, fcpConnection);
45                 return fcpConnection;
46         }
47
48         private FcpConnection createConnection() throws IOException {
49                 FcpConnection connection = new FcpConnection(hostname, port);
50                 connection.connect();
51                 FcpReplySequence<?> nodeHelloSequence = new ClientHelloReplySequence(connection);
52                 ClientHello clientHello = new ClientHello(clientName.get(), "2.0");
53                 try {
54                         nodeHelloSequence.send(clientHello).get();
55                 } catch (InterruptedException | ExecutionException e) {
56                         connection.close();
57                         throw new IOException(String.format("Could not connect to %s:%d.", hostname, port), e);
58                 }
59                 return connection;
60         }
61
62         @Override
63         public GenerateKeypairCommand generateKeypair() {
64                 return new GenerateKeypairCommandImpl(threadPool, this::connect);
65         }
66
67         @Override
68         public ClientGetCommand clientGet() {
69                 return new ClientGetCommandImpl(threadPool, this::connect);
70         }
71
72         @Override
73         public ClientPutCommand clientPut() {
74                 return new ClientPutCommandImpl(threadPool, this::connect);
75         }
76
77         private class ClientHelloReplySequence extends FcpReplySequence<Void> {
78
79                 private final AtomicReference<NodeHello> receivedNodeHello;
80                 private final AtomicBoolean receivedClosed;
81
82                 public ClientHelloReplySequence(FcpConnection connection) {
83                         super(DefaultFcpClient.this.threadPool, connection);
84                         receivedNodeHello = new AtomicReference<>();
85                         receivedClosed = new AtomicBoolean();
86                 }
87
88                 @Override
89                 protected boolean isFinished() {
90                         return receivedNodeHello.get() != null || receivedClosed.get();
91                 }
92
93                 @Override
94                 protected void consumeNodeHello(NodeHello nodeHello) {
95                         receivedNodeHello.set(nodeHello);
96                 }
97
98                 @Override
99                 protected void consumeCloseConnectionDuplicateClientName(
100                         CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
101                         receivedClosed.set(true);
102                 }
103
104         }
105
106 }
107