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.NodeHello;
39 import net.pterodactylus.fcp.NodeRef;
40 import net.pterodactylus.fcp.Peer;
41 import net.pterodactylus.fcp.ProtocolError;
44 * High-level FCP client that hides the details of the underlying FCP
47 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
49 public class FcpClient {
51 /** Object used for synchronization. */
52 private final Object syncObject = new Object();
54 /** The name of this client. */
55 private final String name;
57 /** The underlying FCP connection. */
58 private final FcpConnection fcpConnection;
61 * Creates an FCP client with the given name.
64 * The name of the FCP client
65 * @throws UnknownHostException
66 * if the hostname “localhost” is unknown
68 public FcpClient(String name) throws UnknownHostException {
69 this(name, "localhost");
73 * Creates an FCP client.
76 * The name of the FCP client
78 * The hostname of the Freenet node
79 * @throws UnknownHostException
80 * if the given hostname can not be resolved
82 public FcpClient(String name, String hostname) throws UnknownHostException {
83 this(name, hostname, FcpConnection.DEFAULT_PORT);
87 * Creates an FCP client.
90 * The name of the FCP client
92 * The hostname of the Freenet node
94 * The Freenet node’s FCP port
95 * @throws UnknownHostException
96 * if the given hostname can not be resolved
98 public FcpClient(String name, String hostname, int port) throws UnknownHostException {
99 this(name, InetAddress.getByName(hostname), port);
103 * Creates an FCP client.
106 * The name of the FCP client
108 * The host address of the Freenet node
110 public FcpClient(String name, InetAddress host) {
111 this(name, host, FcpConnection.DEFAULT_PORT);
115 * Creates an FCP client.
118 * The name of the FCP client
120 * The host address of the Freenet node
122 * The Freenet node’s FCP port
124 public FcpClient(String name, InetAddress host, int port) {
126 fcpConnection = new FcpConnection(host, port);
134 * Connects the FCP client.
136 * @throws IOException
137 * if an I/O error occurs
138 * @throws FcpException
139 * if an FCP error occurs
141 public void connect() throws IOException, FcpException {
142 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
148 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
149 completionLatch.countDown();
152 fcpConnection.addFcpListener(fcpListener);
154 fcpConnection.connect();
155 ClientHello clientHello = new ClientHello(name);
156 fcpConnection.sendMessage(clientHello);
159 fcpListener.complete();
161 } catch (InterruptedException e) {
162 /* ignore, we’ll loop. */
166 fcpConnection.removeFcpListener(fcpListener);
168 if (fcpListener.getFcpException() != null) {
169 throw fcpListener.getFcpException();
174 * Disconnects the FCP client.
176 public void disconnect() {
177 synchronized (syncObject) {
178 fcpConnection.close();
179 syncObject.notifyAll();
188 * Returns all peers that the node has.
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() throws IOException, FcpException {
197 final Set<Peer> peers = new HashSet<Peer>();
198 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
204 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
212 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
213 completionLatch.countDown();
216 fcpConnection.addFcpListener(fcpListener);
217 fcpConnection.sendMessage(new ListPeers("list-peers"));
221 fcpListener.complete();
223 } catch (InterruptedException e) {
224 /* ignore, we’ll loop. */
228 fcpConnection.removeFcpListener(fcpListener);
230 if (fcpListener.getFcpException() != null) {
231 throw fcpListener.getFcpException();
237 * Adds the given peer to the node.
241 * @throws IOException
242 * if an I/O error occurs
243 * @throws FcpException
244 * if an FCP error occurs
246 public void addPeer(Peer peer) throws IOException, FcpException {
247 addPeer(peer.getNodeRef());
251 * Adds the peer defined by the noderef to the node.
254 * The noderef that defines the new peer
255 * @throws IOException
256 * if an I/O error occurs
257 * @throws FcpException
258 * if an FCP error occurs
260 public void addPeer(NodeRef nodeRef) throws IOException, FcpException {
261 addPeer(new AddPeer(nodeRef));
265 * Adds a peer, reading the noderef from the given URL.
268 * The URL to read the noderef from
269 * @throws IOException
270 * if an I/O error occurs
271 * @throws FcpException
272 * if an FCP error occurs
274 public void addPeer(URL url) throws IOException, FcpException {
275 addPeer(new AddPeer(url));
279 * Adds a peer, reading the noderef of the peer from the given file.
280 * <strong>Note:</strong> the file to read the noderef from has to reside on
281 * the same machine as the node!
284 * The name of the file containing the peer’s noderef
285 * @throws IOException
286 * if an I/O error occurs
287 * @throws FcpException
288 * if an FCP error occurs
290 public void addPeer(String file) throws IOException, FcpException {
291 addPeer(new AddPeer(file));
295 * Sends the given {@link AddPeer} message to the node. This method should
296 * not be called directly. Use one of {@link #addPeer(Peer)},
297 * {@link #addPeer(NodeRef)}, {@link #addPeer(URL)}, or
298 * {@link #addPeer(String)} instead.
301 * The “AddPeer” message
302 * @throws IOException
303 * if an I/O error occurs
304 * @throws FcpException
305 * if an FCP error occurs
307 private void addPeer(AddPeer addPeer) throws IOException, FcpException {
308 ExtendedFcpAdapter fcpListener = new ExtendedFcpAdapter() {
314 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
315 completionLatch.countDown();
318 fcpConnection.addFcpListener(fcpListener);
320 fcpConnection.sendMessage(addPeer);
323 fcpListener.complete();
325 } catch (InterruptedException ie1) {
326 /* ignore, we’ll loop. */
330 fcpConnection.removeFcpListener(fcpListener);
332 if (fcpListener.getFcpException() != null) {
333 throw fcpListener.getFcpException();
338 * Implementation of an {@link FcpListener} that can store an
339 * {@link FcpException} and wait for the arrival of a certain command.
341 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
343 private static class ExtendedFcpAdapter extends FcpAdapter {
345 /** The count down latch used to wait for completion. */
346 protected final CountDownLatch completionLatch = new CountDownLatch(1);
348 /** The FCP exception, if any. */
349 protected FcpException fcpException;
352 * Creates a new extended FCP adapter.
354 public ExtendedFcpAdapter() {
359 * Returns the FCP exception that occured. If no FCP exception occured,
360 * <code>null</code> is returned.
362 * @return The FCP exception that occured, or <code>null</code>
364 public FcpException getFcpException() {
369 * Waits for the completion of the command.
371 * @throws InterruptedException
372 * if {@link CountDownLatch#await()} is interrupted
374 public void complete() throws InterruptedException {
375 completionLatch.await();
382 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
383 fcpException = new FcpException("Connection closed", throwable);
384 completionLatch.countDown();
391 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
392 fcpException = new FcpException("Connection closed, duplicate client name");
393 completionLatch.countDown();
400 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
401 fcpException = new FcpException("Protocol error (" + protocolError.getCode() + ", " + protocolError.getCodeDescription());
402 completionLatch.countDown();