2 * fcplib - HighLevelClient.java -
3 * Copyright © 2008 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;
24 import java.net.UnknownHostException;
25 import java.util.Collections;
26 import java.util.HashMap;
28 import java.util.Map.Entry;
30 import net.pterodactylus.fcp.AllData;
31 import net.pterodactylus.fcp.ClientHello;
32 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
33 import net.pterodactylus.fcp.ConfigData;
34 import net.pterodactylus.fcp.DataFound;
35 import net.pterodactylus.fcp.EndListPeerNotes;
36 import net.pterodactylus.fcp.EndListPeers;
37 import net.pterodactylus.fcp.EndListPersistentRequests;
38 import net.pterodactylus.fcp.FCPPluginReply;
39 import net.pterodactylus.fcp.FcpConnection;
40 import net.pterodactylus.fcp.FcpListener;
41 import net.pterodactylus.fcp.FcpMessage;
42 import net.pterodactylus.fcp.FinishedCompression;
43 import net.pterodactylus.fcp.GenerateSSK;
44 import net.pterodactylus.fcp.GetFailed;
45 import net.pterodactylus.fcp.IdentifierCollision;
46 import net.pterodactylus.fcp.ListPeers;
47 import net.pterodactylus.fcp.NodeData;
48 import net.pterodactylus.fcp.NodeHello;
49 import net.pterodactylus.fcp.Peer;
50 import net.pterodactylus.fcp.PeerNote;
51 import net.pterodactylus.fcp.PeerRemoved;
52 import net.pterodactylus.fcp.PersistentGet;
53 import net.pterodactylus.fcp.PersistentPut;
54 import net.pterodactylus.fcp.PersistentPutDir;
55 import net.pterodactylus.fcp.PersistentRequestModified;
56 import net.pterodactylus.fcp.PersistentRequestRemoved;
57 import net.pterodactylus.fcp.PluginInfo;
58 import net.pterodactylus.fcp.ProtocolError;
59 import net.pterodactylus.fcp.PutFailed;
60 import net.pterodactylus.fcp.PutFetchable;
61 import net.pterodactylus.fcp.PutSuccessful;
62 import net.pterodactylus.fcp.SSKKeypair;
63 import net.pterodactylus.fcp.SimpleProgress;
64 import net.pterodactylus.fcp.StartedCompression;
65 import net.pterodactylus.fcp.SubscribedUSKUpdate;
66 import net.pterodactylus.fcp.TestDDAComplete;
67 import net.pterodactylus.fcp.TestDDAReply;
68 import net.pterodactylus.fcp.URIGenerated;
69 import net.pterodactylus.fcp.UnknownNodeIdentifier;
70 import net.pterodactylus.fcp.UnknownPeerNoteType;
73 * A high-level client that allows simple yet full-featured access to a Freenet
76 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
79 public class HighLevelClient {
81 /** Object for internal synchronization. */
82 private final Object syncObject = new Object();
84 /** The name of the client. */
85 private final String clientName;
87 /** The address of the node. */
88 private InetAddress address;
90 /** The port number of the node. */
93 /** The FCP connection to the node. */
94 private FcpConnection fcpConnection;
96 /** The listener for the connection. */
97 private HighLevelClientFcpListener highLevelClientFcpListener = new HighLevelClientFcpListener();
99 /** The callback for {@link #connect()}. */
100 private HighLevelCallback<ConnectResult> connectCallback;
102 /** Mapping from request identifiers to callbacks. */
103 private Map<String, HighLevelCallback<KeyGenerationResult>> keyGenerationCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<KeyGenerationResult>>());
105 /** Mapping from request identifier to peer list callbacks. */
106 private Map<String, HighLevelCallback<PeerListResult>> peerListCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<PeerListResult>>());
109 * Creates a new high-level client that connects to a node on
110 * <code>localhost</code>.
113 * The name of the client
114 * @throws UnknownHostException
115 * if the hostname of the node can not be resolved.
117 public HighLevelClient(String clientName) throws UnknownHostException {
118 this(clientName, "localhost");
122 * Creates a new high-level client that connects to a node on the given
126 * The name of the client
128 * The hostname of the node
129 * @throws UnknownHostException
130 * if the hostname of the node can not be resolved.
132 public HighLevelClient(String clientName, String host) throws UnknownHostException {
133 this(clientName, host, FcpConnection.DEFAULT_PORT);
137 * Creates a new high-level client that connects to a node on the given
141 * The name of the client
143 * The hostname of the node
145 * The port number of the node
146 * @throws UnknownHostException
147 * if the hostname of the node can not be resolved.
149 public HighLevelClient(String clientName, String host, int port) throws UnknownHostException {
150 this(clientName, InetAddress.getByName(host), port);
154 * Creates a new high-level client that connects to a node at the given
158 * The name of the client
160 * The address of the node
162 * The port number of the node
164 public HighLevelClient(String clientName, InetAddress address, int port) {
165 this.clientName = clientName;
166 this.address = address;
179 * Connects the client.
181 * @return A callback with a connection result
182 * @throws IOException
183 * if an I/O error occurs communicating with the node
185 public HighLevelCallback<ConnectResult> connect() throws IOException {
186 fcpConnection = new FcpConnection(address, port);
187 fcpConnection.addFcpListener(highLevelClientFcpListener);
188 ClientHello clientHello = new ClientHello(clientName);
189 connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
190 fcpConnection.sendMessage(clientHello);
191 return connectCallback;
195 * Disconnects the client from the node.
197 public void disconnect() {
201 * Generates a new SSK keypair.
203 * @return A callback with the keypair
204 * @throws IOException
205 * if an I/O error occurs communicating with the node
207 public HighLevelCallback<KeyGenerationResult> generateKey() throws IOException {
208 String identifier = generateIdentifier("generateSSK");
209 GenerateSSK generateSSK = new GenerateSSK(identifier);
210 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = new HighLevelCallback<KeyGenerationResult>(new KeyGenerationResult());
211 keyGenerationCallbacks.put(identifier, keyGenerationCallback);
212 fcpConnection.sendMessage(generateSSK);
213 return keyGenerationCallback;
217 * Gets a list of all peers from the node.
219 * @return A callback with the peer list
220 * @throws IOException
221 * if an I/O error occurs with the node
223 public HighLevelCallback<PeerListResult> getPeers() throws IOException {
224 String identifier = generateIdentifier("listPeers");
225 ListPeers listPeers = new ListPeers(identifier, true, true);
226 HighLevelCallback<PeerListResult> peerListCallback = new HighLevelCallback<PeerListResult>(new PeerListResult());
227 peerListCallbacks.put(identifier, peerListCallback);
228 fcpConnection.sendMessage(listPeers);
229 return peerListCallback;
233 * Generates an identifier for the given function.
236 * The name of the function
237 * @return An identifier
239 private String generateIdentifier(String function) {
240 return "jFCPlib-" + function + "-" + System.currentTimeMillis();
244 * FCP listener for {@link HighLevelClient}.
246 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
249 private class HighLevelClientFcpListener implements FcpListener {
252 * Creates a new FCP listener for {@link HighLevelClient}.
254 HighLevelClientFcpListener() {
262 * Searches all callback collections for a callback with the given
263 * identifier and cancels it.
266 * The identifier to search for, or <code>null</code> to
267 * cancel all pending requests
269 @SuppressWarnings("synthetic-access")
270 private void cancelIdentifier(String identifier) {
271 synchronized (syncObject) {
272 if (connectCallback != null) {
273 connectCallback.getIntermediaryResult().setFailed(true);
274 connectCallback.setDone();
275 connectCallback = null;
278 if (identifier == null) {
279 /* key generation callbacks */
280 for (Entry<String, HighLevelCallback<KeyGenerationResult>> keyGenerationEntry: keyGenerationCallbacks.entrySet()) {
281 keyGenerationEntry.getValue().getIntermediaryResult().setFailed(true);
282 keyGenerationEntry.getValue().setDone();
284 keyGenerationCallbacks.clear();
285 /* peer list callbacks. */
286 for (Entry<String, HighLevelCallback<PeerListResult>> peerListEntry: peerListCallbacks.entrySet()) {
287 peerListEntry.getValue().getIntermediaryResult().setFailed(true);
288 peerListEntry.getValue().setDone();
290 peerListCallbacks.clear();
292 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(identifier);
293 if (keyGenerationCallback != null) {
294 keyGenerationCallback.getIntermediaryResult().setFailed(true);
295 keyGenerationCallback.setDone();
298 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
299 if (peerListCallback != null) {
300 peerListCallback.getIntermediaryResult().setFailed(true);
301 peerListCallback.setDone();
308 // INTERFACE FcpListener
312 * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection)
314 @SuppressWarnings("synthetic-access")
315 public void connectionClosed(FcpConnection fcpConnection) {
316 if (fcpConnection != HighLevelClient.this.fcpConnection) {
319 cancelIdentifier(null);
323 * @see net.pterodactylus.fcp.FcpListener#receivedAllData(net.pterodactylus.fcp.FcpConnection,
324 * net.pterodactylus.fcp.AllData)
326 public void receivedAllData(FcpConnection fcpConnection, AllData allData) {
330 * @see net.pterodactylus.fcp.FcpListener#receivedCloseConnectionDuplicateClientName(net.pterodactylus.fcp.FcpConnection,
331 * net.pterodactylus.fcp.CloseConnectionDuplicateClientName)
333 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
337 * @see net.pterodactylus.fcp.FcpListener#receivedConfigData(net.pterodactylus.fcp.FcpConnection,
338 * net.pterodactylus.fcp.ConfigData)
340 public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) {
344 * @see net.pterodactylus.fcp.FcpListener#receivedDataFound(net.pterodactylus.fcp.FcpConnection,
345 * net.pterodactylus.fcp.DataFound)
347 public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) {
351 * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeerNotes(net.pterodactylus.fcp.FcpConnection,
352 * net.pterodactylus.fcp.EndListPeerNotes)
354 public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) {
358 * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.fcp.FcpConnection,
359 * net.pterodactylus.fcp.EndListPeers)
361 @SuppressWarnings("synthetic-access")
362 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
363 if (fcpConnection != HighLevelClient.this.fcpConnection) {
366 String identifier = endListPeers.getIdentifier();
367 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
368 if (peerListCallback == null) {
371 peerListCallback.setDone();
375 * @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
376 * net.pterodactylus.fcp.EndListPersistentRequests)
378 public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
382 * @see net.pterodactylus.fcp.FcpListener#receivedFCPPluginReply(net.pterodactylus.fcp.FcpConnection,
383 * net.pterodactylus.fcp.FCPPluginReply)
385 public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
389 * @see net.pterodactylus.fcp.FcpListener#receivedGetFailed(net.pterodactylus.fcp.FcpConnection,
390 * net.pterodactylus.fcp.GetFailed)
392 public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) {
396 * @see net.pterodactylus.fcp.FcpListener#receivedIdentifierCollision(net.pterodactylus.fcp.FcpConnection,
397 * net.pterodactylus.fcp.IdentifierCollision)
399 public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) {
403 * @see net.pterodactylus.fcp.FcpListener#receivedMessage(net.pterodactylus.fcp.FcpConnection,
404 * net.pterodactylus.fcp.FcpMessage)
406 public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
410 * @see net.pterodactylus.fcp.FcpListener#receivedNodeData(net.pterodactylus.fcp.FcpConnection,
411 * net.pterodactylus.fcp.NodeData)
413 public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) {
417 * @see net.pterodactylus.fcp.FcpListener#receivedNodeHello(net.pterodactylus.fcp.FcpConnection,
418 * net.pterodactylus.fcp.NodeHello)
420 @SuppressWarnings("synthetic-access")
421 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
422 if (fcpConnection != HighLevelClient.this.fcpConnection) {
425 synchronized (syncObject) {
426 connectCallback.getIntermediaryResult().setFailed(false);
427 connectCallback.setDone();
428 connectCallback = null;
433 * @see net.pterodactylus.fcp.FcpListener#receivedPeer(net.pterodactylus.fcp.FcpConnection,
434 * net.pterodactylus.fcp.Peer)
436 @SuppressWarnings("synthetic-access")
437 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
438 if (fcpConnection != HighLevelClient.this.fcpConnection) {
441 String identifier = peer.getIdentifier();
442 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.get(identifier);
443 if (peerListCallback == null) {
446 peerListCallback.getIntermediaryResult().addPeer(peer);
450 * @see net.pterodactylus.fcp.FcpListener#receivedPeerNote(net.pterodactylus.fcp.FcpConnection,
451 * net.pterodactylus.fcp.PeerNote)
453 public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) {
457 * @see net.pterodactylus.fcp.FcpListener#receivedPeerRemoved(net.pterodactylus.fcp.FcpConnection,
458 * net.pterodactylus.fcp.PeerRemoved)
460 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
464 * @see net.pterodactylus.fcp.FcpListener#receivedPersistentGet(net.pterodactylus.fcp.FcpConnection,
465 * net.pterodactylus.fcp.PersistentGet)
467 public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) {
471 * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
472 * net.pterodactylus.fcp.PersistentPut)
474 public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
478 * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
479 * net.pterodactylus.fcp.PersistentPutDir)
481 public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
485 * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestModified(net.pterodactylus.fcp.FcpConnection,
486 * net.pterodactylus.fcp.PersistentRequestModified)
488 public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) {
492 * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestRemoved(net.pterodactylus.fcp.FcpConnection,
493 * net.pterodactylus.fcp.PersistentRequestRemoved)
495 public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) {
499 * @see net.pterodactylus.fcp.FcpListener#receivedPluginInfo(net.pterodactylus.fcp.FcpConnection,
500 * net.pterodactylus.fcp.PluginInfo)
502 public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) {
506 * @see net.pterodactylus.fcp.FcpListener#receivedProtocolError(net.pterodactylus.fcp.FcpConnection,
507 * net.pterodactylus.fcp.ProtocolError)
509 @SuppressWarnings("synthetic-access")
510 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
511 if (fcpConnection != HighLevelClient.this.fcpConnection) {
514 String identifier = protocolError.getIdentifier();
515 if (identifier == null) {
518 cancelIdentifier(identifier);
522 * @see net.pterodactylus.fcp.FcpListener#receivedPutFailed(net.pterodactylus.fcp.FcpConnection,
523 * net.pterodactylus.fcp.PutFailed)
525 public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) {
529 * @see net.pterodactylus.fcp.FcpListener#receivedPutFetchable(net.pterodactylus.fcp.FcpConnection,
530 * net.pterodactylus.fcp.PutFetchable)
532 public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) {
536 * @see net.pterodactylus.fcp.FcpListener#receivedPutSuccessful(net.pterodactylus.fcp.FcpConnection,
537 * net.pterodactylus.fcp.PutSuccessful)
539 public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) {
543 * @see net.pterodactylus.fcp.FcpListener#receivedSSKKeypair(net.pterodactylus.fcp.FcpConnection,
544 * net.pterodactylus.fcp.SSKKeypair)
546 @SuppressWarnings("synthetic-access")
547 public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) {
548 if (fcpConnection != HighLevelClient.this.fcpConnection) {
551 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(sskKeypair.getIdentifier());
552 if (keyGenerationCallback == null) {
555 KeyGenerationResult keyGenerationResult = keyGenerationCallback.getIntermediaryResult();
556 keyGenerationResult.setInsertURI(sskKeypair.getInsertURI());
557 keyGenerationResult.setRequestURI(sskKeypair.getRequestURI());
558 keyGenerationCallback.setDone();
562 * @see net.pterodactylus.fcp.FcpListener#receivedSimpleProgress(net.pterodactylus.fcp.FcpConnection,
563 * net.pterodactylus.fcp.SimpleProgress)
565 public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) {
569 * @see net.pterodactylus.fcp.FcpListener#receivedStartedCompression(net.pterodactylus.fcp.FcpConnection,
570 * net.pterodactylus.fcp.StartedCompression)
572 public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) {
576 * @see net.pterodactylus.fcp.FcpListener#receivedSubscribedUSKUpdate(net.pterodactylus.fcp.FcpConnection,
577 * net.pterodactylus.fcp.SubscribedUSKUpdate)
579 public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) {
583 * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAComplete(net.pterodactylus.fcp.FcpConnection,
584 * net.pterodactylus.fcp.TestDDAComplete)
586 public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) {
590 * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAReply(net.pterodactylus.fcp.FcpConnection,
591 * net.pterodactylus.fcp.TestDDAReply)
593 public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) {
597 * @see net.pterodactylus.fcp.FcpListener#receivedURIGenerated(net.pterodactylus.fcp.FcpConnection,
598 * net.pterodactylus.fcp.URIGenerated)
600 public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) {
604 * @see net.pterodactylus.fcp.FcpListener#receivedUnknownNodeIdentifier(net.pterodactylus.fcp.FcpConnection,
605 * net.pterodactylus.fcp.UnknownNodeIdentifier)
607 public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) {
611 * @see net.pterodactylus.fcp.FcpListener#receivedUnknownPeerNoteType(net.pterodactylus.fcp.FcpConnection,
612 * net.pterodactylus.fcp.UnknownPeerNoteType)
614 public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) {
618 * @see net.pterodactylus.fcp.FcpListener#receviedFinishedCompression(net.pterodactylus.fcp.FcpConnection,
619 * net.pterodactylus.fcp.FinishedCompression)
621 public void receviedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) {