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