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.PeerRemoved;
43 import net.pterodactylus.fcp.ProtocolError;
44 import net.pterodactylus.fcp.RemovePeer;
47 * High-level FCP client that hides the details of the underlying FCP
50 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
52 public class FcpClient {
54 /** Object used for synchronization. */
55 private final Object syncObject = new Object();
57 /** The name of this client. */
58 private final String name;
60 /** The underlying FCP connection. */
61 private final FcpConnection fcpConnection;
64 * Creates an FCP client with the given name.
67 * The name of the FCP client
68 * @throws UnknownHostException
69 * if the hostname “localhost” is unknown
71 public FcpClient(String name) throws UnknownHostException {
72 this(name, "localhost");
76 * Creates an FCP client.
79 * The name of the FCP client
81 * The hostname of the Freenet node
82 * @throws UnknownHostException
83 * if the given hostname can not be resolved
85 public FcpClient(String name, String hostname) throws UnknownHostException {
86 this(name, hostname, FcpConnection.DEFAULT_PORT);
90 * Creates an FCP client.
93 * The name of the FCP client
95 * The hostname of the Freenet node
97 * The Freenet node’s FCP port
98 * @throws UnknownHostException
99 * if the given hostname can not be resolved
101 public FcpClient(String name, String hostname, int port) throws UnknownHostException {
102 this(name, InetAddress.getByName(hostname), port);
106 * Creates an FCP client.
109 * The name of the FCP client
111 * The host address of the Freenet node
113 public FcpClient(String name, InetAddress host) {
114 this(name, host, FcpConnection.DEFAULT_PORT);
118 * Creates an FCP client.
121 * The name of the FCP client
123 * The host address of the Freenet node
125 * The Freenet node’s FCP port
127 public FcpClient(String name, InetAddress host, int port) {
129 fcpConnection = new FcpConnection(host, port);
137 * Connects the FCP client.
139 * @throws IOException
140 * if an I/O error occurs
141 * @throws FcpException
142 * if an FCP error occurs
144 public void connect() throws IOException, FcpException {
145 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
151 @SuppressWarnings("synthetic-access")
152 public void run() throws IOException {
153 fcpConnection.connect();
154 ClientHello clientHello = new ClientHello(name);
155 fcpConnection.sendMessage(clientHello);
162 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
163 completionLatch.countDown();
166 fcpListener.execute();
170 * Disconnects the FCP client.
172 public void disconnect() {
173 synchronized (syncObject) {
174 fcpConnection.close();
175 syncObject.notifyAll();
184 * Returns all peers that the node has.
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
192 public Set<Peer> getPeers() throws IOException, FcpException {
193 final Set<Peer> peers = new HashSet<Peer>();
194 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
200 @SuppressWarnings("synthetic-access")
201 public void run() throws IOException {
202 fcpConnection.sendMessage(new ListPeers("list-peers"));
209 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
217 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
218 completionLatch.countDown();
221 fcpListener.execute();
226 * Adds the given peer to the node.
230 * @throws IOException
231 * if an I/O error occurs
232 * @throws FcpException
233 * if an FCP error occurs
235 public void addPeer(Peer peer) throws IOException, FcpException {
236 addPeer(peer.getNodeRef());
240 * Adds the peer defined by the noderef to the node.
243 * The noderef that defines the new peer
244 * @throws IOException
245 * if an I/O error occurs
246 * @throws FcpException
247 * if an FCP error occurs
249 public void addPeer(NodeRef nodeRef) throws IOException, FcpException {
250 addPeer(new AddPeer(nodeRef));
254 * Adds a peer, reading the noderef from the given URL.
257 * The URL to read the noderef from
258 * @throws IOException
259 * if an I/O error occurs
260 * @throws FcpException
261 * if an FCP error occurs
263 public void addPeer(URL url) throws IOException, FcpException {
264 addPeer(new AddPeer(url));
268 * Adds a peer, reading the noderef of the peer from the given file.
269 * <strong>Note:</strong> the file to read the noderef from has to reside on
270 * the same machine as the node!
273 * The name of the file containing the peer’s noderef
274 * @throws IOException
275 * if an I/O error occurs
276 * @throws FcpException
277 * if an FCP error occurs
279 public void addPeer(String file) throws IOException, FcpException {
280 addPeer(new AddPeer(file));
284 * Sends the given {@link AddPeer} message to the node. This method should
285 * not be called directly. Use one of {@link #addPeer(Peer)},
286 * {@link #addPeer(NodeRef)}, {@link #addPeer(URL)}, or
287 * {@link #addPeer(String)} instead.
290 * The “AddPeer” message
291 * @throws IOException
292 * if an I/O error occurs
293 * @throws FcpException
294 * if an FCP error occurs
296 private void addPeer(final AddPeer addPeer) throws IOException, FcpException {
297 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
303 @SuppressWarnings("synthetic-access")
304 public void run() throws IOException {
305 fcpConnection.sendMessage(addPeer);
312 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
313 completionLatch.countDown();
316 fcpListener.execute();
320 * Modifies the given peer.
324 * @param allowLocalAddresses
325 * <code>true</code> to allow local address, <code>false</code>
326 * to not allow local address, <code>null</code> to not change
329 * <code>true</code> to disable the peer, <code>false</code> to
330 * enable the peer, <code>null</code> to not change the setting
332 * <code>true</code> to enable “listen only” for the peer,
333 * <code>false</code> to disable it, <code>null</code> to not
335 * @throws IOException
336 * if an I/O error occurs
337 * @throws FcpException
338 * if an FCP error occurs
340 public void modifyPeer(final Peer peer, final Boolean allowLocalAddresses, final Boolean disabled, final Boolean listenOnly) throws IOException, FcpException {
341 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
347 @SuppressWarnings("synthetic-access")
348 public void run() throws IOException {
349 fcpConnection.sendMessage(new ModifyPeer(peer.getIdentity(), allowLocalAddresses, disabled, listenOnly));
356 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
357 completionLatch.countDown();
360 fcpListener.execute();
364 * Removes the given peer.
368 * @throws IOException
369 * if an I/O error occurs
370 * @throws FcpException
371 * if an FCP error occurs
373 public void removePeer(final Peer peer) throws IOException, FcpException {
374 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
380 @SuppressWarnings("synthetic-access")
381 public void run() throws IOException {
382 fcpConnection.sendMessage(new RemovePeer(peer.getIdentity()));
389 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
390 completionLatch.countDown();
393 fcpListener.execute();
397 * Implementation of an {@link FcpListener} that can store an
398 * {@link FcpException} and wait for the arrival of a certain command.
400 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
402 private abstract class ExtendedFcpAdapter extends FcpAdapter {
404 /** The count down latch used to wait for completion. */
405 protected final CountDownLatch completionLatch = new CountDownLatch(1);
407 /** The FCP exception, if any. */
408 protected FcpException fcpException;
411 * Creates a new extended FCP adapter.
413 public ExtendedFcpAdapter() {
418 * Executes the FCP commands in {@link #run()}, wrapping the execution
419 * and catching exceptions.
421 * @throws IOException
422 * if an I/O error occurs
423 * @throws FcpException
424 * if an FCP error occurs
426 @SuppressWarnings("synthetic-access")
427 public void execute() throws IOException, FcpException {
428 fcpConnection.addFcpListener(this);
433 completionLatch.await();
435 } catch (InterruptedException ie1) {
436 /* ignore, we’ll loop. */
440 fcpConnection.removeFcpListener(this);
442 if (fcpException != null) {
448 * The FCP commands that actually get executed.
450 * @throws IOException
451 * if an I/O error occurs
453 public abstract void run() throws IOException;
459 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
460 fcpException = new FcpException("Connection closed", throwable);
461 completionLatch.countDown();
468 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
469 fcpException = new FcpException("Connection closed, duplicate client name");
470 completionLatch.countDown();
477 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
478 fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
479 completionLatch.countDown();