Add method getPeers().
[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.NodeHello;
36 import net.pterodactylus.fcp.Peer;
37 import net.pterodactylus.fcp.ProtocolError;
38
39 /**
40  * High-level FCP client that hides the details of the underlying FCP
41  * implementation.
42  *
43  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
44  */
45 public class FcpClient {
46
47         /** Object used for synchronization. */
48         private final Object syncObject = new Object();
49
50         /** The name of this client. */
51         private final String name;
52
53         /** The underlying FCP connection. */
54         private final FcpConnection fcpConnection;
55
56         /**
57          * Creates an FCP client with the given name.
58          *
59          * @param name
60          *            The name of the FCP client
61          * @throws UnknownHostException
62          *             if the hostname “localhost” is unknown
63          */
64         public FcpClient(String name) throws UnknownHostException {
65                 this(name, "localhost");
66         }
67
68         /**
69          * Creates an FCP client.
70          *
71          * @param name
72          *            The name of the FCP client
73          * @param hostname
74          *            The hostname of the Freenet node
75          * @throws UnknownHostException
76          *             if the given hostname can not be resolved
77          */
78         public FcpClient(String name, String hostname) throws UnknownHostException {
79                 this(name, hostname, FcpConnection.DEFAULT_PORT);
80         }
81
82         /**
83          * Creates an FCP client.
84          *
85          * @param name
86          *            The name of the FCP client
87          * @param hostname
88          *            The hostname of the Freenet node
89          * @param port
90          *            The Freenet node’s FCP port
91          * @throws UnknownHostException
92          *             if the given hostname can not be resolved
93          */
94         public FcpClient(String name, String hostname, int port) throws UnknownHostException {
95                 this(name, InetAddress.getByName(hostname), port);
96         }
97
98         /**
99          * Creates an FCP client.
100          *
101          * @param name
102          *            The name of the FCP client
103          * @param host
104          *            The host address of the Freenet node
105          */
106         public FcpClient(String name, InetAddress host) {
107                 this(name, host, FcpConnection.DEFAULT_PORT);
108         }
109
110         /**
111          * Creates an FCP client.
112          *
113          * @param name
114          *            The name of the FCP client
115          * @param host
116          *            The host address of the Freenet node
117          * @param port
118          *            The Freenet node’s FCP port
119          */
120         public FcpClient(String name, InetAddress host, int port) {
121                 this.name = name;
122                 fcpConnection = new FcpConnection(host, port);
123         }
124
125         //
126         // ACTIONS
127         //
128
129         /**
130          * Connects the FCP client.
131          *
132          * @throws IOException
133          *             if an I/O error occurs
134          * @throws FcpException
135          *             if an FCP error occurs
136          */
137         public void connect() throws IOException, FcpException {
138                 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
139
140                         /**
141                          * {@inheritDoc}
142                          */
143                         @Override
144                         public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
145                                 completionLatch.countDown();
146                         }
147                 };
148                 fcpConnection.addFcpListener(fcpListener);
149                 try {
150                         fcpConnection.connect();
151                         ClientHello clientHello = new ClientHello(name);
152                         fcpConnection.sendMessage(clientHello);
153                         while (true) {
154                                 try {
155                                         fcpListener.complete();
156                                         break;
157                                 } catch (InterruptedException e) {
158                                         /* ignore, we’ll loop. */
159                                 }
160                         }
161                 } finally {
162                         fcpConnection.removeFcpListener(fcpListener);
163                 }
164                 if (fcpListener.getFcpException() != null) {
165                         throw fcpListener.getFcpException();
166                 }
167         }
168
169         /**
170          * Disconnects the FCP client.
171          */
172         public void disconnect() {
173                 synchronized (syncObject) {
174                         fcpConnection.close();
175                         syncObject.notifyAll();
176                 }
177         }
178
179         //
180         // PEER MANAGEMENT
181         //
182
183         /**
184          * Returns all peers that the node has.
185          *
186          * @return A set containing the node’s peers
187          * @throws IOException
188          *             if an I/O error occurs
189          * @throws FcpException
190          *             if an FCP error occurs
191          */
192         public Set<Peer> getPeers() throws IOException, FcpException {
193                 final Set<Peer> peers = new HashSet<Peer>();
194                 ExtendedFcpAdapter fcpAdapter = new ExtendedFcpAdapter() {
195
196                         /**
197                          * {@inheritDoc}
198                          */
199                         @Override
200                         public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
201                                 peers.add(peer);
202                         }
203
204                         /**
205                          * {@inheritDoc}
206                          */
207                         @Override
208                         public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
209                                 completionLatch.countDown();
210                         }
211                 };
212                 fcpConnection.addFcpListener(fcpAdapter);
213                 try {
214                         while (true) {
215                                 try {
216                                         fcpAdapter.complete();
217                                         break;
218                                 } catch (InterruptedException e) {
219                                         /* ignore, we’ll loop. */
220                                 }
221                         }
222                 } finally {
223                         fcpConnection.removeFcpListener(fcpAdapter);
224                 }
225                 return peers;
226         }
227
228         /**
229          * Implementation of an {@link FcpListener} that can store an
230          * {@link FcpException} and wait for the arrival of a certain command.
231          *
232          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
233          */
234         private static class ExtendedFcpAdapter extends FcpAdapter {
235
236                 /** The count down latch used to wait for completion. */
237                 protected final CountDownLatch completionLatch = new CountDownLatch(1);
238
239                 /** The FCP exception, if any. */
240                 protected FcpException fcpException;
241
242                 /**
243                  * Creates a new extended FCP adapter.
244                  */
245                 public ExtendedFcpAdapter() {
246                         /* do nothing. */
247                 }
248
249                 /**
250                  * Returns the FCP exception that occured. If no FCP exception occured,
251                  * <code>null</code> is returned.
252                  *
253                  * @return The FCP exception that occured, or <code>null</code>
254                  */
255                 public FcpException getFcpException() {
256                         return fcpException;
257                 }
258
259                 /**
260                  * Waits for the completion of the command.
261                  *
262                  * @throws InterruptedException
263                  *             if {@link CountDownLatch#await()} is interrupted
264                  */
265                 public void complete() throws InterruptedException {
266                         completionLatch.await();
267                 }
268
269                 /**
270                  * {@inheritDoc}
271                  */
272                 @Override
273                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
274                         fcpException = new FcpException("Connection closed", throwable);
275                         completionLatch.countDown();
276                 }
277
278                 /**
279                  * {@inheritDoc}
280                  */
281                 @Override
282                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
283                         fcpException = new FcpException("Connection closed, duplicate client name");
284                         completionLatch.countDown();
285                 }
286
287                 /**
288                  * {@inheritDoc}
289                  */
290                 @Override
291                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
292                         fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
293                         completionLatch.countDown();
294                 }
295
296         }
297
298 }