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 @SuppressWarnings("synthetic-access")
150 public void run() throws IOException {
151 fcpConnection.connect();
152 ClientHello clientHello = new ClientHello(name);
153 fcpConnection.sendMessage(clientHello);
160 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
161 completionLatch.countDown();
164 fcpListener.execute();
168 * Disconnects the FCP client.
170 public void disconnect() {
171 synchronized (syncObject) {
172 fcpConnection.close();
173 syncObject.notifyAll();
182 * Returns all peers that the node has.
184 * @return A set containing the node’s peers
185 * @throws IOException
186 * if an I/O error occurs
187 * @throws FcpException
188 * if an FCP error occurs
190 public Set<Peer> getPeers() throws IOException, FcpException {
191 final Set<Peer> peers = new HashSet<Peer>();
192 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
198 @SuppressWarnings("synthetic-access")
199 public void run() throws IOException {
200 fcpConnection.sendMessage(new ListPeers("list-peers"));
207 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
215 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
216 completionLatch.countDown();
219 fcpListener.execute();
224 * Adds the given peer to the node.
228 * @throws IOException
229 * if an I/O error occurs
230 * @throws FcpException
231 * if an FCP error occurs
233 public void addPeer(Peer peer) throws IOException, FcpException {
234 addPeer(peer.getNodeRef());
238 * Adds the peer defined by the noderef to the node.
241 * The noderef that defines the new peer
242 * @throws IOException
243 * if an I/O error occurs
244 * @throws FcpException
245 * if an FCP error occurs
247 public void addPeer(NodeRef nodeRef) throws IOException, FcpException {
248 addPeer(new AddPeer(nodeRef));
252 * Adds a peer, reading the noderef from the given URL.
255 * The URL to read the noderef from
256 * @throws IOException
257 * if an I/O error occurs
258 * @throws FcpException
259 * if an FCP error occurs
261 public void addPeer(URL url) throws IOException, FcpException {
262 addPeer(new AddPeer(url));
266 * Adds a peer, reading the noderef of the peer from the given file.
267 * <strong>Note:</strong> the file to read the noderef from has to reside on
268 * the same machine as the node!
271 * The name of the file containing the peer’s noderef
272 * @throws IOException
273 * if an I/O error occurs
274 * @throws FcpException
275 * if an FCP error occurs
277 public void addPeer(String file) throws IOException, FcpException {
278 addPeer(new AddPeer(file));
282 * Sends the given {@link AddPeer} message to the node. This method should
283 * not be called directly. Use one of {@link #addPeer(Peer)},
284 * {@link #addPeer(NodeRef)}, {@link #addPeer(URL)}, or
285 * {@link #addPeer(String)} instead.
288 * The “AddPeer” message
289 * @throws IOException
290 * if an I/O error occurs
291 * @throws FcpException
292 * if an FCP error occurs
294 private void addPeer(final AddPeer addPeer) throws IOException, FcpException {
295 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
301 @SuppressWarnings("synthetic-access")
302 public void run() throws IOException {
303 fcpConnection.sendMessage(addPeer);
310 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
311 completionLatch.countDown();
314 fcpListener.execute();
318 * Modifies the given peer.
322 * @param allowLocalAddresses
323 * <code>true</code> to allow local address, <code>false</code>
324 * to not allow local address, <code>null</code> to not change
327 * <code>true</code> to disable the peer, <code>false</code> to
328 * enable the peer, <code>null</code> to not change the setting
330 * <code>true</code> to enable “listen only” for the peer,
331 * <code>false</code> to disable it, <code>null</code> to not
333 * @throws IOException
334 * if an I/O error occurs
335 * @throws FcpException
336 * if an FCP error occurs
338 public void modifyPeer(final Peer peer, final Boolean allowLocalAddresses, final Boolean disabled, final Boolean listenOnly) throws IOException, FcpException {
339 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
345 @SuppressWarnings("synthetic-access")
346 public void run() throws IOException {
347 fcpConnection.sendMessage(new ModifyPeer(peer.getIdentity(), allowLocalAddresses, disabled, listenOnly));
354 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
355 completionLatch.countDown();
358 fcpListener.execute();
362 * Implementation of an {@link FcpListener} that can store an
363 * {@link FcpException} and wait for the arrival of a certain command.
365 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
367 private abstract class ExtendedFcpAdapter extends FcpAdapter {
369 /** The count down latch used to wait for completion. */
370 protected final CountDownLatch completionLatch = new CountDownLatch(1);
372 /** The FCP exception, if any. */
373 protected FcpException fcpException;
376 * Creates a new extended FCP adapter.
378 public ExtendedFcpAdapter() {
383 * Executes the FCP commands in {@link #run()}, wrapping the execution
384 * and catching exceptions.
386 * @throws IOException
387 * if an I/O error occurs
388 * @throws FcpException
389 * if an FCP error occurs
391 @SuppressWarnings("synthetic-access")
392 public void execute() throws IOException, FcpException {
393 fcpConnection.addFcpListener(this);
398 completionLatch.await();
400 } catch (InterruptedException ie1) {
401 /* ignore, we’ll loop. */
405 fcpConnection.removeFcpListener(this);
407 if (fcpException != null) {
413 * The FCP commands that actually get executed.
415 * @throws IOException
416 * if an I/O error occurs
418 public abstract void run() throws IOException;
424 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
425 fcpException = new FcpException("Connection closed", throwable);
426 completionLatch.countDown();
433 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
434 fcpException = new FcpException("Connection closed, duplicate client name");
435 completionLatch.countDown();
442 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
443 fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
444 completionLatch.countDown();