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 * @param withMetadata
187 * <code>true</code> to include peer metadata
188 * @param withVolatile
189 * <code>true</code> to include volatile peer data
190 * @return A set containing the node’s peers
191 * @throws IOException
192 * if an I/O error occurs
193 * @throws FcpException
194 * if an FCP error occurs
196 public Set<Peer> getPeers(final boolean withMetadata, final boolean withVolatile) throws IOException, FcpException {
197 final Set<Peer> peers = new HashSet<Peer>();
198 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
204 @SuppressWarnings("synthetic-access")
205 public void run() throws IOException {
206 fcpConnection.sendMessage(new ListPeers("list-peers", withMetadata, withVolatile));
213 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
221 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
222 completionLatch.countDown();
225 fcpListener.execute();
230 * Adds the given peer to the node.
234 * @throws IOException
235 * if an I/O error occurs
236 * @throws FcpException
237 * if an FCP error occurs
239 public void addPeer(Peer peer) throws IOException, FcpException {
240 addPeer(peer.getNodeRef());
244 * Adds the peer defined by the noderef to the node.
247 * The noderef that defines the new peer
248 * @throws IOException
249 * if an I/O error occurs
250 * @throws FcpException
251 * if an FCP error occurs
253 public void addPeer(NodeRef nodeRef) throws IOException, FcpException {
254 addPeer(new AddPeer(nodeRef));
258 * Adds a peer, reading the noderef from the given URL.
261 * The URL to read the noderef from
262 * @throws IOException
263 * if an I/O error occurs
264 * @throws FcpException
265 * if an FCP error occurs
267 public void addPeer(URL url) throws IOException, FcpException {
268 addPeer(new AddPeer(url));
272 * Adds a peer, reading the noderef of the peer from the given file.
273 * <strong>Note:</strong> the file to read the noderef from has to reside on
274 * the same machine as the node!
277 * The name of the file containing the peer’s noderef
278 * @throws IOException
279 * if an I/O error occurs
280 * @throws FcpException
281 * if an FCP error occurs
283 public void addPeer(String file) throws IOException, FcpException {
284 addPeer(new AddPeer(file));
288 * Sends the given {@link AddPeer} message to the node. This method should
289 * not be called directly. Use one of {@link #addPeer(Peer)},
290 * {@link #addPeer(NodeRef)}, {@link #addPeer(URL)}, or
291 * {@link #addPeer(String)} instead.
294 * The “AddPeer” message
295 * @throws IOException
296 * if an I/O error occurs
297 * @throws FcpException
298 * if an FCP error occurs
300 private void addPeer(final AddPeer addPeer) throws IOException, FcpException {
301 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
307 @SuppressWarnings("synthetic-access")
308 public void run() throws IOException {
309 fcpConnection.sendMessage(addPeer);
316 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
317 completionLatch.countDown();
320 fcpListener.execute();
324 * Modifies the given peer.
328 * @param allowLocalAddresses
329 * <code>true</code> to allow local address, <code>false</code>
330 * to not allow local address, <code>null</code> to not change
333 * <code>true</code> to disable the peer, <code>false</code> to
334 * enable the peer, <code>null</code> to not change the setting
336 * <code>true</code> to enable “listen only” for the peer,
337 * <code>false</code> to disable it, <code>null</code> to not
339 * @throws IOException
340 * if an I/O error occurs
341 * @throws FcpException
342 * if an FCP error occurs
344 public void modifyPeer(final Peer peer, final Boolean allowLocalAddresses, final Boolean disabled, final Boolean listenOnly) throws IOException, FcpException {
345 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
351 @SuppressWarnings("synthetic-access")
352 public void run() throws IOException {
353 fcpConnection.sendMessage(new ModifyPeer(peer.getIdentity(), allowLocalAddresses, disabled, listenOnly));
360 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
361 completionLatch.countDown();
364 fcpListener.execute();
368 * Removes the given peer.
372 * @throws IOException
373 * if an I/O error occurs
374 * @throws FcpException
375 * if an FCP error occurs
377 public void removePeer(final Peer peer) throws IOException, FcpException {
378 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
384 @SuppressWarnings("synthetic-access")
385 public void run() throws IOException {
386 fcpConnection.sendMessage(new RemovePeer(peer.getIdentity()));
393 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
394 completionLatch.countDown();
397 fcpListener.execute();
401 * Implementation of an {@link FcpListener} that can store an
402 * {@link FcpException} and wait for the arrival of a certain command.
404 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
406 private abstract class ExtendedFcpAdapter extends FcpAdapter {
408 /** The count down latch used to wait for completion. */
409 protected final CountDownLatch completionLatch = new CountDownLatch(1);
411 /** The FCP exception, if any. */
412 protected FcpException fcpException;
415 * Creates a new extended FCP adapter.
417 public ExtendedFcpAdapter() {
422 * Executes the FCP commands in {@link #run()}, wrapping the execution
423 * and catching exceptions.
425 * @throws IOException
426 * if an I/O error occurs
427 * @throws FcpException
428 * if an FCP error occurs
430 @SuppressWarnings("synthetic-access")
431 public void execute() throws IOException, FcpException {
432 fcpConnection.addFcpListener(this);
437 completionLatch.await();
439 } catch (InterruptedException ie1) {
440 /* ignore, we’ll loop. */
444 fcpConnection.removeFcpListener(this);
446 if (fcpException != null) {
452 * The FCP commands that actually get executed.
454 * @throws IOException
455 * if an I/O error occurs
457 public abstract void run() throws IOException;
463 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
464 fcpException = new FcpException("Connection closed", throwable);
465 completionLatch.countDown();
472 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
473 fcpException = new FcpException("Connection closed, duplicate client name");
474 completionLatch.countDown();
481 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
482 fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
483 completionLatch.countDown();