hand reason for disconnects to listeners
[jFCPlib.git] / src / net / pterodactylus / fcp / highlevel / HighLevelClient.java
1 /*
2  * fcplib - HighLevelClient.java -
3  * Copyright © 2008 David Roden
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 package net.pterodactylus.fcp.highlevel;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileReader;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.net.InetAddress;
28 import java.net.URL;
29 import java.net.UnknownHostException;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.logging.Logger;
37
38 import net.pterodactylus.fcp.AddPeer;
39 import net.pterodactylus.fcp.AllData;
40 import net.pterodactylus.fcp.ClientGet;
41 import net.pterodactylus.fcp.ClientHello;
42 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
43 import net.pterodactylus.fcp.ConfigData;
44 import net.pterodactylus.fcp.DataFound;
45 import net.pterodactylus.fcp.EndListPeerNotes;
46 import net.pterodactylus.fcp.EndListPeers;
47 import net.pterodactylus.fcp.EndListPersistentRequests;
48 import net.pterodactylus.fcp.FCPPluginReply;
49 import net.pterodactylus.fcp.FcpConnection;
50 import net.pterodactylus.fcp.FcpListener;
51 import net.pterodactylus.fcp.FcpMessage;
52 import net.pterodactylus.fcp.FcpUtils;
53 import net.pterodactylus.fcp.FinishedCompression;
54 import net.pterodactylus.fcp.GenerateSSK;
55 import net.pterodactylus.fcp.GetFailed;
56 import net.pterodactylus.fcp.IdentifierCollision;
57 import net.pterodactylus.fcp.ListPeers;
58 import net.pterodactylus.fcp.NodeData;
59 import net.pterodactylus.fcp.NodeHello;
60 import net.pterodactylus.fcp.NodeRef;
61 import net.pterodactylus.fcp.Peer;
62 import net.pterodactylus.fcp.PeerNote;
63 import net.pterodactylus.fcp.PeerRemoved;
64 import net.pterodactylus.fcp.PersistentGet;
65 import net.pterodactylus.fcp.PersistentPut;
66 import net.pterodactylus.fcp.PersistentPutDir;
67 import net.pterodactylus.fcp.PersistentRequestModified;
68 import net.pterodactylus.fcp.PersistentRequestRemoved;
69 import net.pterodactylus.fcp.PluginInfo;
70 import net.pterodactylus.fcp.ProtocolError;
71 import net.pterodactylus.fcp.PutFailed;
72 import net.pterodactylus.fcp.PutFetchable;
73 import net.pterodactylus.fcp.PutSuccessful;
74 import net.pterodactylus.fcp.ReturnType;
75 import net.pterodactylus.fcp.SSKKeypair;
76 import net.pterodactylus.fcp.SimpleProgress;
77 import net.pterodactylus.fcp.StartedCompression;
78 import net.pterodactylus.fcp.SubscribedUSKUpdate;
79 import net.pterodactylus.fcp.TestDDAComplete;
80 import net.pterodactylus.fcp.TestDDAReply;
81 import net.pterodactylus.fcp.TestDDARequest;
82 import net.pterodactylus.fcp.TestDDAResponse;
83 import net.pterodactylus.fcp.URIGenerated;
84 import net.pterodactylus.fcp.UnknownNodeIdentifier;
85 import net.pterodactylus.fcp.UnknownPeerNoteType;
86
87 /**
88  * A high-level client that allows simple yet full-featured access to a Freenet
89  * node.
90  *
91  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
92  * @version $Id$
93  */
94 public class HighLevelClient {
95
96         /** Logger. */
97         private static final Logger logger = Logger.getLogger(HighLevelClient.class.getName());
98
99         /** Object for internal synchronization. */
100         private final Object syncObject = new Object();
101
102         /** The name of the client. */
103         private final String clientName;
104
105         /** The address of the node. */
106         private InetAddress address;
107
108         /** The port number of the node. */
109         private int port;
110
111         /** The FCP connection to the node. */
112         private FcpConnection fcpConnection;
113
114         /** Listeners for high-level client events. */
115         private List<HighLevelClientListener> highLevelClientListeners = Collections.synchronizedList(new ArrayList<HighLevelClientListener>());
116
117         /** The listener for the connection. */
118         private HighLevelClientFcpListener highLevelClientFcpListener = new HighLevelClientFcpListener();
119
120         /** The callback for {@link #connect()}. */
121         private HighLevelCallback<ConnectResult> connectCallback;
122
123         /** Mapping from request identifiers to callbacks. */
124         private Map<String, HighLevelCallback<KeyGenerationResult>> keyGenerationCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<KeyGenerationResult>>());
125
126         /** Mapping from request identifier to peer list callbacks. */
127         private Map<String, HighLevelCallback<PeerListResult>> peerListCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<PeerListResult>>());
128
129         /** Mapping from request identifier to peer callbacks. */
130         private Map<String, HighLevelCallback<PeerResult>> peerCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<PeerResult>>());
131
132         /** Mapping from directories to DDA callbacks. */
133         private Map<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<DirectDiskAccessResult>>());
134
135         /** Mapping from request identifiers to download callbacks. */
136         private Map<String, HighLevelProgressCallback<DownloadResult>> downloadCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelProgressCallback<DownloadResult>>());
137
138         /**
139          * Creates a new high-level client that connects to a node on
140          * <code>localhost</code>.
141          *
142          * @param clientName
143          *            The name of the client
144          * @throws UnknownHostException
145          *             if the hostname of the node can not be resolved.
146          */
147         public HighLevelClient(String clientName) throws UnknownHostException {
148                 this(clientName, "localhost");
149         }
150
151         /**
152          * Creates a new high-level client that connects to a node on the given
153          * host.
154          *
155          * @param clientName
156          *            The name of the client
157          * @param host
158          *            The hostname of the node
159          * @throws UnknownHostException
160          *             if the hostname of the node can not be resolved.
161          */
162         public HighLevelClient(String clientName, String host) throws UnknownHostException {
163                 this(clientName, host, FcpConnection.DEFAULT_PORT);
164         }
165
166         /**
167          * Creates a new high-level client that connects to a node on the given
168          * host.
169          *
170          * @param clientName
171          *            The name of the client
172          * @param host
173          *            The hostname of the node
174          * @param port
175          *            The port number of the node
176          * @throws UnknownHostException
177          *             if the hostname of the node can not be resolved.
178          */
179         public HighLevelClient(String clientName, String host, int port) throws UnknownHostException {
180                 this(clientName, InetAddress.getByName(host), port);
181         }
182
183         /**
184          * Creates a new high-level client that connects to a node at the given
185          * address.
186          *
187          * @param clientName
188          *            The name of the client
189          * @param address
190          *            The address of the node
191          * @param port
192          *            The port number of the node
193          */
194         public HighLevelClient(String clientName, InetAddress address, int port) {
195                 this.clientName = clientName;
196                 this.address = address;
197                 this.port = port;
198         }
199
200         //
201         // EVENT MANAGEMENT
202         //
203
204         /**
205          * Adds the given high-level client listener to list of listeners.
206          *
207          * @param highLevelClientListener
208          *            The listener to add
209          */
210         public void addHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
211                 highLevelClientListeners.add(highLevelClientListener);
212         }
213
214         /**
215          * Removes the given high-level client listener from the list of listeners.
216          *
217          * @param highLevelClientListener
218          *            The listener to remove
219          */
220         public void removeHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
221                 highLevelClientListeners.remove(highLevelClientListener);
222         }
223
224         /**
225          * Notifies all listeners that a client has connected.
226          */
227         private void fireClientConnected() {
228                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
229                         highLevelClientListener.clientConnected(this);
230                 }
231         }
232
233         /**
234          * Notifies all listeners that a client has disconnected.
235          *
236          * @param throwable
237          *            The exception that caused the disconnect, or <code>null</code>
238          *            if there was no exception
239          */
240         private void fireClientDisconnected(Throwable throwable) {
241                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
242                         highLevelClientListener.clientDisconnected(this, throwable);
243                 }
244         }
245
246         //
247         // ACCESSORS
248         //
249
250         /**
251          * Returns the FCP connection that backs this high-level client. This method
252          * should be used with care as fiddling around with the FCP connection can
253          * easily break the high-level client if you don’t know what you’re doing!
254          *
255          * @return The FCP connection of this client
256          */
257         public FcpConnection getFcpConnection() {
258                 return fcpConnection;
259         }
260
261         //
262         // ACTIONS
263         //
264
265         /**
266          * Connects the client.
267          *
268          * @return A callback with a connection result
269          * @throws IOException
270          *             if an I/O error occurs communicating with the node
271          */
272         public HighLevelCallback<ConnectResult> connect() throws IOException {
273                 fcpConnection = new FcpConnection(address, port);
274                 fcpConnection.addFcpListener(highLevelClientFcpListener);
275                 ClientHello clientHello = new ClientHello(clientName);
276                 connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
277                 fcpConnection.sendMessage(clientHello);
278                 return connectCallback;
279         }
280
281         /**
282          * Disconnects the client from the node.
283          */
284         public void disconnect() {
285                 disconnect(null);
286         }
287
288         /**
289          * Generates a new SSK keypair.
290          *
291          * @return A callback with the keypair
292          * @throws IOException
293          *             if an I/O error occurs communicating with the node
294          */
295         public HighLevelCallback<KeyGenerationResult> generateKey() throws IOException {
296                 String identifier = generateIdentifier("generateSSK");
297                 GenerateSSK generateSSK = new GenerateSSK(identifier);
298                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = new HighLevelCallback<KeyGenerationResult>(new KeyGenerationResult(identifier));
299                 keyGenerationCallbacks.put(identifier, keyGenerationCallback);
300                 fcpConnection.sendMessage(generateSSK);
301                 return keyGenerationCallback;
302         }
303
304         /**
305          * Gets a list of all peers from the node.
306          *
307          * @return A callback with the peer list
308          * @throws IOException
309          *             if an I/O error occurs with the node
310          */
311         public HighLevelCallback<PeerListResult> getPeers() throws IOException {
312                 String identifier = generateIdentifier("listPeers");
313                 ListPeers listPeers = new ListPeers(identifier, true, true);
314                 HighLevelCallback<PeerListResult> peerListCallback = new HighLevelCallback<PeerListResult>(new PeerListResult(identifier));
315                 peerListCallbacks.put(identifier, peerListCallback);
316                 fcpConnection.sendMessage(listPeers);
317                 return peerListCallback;
318         }
319
320         /**
321          * Adds the peer whose noderef is stored in the given file.
322          *
323          * @param nodeRefFile
324          *            The name of the file the peer’s noderef is stored in
325          * @return A peer callback
326          * @throws IOException
327          *             if an I/O error occurs communicating with the node
328          */
329         public HighLevelCallback<PeerResult> addPeer(String nodeRefFile) throws IOException {
330                 String identifier = generateIdentifier("addPeer");
331                 AddPeer addPeer = new AddPeer(nodeRefFile);
332                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
333                 peerCallbacks.put(identifier, peerCallback);
334                 fcpConnection.sendMessage(addPeer);
335                 return peerCallback;
336         }
337
338         /**
339          * Adds the peer whose noderef is stored in the given file.
340          *
341          * @param nodeRefURL
342          *            The URL where the peer’s noderef is stored
343          * @return A peer callback
344          * @throws IOException
345          *             if an I/O error occurs communicating with the node
346          */
347         public HighLevelCallback<PeerResult> addPeer(URL nodeRefURL) throws IOException {
348                 String identifier = generateIdentifier("addPeer");
349                 AddPeer addPeer = new AddPeer(nodeRefURL);
350                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
351                 peerCallbacks.put(identifier, peerCallback);
352                 fcpConnection.sendMessage(addPeer);
353                 return peerCallback;
354         }
355
356         /**
357          * Adds the peer whose noderef is stored in the given file.
358          *
359          * @param nodeRef
360          *            The peer’s noderef
361          * @return A peer callback
362          * @throws IOException
363          *             if an I/O error occurs communicating with the node
364          */
365         public HighLevelCallback<PeerResult> addPeer(NodeRef nodeRef) throws IOException {
366                 String identifier = generateIdentifier("addPeer");
367                 AddPeer addPeer = new AddPeer(nodeRef);
368                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
369                 peerCallbacks.put(identifier, peerCallback);
370                 fcpConnection.sendMessage(addPeer);
371                 return peerCallback;
372         }
373
374         /**
375          * Checks whether direct disk access for the given directory is possible.
376          * You have to perform this check before you can upload or download anything
377          * from or the disk directly!
378          *
379          * @param directory
380          *            The directory to check
381          * @param wantRead
382          *            Whether you want to read the given directory
383          * @param wantWrite
384          *            Whether you want to write to the given directory
385          * @return A direct disk access callback
386          * @throws IOException
387          */
388         public HighLevelCallback<DirectDiskAccessResult> checkDirectDiskAccess(String directory, boolean wantRead, boolean wantWrite) throws IOException {
389                 TestDDARequest testDDARequest = new TestDDARequest(directory, wantRead, wantWrite);
390                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = new HighLevelCallback<DirectDiskAccessResult>(new DirectDiskAccessResult(directory));
391                 directDiskAccessCallbacks.put(directory, directDiskAccessCallback);
392                 fcpConnection.sendMessage(testDDARequest);
393                 return directDiskAccessCallback;
394         }
395
396         /**
397          * Starts a download. Files can either be download to disk or streamed from
398          * the node. When downloading to disk you have to perform a direct disk
399          * access check for the directory you want to put the downloaded file in!
400          *
401          * @see #checkDirectDiskAccess(String, boolean, boolean)
402          * @param uri
403          *            The URI to get
404          * @param filename
405          *            The filename to save the data to, or <code>null</code> to
406          *            retrieve the data as InputStream from the
407          *            {@link DownloadResult}
408          * @param global
409          *            Whether to put the download on the global queue
410          * @return A download result
411          * @throws IOException
412          *             if an I/O error occurs communicating with the node
413          */
414         public HighLevelProgressCallback<DownloadResult> download(String uri, String filename, boolean global) throws IOException {
415                 String identifier = generateIdentifier("download");
416                 ClientGet clientGet = new ClientGet(uri, identifier, (filename == null) ? ReturnType.direct : ReturnType.disk);
417                 clientGet.setGlobal(global);
418                 HighLevelProgressCallback<DownloadResult> downloadCallback = new HighLevelProgressCallback<DownloadResult>(new DownloadResult(identifier));
419                 downloadCallbacks.put(identifier, downloadCallback);
420                 fcpConnection.sendMessage(clientGet);
421                 return downloadCallback;
422         }
423
424         //
425         // PRIVATE METHODS
426         //
427
428         /**
429          * Generates an identifier for the given function.
430          *
431          * @param function
432          *            The name of the function
433          * @return An identifier
434          */
435         private String generateIdentifier(String function) {
436                 return "jFCPlib-" + function + "-" + System.currentTimeMillis();
437         }
438
439         /**
440          * Disconnects the client from the node, handing the given Throwable to
441          * {@link #fireClientDisconnected(Throwable)}.
442          *
443          * @param throwable
444          *            The exception that caused the disconnect, or <code>null</code>
445          *            if there was no exception
446          */
447         private void disconnect(Throwable throwable) {
448                 fcpConnection.close();
449                 fireClientDisconnected(throwable);
450         }
451
452         /**
453          * FCP listener for {@link HighLevelClient}.
454          *
455          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
456          * @version $Id$
457          */
458         private class HighLevelClientFcpListener implements FcpListener {
459
460                 /** Mapping from directory to written file (for cleanup). */
461                 private final Map<DirectDiskAccessResult, String> writtenFiles = new HashMap<DirectDiskAccessResult, String>();
462
463                 /**
464                  * Creates a new FCP listener for {@link HighLevelClient}.
465                  */
466                 HighLevelClientFcpListener() {
467                         /* do nothing. */
468                 }
469
470                 //
471                 // PRIVATE METHODS
472                 //
473
474                 /**
475                  * Searches all callback collections for a callback with the given
476                  * identifier and cancels it.
477                  *
478                  * @param identifier
479                  *            The identifier to search for, or <code>null</code> to
480                  *            cancel all pending requests
481                  */
482                 @SuppressWarnings("synthetic-access")
483                 private void cancelIdentifier(String identifier) {
484                         synchronized (syncObject) {
485                                 if (connectCallback != null) {
486                                         connectCallback.getIntermediaryResult().setFailed(true);
487                                         connectCallback.setDone();
488                                         connectCallback = null;
489                                 }
490                         }
491                         if (identifier == null) {
492                                 /* key generation callbacks */
493                                 for (Entry<String, HighLevelCallback<KeyGenerationResult>> keyGenerationEntry: keyGenerationCallbacks.entrySet()) {
494                                         keyGenerationEntry.getValue().getIntermediaryResult().setFailed(true);
495                                         keyGenerationEntry.getValue().setDone();
496                                 }
497                                 keyGenerationCallbacks.clear();
498                                 /* peer list callbacks. */
499                                 for (Entry<String, HighLevelCallback<PeerListResult>> peerListEntry: peerListCallbacks.entrySet()) {
500                                         peerListEntry.getValue().getIntermediaryResult().setFailed(true);
501                                         peerListEntry.getValue().setDone();
502                                 }
503                                 peerListCallbacks.clear();
504                                 /* peer callbacks. */
505                                 for (Entry<String, HighLevelCallback<PeerResult>> peerEntry: peerCallbacks.entrySet()) {
506                                         peerEntry.getValue().getIntermediaryResult().setFailed(true);
507                                         peerEntry.getValue().setDone();
508                                 }
509                                 peerCallbacks.clear();
510                                 /* direct disk access callbacks. */
511                                 for (Entry<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessEntry: directDiskAccessCallbacks.entrySet()) {
512                                         directDiskAccessEntry.getValue().getIntermediaryResult().setFailed(true);
513                                         directDiskAccessEntry.getValue().setDone();
514                                 }
515                                 directDiskAccessCallbacks.clear();
516                                 /* download callbacks. */
517                                 for (Entry<String, HighLevelProgressCallback<DownloadResult>> downloadEntry: downloadCallbacks.entrySet()) {
518                                         downloadEntry.getValue().getIntermediaryResult().setFailed(true);
519                                         downloadEntry.getValue().setDone();
520                                 }
521                                 downloadCallbacks.clear();
522                         } else {
523                                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(identifier);
524                                 if (keyGenerationCallback != null) {
525                                         keyGenerationCallback.getIntermediaryResult().setFailed(true);
526                                         keyGenerationCallback.setDone();
527                                         return;
528                                 }
529                                 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
530                                 if (peerListCallback != null) {
531                                         peerListCallback.getIntermediaryResult().setFailed(true);
532                                         peerListCallback.setDone();
533                                         return;
534                                 }
535                                 HighLevelCallback<PeerResult> peerCallback = peerCallbacks.remove(identifier);
536                                 if (peerCallback != null) {
537                                         peerCallback.getIntermediaryResult().setFailed(true);
538                                         peerCallback.setDone();
539                                         return;
540                                 }
541                                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(identifier);
542                                 if (directDiskAccessCallback != null) {
543                                         directDiskAccessCallback.getIntermediaryResult().setFailed(true);
544                                         directDiskAccessCallback.setDone();
545                                         return;
546                                 }
547                                 HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
548                                 if (downloadCallback != null) {
549                                         downloadCallback.getIntermediaryResult().setFailed(true);
550                                         downloadCallback.setDone();
551                                         return;
552                                 }
553                         }
554                 }
555
556                 /**
557                  * Reads the given file and returns the first line of the file.
558                  *
559                  * @param readFilename
560                  *            The name of the file to read
561                  * @return The content of the file
562                  */
563                 private String readContent(String readFilename) {
564                         FileReader fileReader = null;
565                         BufferedReader bufferedFileReader = null;
566                         try {
567                                 fileReader = new FileReader(readFilename);
568                                 bufferedFileReader = new BufferedReader(fileReader);
569                                 String content = bufferedFileReader.readLine();
570                                 return content;
571                         } catch (IOException ioe1) {
572                                 /* swallow. */
573                         } finally {
574                                 FcpUtils.close(bufferedFileReader);
575                                 FcpUtils.close(fileReader);
576                         }
577                         return null;
578                 }
579
580                 /**
581                  * Writes the given content to the given file.
582                  *
583                  * @param directDiskAccessResult
584                  *            The DDA result
585                  * @param writeFilename
586                  *            The name of the file to write to
587                  * @param writeContent
588                  *            The content to write to the file
589                  */
590                 private void writeContent(DirectDiskAccessResult directDiskAccessResult, String writeFilename, String writeContent) {
591                         if ((writeFilename == null) || (writeContent == null)) {
592                                 return;
593                         }
594                         writtenFiles.put(directDiskAccessResult, writeFilename);
595                         FileWriter fileWriter = null;
596                         try {
597                                 fileWriter = new FileWriter(writeFilename);
598                                 fileWriter.write(writeContent);
599                         } catch (IOException ioe1) {
600                                 /* swallow. */
601                         } finally {
602                                 FcpUtils.close(fileWriter);
603                         }
604                 }
605
606                 /**
607                  * Cleans up any files that written for the given result.
608                  *
609                  * @param directDiskAccessResult
610                  *            The direct disk access result
611                  */
612                 @SuppressWarnings("synthetic-access")
613                 private void cleanFiles(DirectDiskAccessResult directDiskAccessResult) {
614                         String writeFilename = writtenFiles.remove(directDiskAccessResult);
615                         if (writeFilename != null) {
616                                 if (!new File(writeFilename).delete()) {
617                                         logger.warning("could not delete " + writeFilename);
618                                 }
619                         }
620                 }
621
622                 //
623                 // INTERFACE FcpListener
624                 //
625
626                 /**
627                  * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection,
628                  *      Throwable)
629                  */
630                 @SuppressWarnings("synthetic-access")
631                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
632                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
633                                 return;
634                         }
635                         cancelIdentifier(null);
636                         disconnect(throwable);
637                 }
638
639                 /**
640                  * @see net.pterodactylus.fcp.FcpListener#receivedAllData(net.pterodactylus.fcp.FcpConnection,
641                  *      net.pterodactylus.fcp.AllData)
642                  */
643                 public void receivedAllData(FcpConnection fcpConnection, AllData allData) {
644                         /* TODO */
645                 }
646
647                 /**
648                  * @see net.pterodactylus.fcp.FcpListener#receivedCloseConnectionDuplicateClientName(net.pterodactylus.fcp.FcpConnection,
649                  *      net.pterodactylus.fcp.CloseConnectionDuplicateClientName)
650                  */
651                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
652                         /* TODO */
653                 }
654
655                 /**
656                  * @see net.pterodactylus.fcp.FcpListener#receivedConfigData(net.pterodactylus.fcp.FcpConnection,
657                  *      net.pterodactylus.fcp.ConfigData)
658                  */
659                 public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) {
660                         /* TODO */
661                 }
662
663                 /**
664                  * @see net.pterodactylus.fcp.FcpListener#receivedDataFound(net.pterodactylus.fcp.FcpConnection,
665                  *      net.pterodactylus.fcp.DataFound)
666                  */
667                 public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) {
668                         /* TODO */
669                 }
670
671                 /**
672                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeerNotes(net.pterodactylus.fcp.FcpConnection,
673                  *      net.pterodactylus.fcp.EndListPeerNotes)
674                  */
675                 public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) {
676                         /* TODO */
677                 }
678
679                 /**
680                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.fcp.FcpConnection,
681                  *      net.pterodactylus.fcp.EndListPeers)
682                  */
683                 @SuppressWarnings("synthetic-access")
684                 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
685                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
686                                 return;
687                         }
688                         String identifier = endListPeers.getIdentifier();
689                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
690                         if (peerListCallback == null) {
691                                 return;
692                         }
693                         peerListCallback.setDone();
694                 }
695
696                 /**
697                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
698                  *      net.pterodactylus.fcp.EndListPersistentRequests)
699                  */
700                 public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
701                         /* TODO */
702                 }
703
704                 /**
705                  * @see net.pterodactylus.fcp.FcpListener#receivedFCPPluginReply(net.pterodactylus.fcp.FcpConnection,
706                  *      net.pterodactylus.fcp.FCPPluginReply)
707                  */
708                 public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
709                         /* TODO */
710                 }
711
712                 /**
713                  * @see net.pterodactylus.fcp.FcpListener#receivedGetFailed(net.pterodactylus.fcp.FcpConnection,
714                  *      net.pterodactylus.fcp.GetFailed)
715                  */
716                 @SuppressWarnings("synthetic-access")
717                 public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) {
718                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
719                                 return;
720                         }
721                         String identifier = getFailed.getIdentifier();
722                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
723                         if (downloadCallback != null) {
724                                 downloadCallback.getIntermediaryResult().setFailed(true);
725                                 downloadCallback.setDone();
726                                 return;
727                         }
728                         /* unknown identifier? */
729                         logger.warning("unknown identifier for GetFailed: " + identifier);
730                 }
731
732                 /**
733                  * @see net.pterodactylus.fcp.FcpListener#receivedIdentifierCollision(net.pterodactylus.fcp.FcpConnection,
734                  *      net.pterodactylus.fcp.IdentifierCollision)
735                  */
736                 public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) {
737                         /* TODO */
738                 }
739
740                 /**
741                  * @see net.pterodactylus.fcp.FcpListener#receivedMessage(net.pterodactylus.fcp.FcpConnection,
742                  *      net.pterodactylus.fcp.FcpMessage)
743                  */
744                 public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
745                         /* TODO */
746                 }
747
748                 /**
749                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeData(net.pterodactylus.fcp.FcpConnection,
750                  *      net.pterodactylus.fcp.NodeData)
751                  */
752                 public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) {
753                         /* TODO */
754                 }
755
756                 /**
757                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeHello(net.pterodactylus.fcp.FcpConnection,
758                  *      net.pterodactylus.fcp.NodeHello)
759                  */
760                 @SuppressWarnings("synthetic-access")
761                 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
762                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
763                                 return;
764                         }
765                         synchronized (syncObject) {
766                                 connectCallback.getIntermediaryResult().setFailed(false);
767                                 connectCallback.setDone();
768                                 connectCallback = null;
769                         }
770                         fireClientConnected();
771                 }
772
773                 /**
774                  * @see net.pterodactylus.fcp.FcpListener#receivedPeer(net.pterodactylus.fcp.FcpConnection,
775                  *      net.pterodactylus.fcp.Peer)
776                  */
777                 @SuppressWarnings("synthetic-access")
778                 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
779                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
780                                 return;
781                         }
782                         String identifier = peer.getIdentifier();
783                         if (identifier == null) {
784                                 return;
785                         }
786                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.get(identifier);
787                         if (peerListCallback != null) {
788                                 peerListCallback.getIntermediaryResult().addPeer(peer);
789                                 return;
790                         }
791                         HighLevelCallback<PeerResult> peerResult = peerCallbacks.remove(identifier);
792                         if (peerResult != null) {
793                                 peerResult.getIntermediaryResult().setPeer(peer);
794                                 peerResult.setDone();
795                                 return;
796                         }
797                         logger.warning("got Peer message with unknown identifier: " + identifier);
798                 }
799
800                 /**
801                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerNote(net.pterodactylus.fcp.FcpConnection,
802                  *      net.pterodactylus.fcp.PeerNote)
803                  */
804                 public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) {
805                         /* TODO */
806                 }
807
808                 /**
809                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerRemoved(net.pterodactylus.fcp.FcpConnection,
810                  *      net.pterodactylus.fcp.PeerRemoved)
811                  */
812                 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
813                         /* TODO */
814                 }
815
816                 /**
817                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentGet(net.pterodactylus.fcp.FcpConnection,
818                  *      net.pterodactylus.fcp.PersistentGet)
819                  */
820                 @SuppressWarnings("synthetic-access")
821                 public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) {
822                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
823                                 return;
824                         }
825                         String identifier = persistentGet.getIdentifier();
826                         if (downloadCallbacks.containsKey(identifier)) {
827                                 /* ignore, because a download does not care about this. */
828                                 return;
829                         }
830                 }
831
832                 /**
833                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
834                  *      net.pterodactylus.fcp.PersistentPut)
835                  */
836                 public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
837                         /* TODO */
838                 }
839
840                 /**
841                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
842                  *      net.pterodactylus.fcp.PersistentPutDir)
843                  */
844                 public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
845                         /* TODO */
846                 }
847
848                 /**
849                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestModified(net.pterodactylus.fcp.FcpConnection,
850                  *      net.pterodactylus.fcp.PersistentRequestModified)
851                  */
852                 public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) {
853                         /* TODO */
854                 }
855
856                 /**
857                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestRemoved(net.pterodactylus.fcp.FcpConnection,
858                  *      net.pterodactylus.fcp.PersistentRequestRemoved)
859                  */
860                 public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) {
861                         /* TODO */
862                 }
863
864                 /**
865                  * @see net.pterodactylus.fcp.FcpListener#receivedPluginInfo(net.pterodactylus.fcp.FcpConnection,
866                  *      net.pterodactylus.fcp.PluginInfo)
867                  */
868                 public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) {
869                         /* TODO */
870                 }
871
872                 /**
873                  * @see net.pterodactylus.fcp.FcpListener#receivedProtocolError(net.pterodactylus.fcp.FcpConnection,
874                  *      net.pterodactylus.fcp.ProtocolError)
875                  */
876                 @SuppressWarnings("synthetic-access")
877                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
878                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
879                                 return;
880                         }
881                         String identifier = protocolError.getIdentifier();
882                         if (identifier == null) {
883                                 return;
884                         }
885                         cancelIdentifier(identifier);
886                 }
887
888                 /**
889                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFailed(net.pterodactylus.fcp.FcpConnection,
890                  *      net.pterodactylus.fcp.PutFailed)
891                  */
892                 public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) {
893                         /* TODO */
894                 }
895
896                 /**
897                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFetchable(net.pterodactylus.fcp.FcpConnection,
898                  *      net.pterodactylus.fcp.PutFetchable)
899                  */
900                 public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) {
901                         /* TODO */
902                 }
903
904                 /**
905                  * @see net.pterodactylus.fcp.FcpListener#receivedPutSuccessful(net.pterodactylus.fcp.FcpConnection,
906                  *      net.pterodactylus.fcp.PutSuccessful)
907                  */
908                 public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) {
909                         /* TODO */
910                 }
911
912                 /**
913                  * @see net.pterodactylus.fcp.FcpListener#receivedSSKKeypair(net.pterodactylus.fcp.FcpConnection,
914                  *      net.pterodactylus.fcp.SSKKeypair)
915                  */
916                 @SuppressWarnings("synthetic-access")
917                 public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) {
918                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
919                                 return;
920                         }
921                         HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(sskKeypair.getIdentifier());
922                         if (keyGenerationCallback == null) {
923                                 return;
924                         }
925                         KeyGenerationResult keyGenerationResult = keyGenerationCallback.getIntermediaryResult();
926                         keyGenerationResult.setInsertURI(sskKeypair.getInsertURI());
927                         keyGenerationResult.setRequestURI(sskKeypair.getRequestURI());
928                         keyGenerationCallback.setDone();
929                 }
930
931                 /**
932                  * @see net.pterodactylus.fcp.FcpListener#receivedSimpleProgress(net.pterodactylus.fcp.FcpConnection,
933                  *      net.pterodactylus.fcp.SimpleProgress)
934                  */
935                 @SuppressWarnings("synthetic-access")
936                 public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) {
937                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
938                                 return;
939                         }
940                         String identifier = simpleProgress.getIdentifier();
941                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
942                         if (downloadCallback != null) {
943                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
944                                 downloadResult.setTotalBlocks(simpleProgress.getTotal());
945                                 downloadResult.setRequiredBlocks(simpleProgress.getRequired());
946                                 downloadResult.setSuccessfulBlocks(simpleProgress.getSucceeded());
947                                 downloadResult.setFailedBlocks(simpleProgress.getFailed());
948                                 downloadResult.setFatallyFailedBlocks(simpleProgress.getFatallyFailed());
949                                 downloadResult.setTotalFinalized(simpleProgress.isFinalizedTotal());
950                                 downloadCallback.progressUpdated();
951                                 return;
952                         }
953                         /* unknown identifier? */
954                         logger.warning("unknown identifier for SimpleProgress: " + identifier);
955                 }
956
957                 /**
958                  * @see net.pterodactylus.fcp.FcpListener#receivedStartedCompression(net.pterodactylus.fcp.FcpConnection,
959                  *      net.pterodactylus.fcp.StartedCompression)
960                  */
961                 public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) {
962                         /* TODO */
963                 }
964
965                 /**
966                  * @see net.pterodactylus.fcp.FcpListener#receivedSubscribedUSKUpdate(net.pterodactylus.fcp.FcpConnection,
967                  *      net.pterodactylus.fcp.SubscribedUSKUpdate)
968                  */
969                 public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) {
970                         /* TODO */
971                 }
972
973                 /**
974                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAComplete(net.pterodactylus.fcp.FcpConnection,
975                  *      net.pterodactylus.fcp.TestDDAComplete)
976                  */
977                 @SuppressWarnings("synthetic-access")
978                 public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) {
979                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
980                                 return;
981                         }
982                         String directory = testDDAComplete.getDirectory();
983                         if (directory == null) {
984                                 return;
985                         }
986                         HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(directory);
987                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallback.getIntermediaryResult();
988                         cleanFiles(directDiskAccessResult);
989                         directDiskAccessResult.setReadAllowed(testDDAComplete.isReadDirectoryAllowed());
990                         directDiskAccessResult.setWriteAllowed(testDDAComplete.isWriteDirectoryAllowed());
991                         directDiskAccessCallback.setDone();
992                 }
993
994                 /**
995                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAReply(net.pterodactylus.fcp.FcpConnection,
996                  *      net.pterodactylus.fcp.TestDDAReply)
997                  */
998                 @SuppressWarnings("synthetic-access")
999                 public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) {
1000                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1001                                 return;
1002                         }
1003                         String directory = testDDAReply.getDirectory();
1004                         if (directory == null) {
1005                                 return;
1006                         }
1007                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallbacks.get(directory).getIntermediaryResult();
1008                         String readFilename = testDDAReply.getReadFilename();
1009                         String readContent = readContent(readFilename);
1010                         String writeFilename = testDDAReply.getWriteFilename();
1011                         String writeContent = testDDAReply.getContentToWrite();
1012                         writeContent(directDiskAccessResult, writeFilename, writeContent);
1013                         TestDDAResponse testDDAResponse = new TestDDAResponse(directory, readContent);
1014                         try {
1015                                 fcpConnection.sendMessage(testDDAResponse);
1016                         } catch (IOException e) {
1017                                 /* swallow. I’m verry unhappy about this. */
1018                         }
1019                 }
1020
1021                 /**
1022                  * @see net.pterodactylus.fcp.FcpListener#receivedURIGenerated(net.pterodactylus.fcp.FcpConnection,
1023                  *      net.pterodactylus.fcp.URIGenerated)
1024                  */
1025                 public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) {
1026                         /* TODO */
1027                 }
1028
1029                 /**
1030                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownNodeIdentifier(net.pterodactylus.fcp.FcpConnection,
1031                  *      net.pterodactylus.fcp.UnknownNodeIdentifier)
1032                  */
1033                 public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) {
1034                         /* TODO */
1035                 }
1036
1037                 /**
1038                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownPeerNoteType(net.pterodactylus.fcp.FcpConnection,
1039                  *      net.pterodactylus.fcp.UnknownPeerNoteType)
1040                  */
1041                 public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) {
1042                         /* TODO */
1043                 }
1044
1045                 /**
1046                  * @see net.pterodactylus.fcp.FcpListener#receviedFinishedCompression(net.pterodactylus.fcp.FcpConnection,
1047                  *      net.pterodactylus.fcp.FinishedCompression)
1048                  */
1049                 public void receviedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) {
1050                         /* TODO */
1051                 }
1052
1053         }
1054
1055 }