2 * jFCPlib - FcpClient.java -
3 * Copyright © 2009 David Roden
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.
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.
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.
20 package net.pterodactylus.fcp.highlevel;
22 import java.io.IOException;
23 import java.net.InetAddress;
25 import java.net.UnknownHostException;
26 import java.util.HashSet;
28 import java.util.concurrent.CountDownLatch;
30 import net.pterodactylus.fcp.AddPeer;
31 import net.pterodactylus.fcp.ClientHello;
32 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
33 import net.pterodactylus.fcp.EndListPeers;
34 import net.pterodactylus.fcp.FcpAdapter;
35 import net.pterodactylus.fcp.FcpConnection;
36 import net.pterodactylus.fcp.FcpListener;
37 import net.pterodactylus.fcp.ListPeers;
38 import net.pterodactylus.fcp.ModifyPeer;
39 import net.pterodactylus.fcp.NodeHello;
40 import net.pterodactylus.fcp.NodeRef;
41 import net.pterodactylus.fcp.Peer;
42 import net.pterodactylus.fcp.ProtocolError;
45 * High-level FCP client that hides the details of the underlying FCP
48 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
50 public class FcpClient {
52 /** Object used for synchronization. */
53 private final Object syncObject = new Object();
55 /** The name of this client. */
56 private final String name;
58 /** The underlying FCP connection. */
59 private final FcpConnection fcpConnection;
62 * Creates an FCP client with the given name.
65 * The name of the FCP client
66 * @throws UnknownHostException
67 * if the hostname “localhost” is unknown
69 public FcpClient(String name) throws UnknownHostException {
70 this(name, "localhost");
74 * Creates an FCP client.
77 * The name of the FCP client
79 * The hostname of the Freenet node
80 * @throws UnknownHostException
81 * if the given hostname can not be resolved
83 public FcpClient(String name, String hostname) throws UnknownHostException {
84 this(name, hostname, FcpConnection.DEFAULT_PORT);
88 * Creates an FCP client.
91 * The name of the FCP client
93 * The hostname of the Freenet node
95 * The Freenet node’s FCP port
96 * @throws UnknownHostException
97 * if the given hostname can not be resolved
99 public FcpClient(String name, String hostname, int port) throws UnknownHostException {
100 this(name, InetAddress.getByName(hostname), port);
104 * Creates an FCP client.
107 * The name of the FCP client
109 * The host address of the Freenet node
111 public FcpClient(String name, InetAddress host) {
112 this(name, host, FcpConnection.DEFAULT_PORT);
116 * Creates an FCP client.
119 * The name of the FCP client
121 * The host address of the Freenet node
123 * The Freenet node’s FCP port
125 public FcpClient(String name, InetAddress host, int port) {
127 fcpConnection = new FcpConnection(host, port);
135 * Connects the FCP client.
137 * @throws IOException
138 * if an I/O error occurs
139 * @throws FcpException
140 * if an FCP error occurs
142 public void connect() throws IOException, FcpException {
143 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
149 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
150 completionLatch.countDown();
153 fcpConnection.addFcpListener(fcpListener);
155 fcpConnection.connect();
156 ClientHello clientHello = new ClientHello(name);
157 fcpConnection.sendMessage(clientHello);
160 fcpListener.complete();
162 } catch (InterruptedException e) {
163 /* ignore, we’ll loop. */
167 fcpConnection.removeFcpListener(fcpListener);
169 if (fcpListener.getFcpException() != null) {
170 throw fcpListener.getFcpException();
175 * Disconnects the FCP client.
177 public void disconnect() {
178 synchronized (syncObject) {
179 fcpConnection.close();
180 syncObject.notifyAll();
189 * Returns all peers that the node has.
191 * @return A set containing the node’s peers
192 * @throws IOException
193 * if an I/O error occurs
194 * @throws FcpException
195 * if an FCP error occurs
197 public Set<Peer> getPeers() throws IOException, FcpException {
198 final Set<Peer> peers = new HashSet<Peer>();
199 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
205 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
213 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
214 completionLatch.countDown();
217 fcpConnection.addFcpListener(fcpListener);
218 fcpConnection.sendMessage(new ListPeers("list-peers"));
222 fcpListener.complete();
224 } catch (InterruptedException e) {
225 /* ignore, we’ll loop. */
229 fcpConnection.removeFcpListener(fcpListener);
231 if (fcpListener.getFcpException() != null) {
232 throw fcpListener.getFcpException();
238 * Adds the given peer to the node.
242 * @throws IOException
243 * if an I/O error occurs
244 * @throws FcpException
245 * if an FCP error occurs
247 public void addPeer(Peer peer) throws IOException, FcpException {
248 addPeer(peer.getNodeRef());
252 * Adds the peer defined by the noderef to the node.
255 * The noderef that defines the new peer
256 * @throws IOException
257 * if an I/O error occurs
258 * @throws FcpException
259 * if an FCP error occurs
261 public void addPeer(NodeRef nodeRef) throws IOException, FcpException {
262 addPeer(new AddPeer(nodeRef));
266 * Adds a peer, reading the noderef from the given URL.
269 * The URL to read the noderef from
270 * @throws IOException
271 * if an I/O error occurs
272 * @throws FcpException
273 * if an FCP error occurs
275 public void addPeer(URL url) throws IOException, FcpException {
276 addPeer(new AddPeer(url));
280 * Adds a peer, reading the noderef of the peer from the given file.
281 * <strong>Note:</strong> the file to read the noderef from has to reside on
282 * the same machine as the node!
285 * The name of the file containing the peer’s noderef
286 * @throws IOException
287 * if an I/O error occurs
288 * @throws FcpException
289 * if an FCP error occurs
291 public void addPeer(String file) throws IOException, FcpException {
292 addPeer(new AddPeer(file));
296 * Sends the given {@link AddPeer} message to the node. This method should
297 * not be called directly. Use one of {@link #addPeer(Peer)},
298 * {@link #addPeer(NodeRef)}, {@link #addPeer(URL)}, or
299 * {@link #addPeer(String)} instead.
302 * The “AddPeer” message
303 * @throws IOException
304 * if an I/O error occurs
305 * @throws FcpException
306 * if an FCP error occurs
308 private void addPeer(AddPeer addPeer) throws IOException, FcpException {
309 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
315 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
316 completionLatch.countDown();
319 fcpConnection.addFcpListener(fcpListener);
321 fcpConnection.sendMessage(addPeer);
324 fcpListener.complete();
326 } catch (InterruptedException ie1) {
327 /* ignore, we’ll loop. */
331 fcpConnection.removeFcpListener(fcpListener);
333 if (fcpListener.getFcpException() != null) {
334 throw fcpListener.getFcpException();
339 * Modifies the given peer.
343 * @param allowLocalAddresses
344 * <code>true</code> to allow local address, <code>false</code>
345 * to not allow local address, <code>null</code> to not change
348 * <code>true</code> to disable the peer, <code>false</code> to
349 * enable the peer, <code>null</code> to not change the setting
351 * <code>true</code> to enable “listen only” for the peer,
352 * <code>false</code> to disable it, <code>null</code> to not
354 * @throws IOException
355 * if an I/O error occurs
356 * @throws FcpException
357 * if an FCP error occurs
359 public void modifyPeer(Peer peer, Boolean allowLocalAddresses, Boolean disabled, Boolean listenOnly) throws IOException, FcpException {
360 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
366 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
367 completionLatch.countDown();
370 fcpConnection.addFcpListener(fcpListener);
372 fcpConnection.sendMessage(new ModifyPeer(peer.getIdentity(), allowLocalAddresses, disabled, listenOnly));
374 fcpConnection.removeFcpListener(fcpListener);
376 if (fcpListener.getFcpException() != null) {
377 throw fcpListener.getFcpException();
382 * Implementation of an {@link FcpListener} that can store an
383 * {@link FcpException} and wait for the arrival of a certain command.
385 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
387 private static class ExtendedFcpAdapter extends FcpAdapter {
389 /** The count down latch used to wait for completion. */
390 protected final CountDownLatch completionLatch = new CountDownLatch(1);
392 /** The FCP exception, if any. */
393 protected FcpException fcpException;
396 * Creates a new extended FCP adapter.
398 public ExtendedFcpAdapter() {
403 * Returns the FCP exception that occured. If no FCP exception occured,
404 * <code>null</code> is returned.
406 * @return The FCP exception that occured, or <code>null</code>
408 public FcpException getFcpException() {
413 * Waits for the completion of the command.
415 * @throws InterruptedException
416 * if {@link CountDownLatch#await()} is interrupted
418 public void complete() throws InterruptedException {
419 completionLatch.await();
426 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
427 fcpException = new FcpException("Connection closed", throwable);
428 completionLatch.countDown();
435 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
436 fcpException = new FcpException("Connection closed, duplicate client name");
437 completionLatch.countDown();
444 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
445 fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
446 completionLatch.countDown();