Rename “fcpAdapter” to “fcpListener”.
[jFCPlib.git] / src / net / pterodactylus / fcp / highlevel / FcpClient.java
1 /*
2  * jFCPlib - FcpClient.java -
3  * Copyright © 2009 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package net.pterodactylus.fcp.highlevel;
21
22 import java.io.IOException;
23 import java.net.InetAddress;
24 import java.net.UnknownHostException;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.concurrent.CountDownLatch;
28
29 import net.pterodactylus.fcp.ClientHello;
30 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
31 import net.pterodactylus.fcp.EndListPeers;
32 import net.pterodactylus.fcp.FcpAdapter;
33 import net.pterodactylus.fcp.FcpConnection;
34 import net.pterodactylus.fcp.FcpListener;
35 import net.pterodactylus.fcp.ListPeers;
36 import net.pterodactylus.fcp.NodeHello;
37 import net.pterodactylus.fcp.Peer;
38 import net.pterodactylus.fcp.ProtocolError;
39
40 /**
41  * High-level FCP client that hides the details of the underlying FCP
42  * implementation.
43  *
44  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
45  */
46 public class FcpClient {
47
48         /** Object used for synchronization. */
49         private final Object syncObject = new Object();
50
51         /** The name of this client. */
52         private final String name;
53
54         /** The underlying FCP connection. */
55         private final FcpConnection fcpConnection;
56
57         /**
58          * Creates an FCP client with the given name.
59          *
60          * @param name
61          *            The name of the FCP client
62          * @throws UnknownHostException
63          *             if the hostname “localhost” is unknown
64          */
65         public FcpClient(String name) throws UnknownHostException {
66                 this(name, "localhost");
67         }
68
69         /**
70          * Creates an FCP client.
71          *
72          * @param name
73          *            The name of the FCP client
74          * @param hostname
75          *            The hostname of the Freenet node
76          * @throws UnknownHostException
77          *             if the given hostname can not be resolved
78          */
79         public FcpClient(String name, String hostname) throws UnknownHostException {
80                 this(name, hostname, FcpConnection.DEFAULT_PORT);
81         }
82
83         /**
84          * Creates an FCP client.
85          *
86          * @param name
87          *            The name of the FCP client
88          * @param hostname
89          *            The hostname of the Freenet node
90          * @param port
91          *            The Freenet node’s FCP port
92          * @throws UnknownHostException
93          *             if the given hostname can not be resolved
94          */
95         public FcpClient(String name, String hostname, int port) throws UnknownHostException {
96                 this(name, InetAddress.getByName(hostname), port);
97         }
98
99         /**
100          * Creates an FCP client.
101          *
102          * @param name
103          *            The name of the FCP client
104          * @param host
105          *            The host address of the Freenet node
106          */
107         public FcpClient(String name, InetAddress host) {
108                 this(name, host, FcpConnection.DEFAULT_PORT);
109         }
110
111         /**
112          * Creates an FCP client.
113          *
114          * @param name
115          *            The name of the FCP client
116          * @param host
117          *            The host address of the Freenet node
118          * @param port
119          *            The Freenet node’s FCP port
120          */
121         public FcpClient(String name, InetAddress host, int port) {
122                 this.name = name;
123                 fcpConnection = new FcpConnection(host, port);
124         }
125
126         //
127         // ACTIONS
128         //
129
130         /**
131          * Connects the FCP client.
132          *
133          * @throws IOException
134          *             if an I/O error occurs
135          * @throws FcpException
136          *             if an FCP error occurs
137          */
138         public void connect() throws IOException, FcpException {
139                 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
140
141                         /**
142                          * {@inheritDoc}
143                          */
144                         @Override
145                         public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
146                                 completionLatch.countDown();
147                         }
148                 };
149                 fcpConnection.addFcpListener(fcpListener);
150                 try {
151                         fcpConnection.connect();
152                         ClientHello clientHello = new ClientHello(name);
153                         fcpConnection.sendMessage(clientHello);
154                         while (true) {
155                                 try {
156                                         fcpListener.complete();
157                                         break;
158                                 } catch (InterruptedException e) {
159                                         /* ignore, we’ll loop. */
160                                 }
161                         }
162                 } finally {
163                         fcpConnection.removeFcpListener(fcpListener);
164                 }
165                 if (fcpListener.getFcpException() != null) {
166                         throw fcpListener.getFcpException();
167                 }
168         }
169
170         /**
171          * Disconnects the FCP client.
172          */
173         public void disconnect() {
174                 synchronized (syncObject) {
175                         fcpConnection.close();
176                         syncObject.notifyAll();
177                 }
178         }
179
180         //
181         // PEER MANAGEMENT
182         //
183
184         /**
185          * Returns all peers that the node has.
186          *
187          * @return A set containing the node’s peers
188          * @throws IOException
189          *             if an I/O error occurs
190          * @throws FcpException
191          *             if an FCP error occurs
192          */
193         public Set<Peer> getPeers() throws IOException, FcpException {
194                 final Set<Peer> peers = new HashSet<Peer>();
195                 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
196
197                         /**
198                          * {@inheritDoc}
199                          */
200                         @Override
201                         public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
202                                 peers.add(peer);
203                         }
204
205                         /**
206                          * {@inheritDoc}
207                          */
208                         @Override
209                         public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
210                                 completionLatch.countDown();
211                         }
212                 };
213                 fcpConnection.addFcpListener(fcpListener);
214                 fcpConnection.sendMessage(new ListPeers("list-peers"));
215                 try {
216                         while (true) {
217                                 try {
218                                         fcpListener.complete();
219                                         break;
220                                 } catch (InterruptedException e) {
221                                         /* ignore, we’ll loop. */
222                                 }
223                         }
224                 } finally {
225                         fcpConnection.removeFcpListener(fcpListener);
226                 }
227                 if (fcpListener.getFcpException() != null) {
228                         throw fcpListener.getFcpException();
229                 }
230                 return peers;
231         }
232
233         /**
234          * Implementation of an {@link FcpListener} that can store an
235          * {@link FcpException} and wait for the arrival of a certain command.
236          *
237          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
238          */
239         private static class ExtendedFcpAdapter extends FcpAdapter {
240
241                 /** The count down latch used to wait for completion. */
242                 protected final CountDownLatch completionLatch = new CountDownLatch(1);
243
244                 /** The FCP exception, if any. */
245                 protected FcpException fcpException;
246
247                 /**
248                  * Creates a new extended FCP adapter.
249                  */
250                 public ExtendedFcpAdapter() {
251                         /* do nothing. */
252                 }
253
254                 /**
255                  * Returns the FCP exception that occured. If no FCP exception occured,
256                  * <code>null</code> is returned.
257                  *
258                  * @return The FCP exception that occured, or <code>null</code>
259                  */
260                 public FcpException getFcpException() {
261                         return fcpException;
262                 }
263
264                 /**
265                  * Waits for the completion of the command.
266                  *
267                  * @throws InterruptedException
268                  *             if {@link CountDownLatch#await()} is interrupted
269                  */
270                 public void complete() throws InterruptedException {
271                         completionLatch.await();
272                 }
273
274                 /**
275                  * {@inheritDoc}
276                  */
277                 @Override
278                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
279                         fcpException = new FcpException("Connection closed", throwable);
280                         completionLatch.countDown();
281                 }
282
283                 /**
284                  * {@inheritDoc}
285                  */
286                 @Override
287                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
288                         fcpException = new FcpException("Connection closed, duplicate client name");
289                         completionLatch.countDown();
290                 }
291
292                 /**
293                  * {@inheritDoc}
294                  */
295                 @Override
296                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
297                         fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
298                         completionLatch.countDown();
299                 }
300
301         }
302
303 }