93378d34bea36c2df823a440833804e11ef5d41d
[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(this, 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                 try {
307                         fcpConnection = new FcpConnection(address, port);
308                         fcpConnection.addFcpListener(highLevelClientFcpListener);
309                         fcpConnection.connect();
310                         ClientHello clientHello = new ClientHello(clientName);
311                         connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
312                         fcpConnection.sendMessage(clientHello);
313                         return connectCallback;
314                 } catch (IOException ioe1) {
315                         fcpConnection = null;
316                         throw ioe1;
317                 }
318         }
319
320         /**
321          * Disconnects the client from the node.
322          */
323         public void disconnect() {
324                 disconnect(null);
325         }
326
327         /**
328          * Generates a new SSK keypair.
329          * 
330          * @return A callback with the keypair
331          * @throws IOException
332          *             if an I/O error occurs communicating with the node
333          */
334         public HighLevelCallback<KeyGenerationResult> generateKey() throws IOException {
335                 String identifier = generateIdentifier("generateSSK");
336                 GenerateSSK generateSSK = new GenerateSSK(identifier);
337                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = new HighLevelCallback<KeyGenerationResult>(new KeyGenerationResult(identifier));
338                 keyGenerationCallbacks.put(identifier, keyGenerationCallback);
339                 fcpConnection.sendMessage(generateSSK);
340                 return keyGenerationCallback;
341         }
342
343         /**
344          * Sets whether to watch the global queue.
345          * 
346          * @param enabled
347          *            <code>true</code> to watch the global queue in addition to
348          *            the client-local queue, <code>false</code> to only watch the
349          *            client-local queue
350          * @throws IOException
351          *             if an I/O error occurs communicating with the node
352          */
353         public void setWatchGlobal(boolean enabled) throws IOException {
354                 WatchGlobal watchGlobal = new WatchGlobal(enabled);
355                 fcpConnection.sendMessage(watchGlobal);
356         }
357
358         /**
359          * Gets a list of all peers from the node.
360          * 
361          * @return A callback with the peer list
362          * @throws IOException
363          *             if an I/O error occurs with the node
364          */
365         public HighLevelCallback<PeerListResult> getPeers() throws IOException {
366                 String identifier = generateIdentifier("listPeers");
367                 ListPeers listPeers = new ListPeers(identifier, true, true);
368                 HighLevelCallback<PeerListResult> peerListCallback = new HighLevelCallback<PeerListResult>(new PeerListResult(identifier));
369                 peerListCallbacks.put(identifier, peerListCallback);
370                 fcpConnection.sendMessage(listPeers);
371                 return peerListCallback;
372         }
373
374         /**
375          * Adds the peer whose noderef is stored in the given file.
376          * 
377          * @param nodeRefFile
378          *            The name of the file the peer’s noderef is stored in
379          * @return A peer callback
380          * @throws IOException
381          *             if an I/O error occurs communicating with the node
382          */
383         public HighLevelCallback<PeerResult> addPeer(String nodeRefFile) throws IOException {
384                 String identifier = generateIdentifier("addPeer");
385                 AddPeer addPeer = new AddPeer(nodeRefFile);
386                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
387                 peerCallbacks.put(identifier, peerCallback);
388                 fcpConnection.sendMessage(addPeer);
389                 return peerCallback;
390         }
391
392         /**
393          * Adds the peer whose noderef is stored in the given file.
394          * 
395          * @param nodeRefURL
396          *            The URL where the peer’s noderef is stored
397          * @return A peer callback
398          * @throws IOException
399          *             if an I/O error occurs communicating with the node
400          */
401         public HighLevelCallback<PeerResult> addPeer(URL nodeRefURL) throws IOException {
402                 String identifier = generateIdentifier("addPeer");
403                 AddPeer addPeer = new AddPeer(nodeRefURL);
404                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
405                 peerCallbacks.put(identifier, peerCallback);
406                 fcpConnection.sendMessage(addPeer);
407                 return peerCallback;
408         }
409
410         /**
411          * Adds the peer whose noderef is stored in the given file.
412          * 
413          * @param nodeRef
414          *            The peer’s noderef
415          * @return A peer callback
416          * @throws IOException
417          *             if an I/O error occurs communicating with the node
418          */
419         public HighLevelCallback<PeerResult> addPeer(NodeRef nodeRef) throws IOException {
420                 String identifier = generateIdentifier("addPeer");
421                 AddPeer addPeer = new AddPeer(nodeRef);
422                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
423                 peerCallbacks.put(identifier, peerCallback);
424                 fcpConnection.sendMessage(addPeer);
425                 return peerCallback;
426         }
427
428         /**
429          * Checks whether direct disk access for the given directory is possible.
430          * You have to perform this check before you can upload or download anything
431          * from or the disk directly!
432          * 
433          * @param directory
434          *            The directory to check
435          * @param wantRead
436          *            Whether you want to read the given directory
437          * @param wantWrite
438          *            Whether you want to write to the given directory
439          * @return A direct disk access callback
440          * @throws IOException
441          */
442         public HighLevelCallback<DirectDiskAccessResult> checkDirectDiskAccess(String directory, boolean wantRead, boolean wantWrite) throws IOException {
443                 TestDDARequest testDDARequest = new TestDDARequest(directory, wantRead, wantWrite);
444                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = new HighLevelCallback<DirectDiskAccessResult>(new DirectDiskAccessResult(directory));
445                 directDiskAccessCallbacks.put(directory, directDiskAccessCallback);
446                 fcpConnection.sendMessage(testDDARequest);
447                 return directDiskAccessCallback;
448         }
449
450         /**
451          * Starts a download. Files can either be download to disk or streamed from
452          * the node. When downloading to disk you have to perform a direct disk
453          * access check for the directory you want to put the downloaded file in!
454          * 
455          * @see #checkDirectDiskAccess(String, boolean, boolean)
456          * @param uri
457          *            The URI to get
458          * @param filename
459          *            The filename to save the data to, or <code>null</code> to
460          *            retrieve the data as InputStream from the
461          *            {@link DownloadResult}
462          * @param global
463          *            Whether to put the download on the global queue
464          * @return A download result
465          * @throws IOException
466          *             if an I/O error occurs communicating with the node
467          */
468         public HighLevelProgressCallback<DownloadResult> download(String uri, String filename, boolean global) throws IOException {
469                 String identifier = generateIdentifier("download");
470                 ClientGet clientGet = new ClientGet(uri, identifier, (filename == null) ? ReturnType.direct : ReturnType.disk);
471                 clientGet.setGlobal(global);
472                 HighLevelProgressCallback<DownloadResult> downloadCallback = new HighLevelProgressCallback<DownloadResult>(new DownloadResult(identifier));
473                 downloadCallbacks.put(identifier, downloadCallback);
474                 fcpConnection.sendMessage(clientGet);
475                 return downloadCallback;
476         }
477
478         /**
479          * Requests a list of all running requests from the node.
480          * 
481          * @return The request list result
482          * @throws IOException
483          *             if an I/O errors communicating with the node
484          */
485         public HighLevelCallback<RequestListResult> getRequests() throws IOException {
486                 String identifier = generateIdentifier("list-persistent-requests");
487                 ListPersistentRequests listPersistentRequests = new ListPersistentRequests();
488                 synchronized (syncObject) {
489                         if (requestListCallback != null) {
490                                 logger.log(Level.SEVERE, "getRequests() called with request still running!");
491                         }
492                         requestListCallback = new HighLevelCallback<RequestListResult>(new RequestListResult(identifier));
493                 }
494                 fcpConnection.sendMessage(listPersistentRequests);
495                 return requestListCallback;
496         }
497
498         //
499         // PRIVATE METHODS
500         //
501
502         /**
503          * Generates an identifier for the given function.
504          * 
505          * @param function
506          *            The name of the function
507          * @return An identifier
508          */
509         private String generateIdentifier(String function) {
510                 return "jFCPlib-" + function + "-" + System.currentTimeMillis();
511         }
512
513         /**
514          * Disconnects the client from the node, handing the given Throwable to
515          * {@link #fireClientDisconnected(Throwable)}.
516          * 
517          * @param throwable
518          *            The exception that caused the disconnect, or <code>null</code>
519          *            if there was no exception
520          */
521         private void disconnect(Throwable throwable) {
522                 if (fcpConnection != null) {
523                         fcpConnection.close();
524                 }
525                 fcpConnection = null;
526         }
527
528         /**
529          * FCP listener for {@link HighLevelClient}.
530          * 
531          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
532          * @version $Id$
533          */
534         private class HighLevelClientFcpListener implements FcpListener {
535
536                 /** Mapping from directory to written file (for cleanup). */
537                 private final Map<DirectDiskAccessResult, String> writtenFiles = new HashMap<DirectDiskAccessResult, String>();
538
539                 /**
540                  * Creates a new FCP listener for {@link HighLevelClient}.
541                  */
542                 HighLevelClientFcpListener() {
543                         /* do nothing. */
544                 }
545
546                 //
547                 // PRIVATE METHODS
548                 //
549
550                 /**
551                  * Searches all callback collections for a callback with the given
552                  * identifier and cancels it.
553                  * 
554                  * @param identifier
555                  *            The identifier to search for, or <code>null</code> to
556                  *            cancel all pending requests
557                  */
558                 @SuppressWarnings("synthetic-access")
559                 private void cancelIdentifier(String identifier) {
560                         synchronized (syncObject) {
561                                 if (connectCallback != null) {
562                                         connectCallback.getIntermediaryResult().setFailed(true);
563                                         connectCallback.setDone();
564                                         connectCallback = null;
565                                 }
566                                 if (requestListCallback != null) {
567                                         requestListCallback.getIntermediaryResult().setFailed(true);
568                                         requestListCallback.setDone();
569                                         requestListCallback = null;
570                                 }
571                         }
572                         if (identifier == null) {
573                                 /* key generation callbacks */
574                                 for (Entry<String, HighLevelCallback<KeyGenerationResult>> keyGenerationEntry: keyGenerationCallbacks.entrySet()) {
575                                         keyGenerationEntry.getValue().getIntermediaryResult().setFailed(true);
576                                         keyGenerationEntry.getValue().setDone();
577                                 }
578                                 keyGenerationCallbacks.clear();
579                                 /* peer list callbacks. */
580                                 for (Entry<String, HighLevelCallback<PeerListResult>> peerListEntry: peerListCallbacks.entrySet()) {
581                                         peerListEntry.getValue().getIntermediaryResult().setFailed(true);
582                                         peerListEntry.getValue().setDone();
583                                 }
584                                 peerListCallbacks.clear();
585                                 /* peer callbacks. */
586                                 for (Entry<String, HighLevelCallback<PeerResult>> peerEntry: peerCallbacks.entrySet()) {
587                                         peerEntry.getValue().getIntermediaryResult().setFailed(true);
588                                         peerEntry.getValue().setDone();
589                                 }
590                                 peerCallbacks.clear();
591                                 /* direct disk access callbacks. */
592                                 for (Entry<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessEntry: directDiskAccessCallbacks.entrySet()) {
593                                         directDiskAccessEntry.getValue().getIntermediaryResult().setFailed(true);
594                                         directDiskAccessEntry.getValue().setDone();
595                                 }
596                                 directDiskAccessCallbacks.clear();
597                                 /* download callbacks. */
598                                 for (Entry<String, HighLevelProgressCallback<DownloadResult>> downloadEntry: downloadCallbacks.entrySet()) {
599                                         downloadEntry.getValue().getIntermediaryResult().setFailed(true);
600                                         downloadEntry.getValue().setDone();
601                                 }
602                                 downloadCallbacks.clear();
603                         } else {
604                                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(identifier);
605                                 if (keyGenerationCallback != null) {
606                                         keyGenerationCallback.getIntermediaryResult().setFailed(true);
607                                         keyGenerationCallback.setDone();
608                                         return;
609                                 }
610                                 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
611                                 if (peerListCallback != null) {
612                                         peerListCallback.getIntermediaryResult().setFailed(true);
613                                         peerListCallback.setDone();
614                                         return;
615                                 }
616                                 HighLevelCallback<PeerResult> peerCallback = peerCallbacks.remove(identifier);
617                                 if (peerCallback != null) {
618                                         peerCallback.getIntermediaryResult().setFailed(true);
619                                         peerCallback.setDone();
620                                         return;
621                                 }
622                                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(identifier);
623                                 if (directDiskAccessCallback != null) {
624                                         directDiskAccessCallback.getIntermediaryResult().setFailed(true);
625                                         directDiskAccessCallback.setDone();
626                                         return;
627                                 }
628                                 HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
629                                 if (downloadCallback != null) {
630                                         downloadCallback.getIntermediaryResult().setFailed(true);
631                                         downloadCallback.setDone();
632                                         return;
633                                 }
634                         }
635                 }
636
637                 /**
638                  * Reads the given file and returns the first line of the file.
639                  * 
640                  * @param readFilename
641                  *            The name of the file to read
642                  * @return The content of the file
643                  */
644                 private String readContent(String readFilename) {
645                         FileReader fileReader = null;
646                         BufferedReader bufferedFileReader = null;
647                         try {
648                                 fileReader = new FileReader(readFilename);
649                                 bufferedFileReader = new BufferedReader(fileReader);
650                                 String content = bufferedFileReader.readLine();
651                                 return content;
652                         } catch (IOException ioe1) {
653                                 /* swallow. */
654                         } finally {
655                                 FcpUtils.close(bufferedFileReader);
656                                 FcpUtils.close(fileReader);
657                         }
658                         return null;
659                 }
660
661                 /**
662                  * Writes the given content to the given file.
663                  * 
664                  * @param directDiskAccessResult
665                  *            The DDA result
666                  * @param writeFilename
667                  *            The name of the file to write to
668                  * @param writeContent
669                  *            The content to write to the file
670                  */
671                 private void writeContent(DirectDiskAccessResult directDiskAccessResult, String writeFilename, String writeContent) {
672                         if ((writeFilename == null) || (writeContent == null)) {
673                                 return;
674                         }
675                         writtenFiles.put(directDiskAccessResult, writeFilename);
676                         FileWriter fileWriter = null;
677                         try {
678                                 fileWriter = new FileWriter(writeFilename);
679                                 fileWriter.write(writeContent);
680                         } catch (IOException ioe1) {
681                                 /* swallow. */
682                         } finally {
683                                 FcpUtils.close(fileWriter);
684                         }
685                 }
686
687                 /**
688                  * Cleans up any files that written for the given result.
689                  * 
690                  * @param directDiskAccessResult
691                  *            The direct disk access result
692                  */
693                 @SuppressWarnings("synthetic-access")
694                 private void cleanFiles(DirectDiskAccessResult directDiskAccessResult) {
695                         String writeFilename = writtenFiles.remove(directDiskAccessResult);
696                         if (writeFilename != null) {
697                                 if (!new File(writeFilename).delete()) {
698                                         logger.warning("could not delete " + writeFilename);
699                                 }
700                         }
701                 }
702
703                 //
704                 // INTERFACE FcpListener
705                 //
706
707                 /**
708                  * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection,
709                  *      Throwable)
710                  */
711                 @SuppressWarnings("synthetic-access")
712                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
713                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
714                                 return;
715                         }
716                         cancelIdentifier(null);
717                         disconnect(throwable);
718                         fireClientDisconnected(throwable);
719                 }
720
721                 /**
722                  * @see net.pterodactylus.fcp.FcpListener#receivedAllData(net.pterodactylus.fcp.FcpConnection,
723                  *      net.pterodactylus.fcp.AllData)
724                  */
725                 public void receivedAllData(FcpConnection fcpConnection, AllData allData) {
726                         /* TODO */
727                 }
728
729                 /**
730                  * @see net.pterodactylus.fcp.FcpListener#receivedCloseConnectionDuplicateClientName(net.pterodactylus.fcp.FcpConnection,
731                  *      net.pterodactylus.fcp.CloseConnectionDuplicateClientName)
732                  */
733                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
734                         /* TODO */
735                 }
736
737                 /**
738                  * @see net.pterodactylus.fcp.FcpListener#receivedConfigData(net.pterodactylus.fcp.FcpConnection,
739                  *      net.pterodactylus.fcp.ConfigData)
740                  */
741                 public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) {
742                         /* TODO */
743                 }
744
745                 /**
746                  * @see net.pterodactylus.fcp.FcpListener#receivedDataFound(net.pterodactylus.fcp.FcpConnection,
747                  *      net.pterodactylus.fcp.DataFound)
748                  */
749                 public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) {
750                         /* TODO */
751                 }
752
753                 /**
754                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeerNotes(net.pterodactylus.fcp.FcpConnection,
755                  *      net.pterodactylus.fcp.EndListPeerNotes)
756                  */
757                 public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) {
758                         /* TODO */
759                 }
760
761                 /**
762                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.fcp.FcpConnection,
763                  *      net.pterodactylus.fcp.EndListPeers)
764                  */
765                 @SuppressWarnings("synthetic-access")
766                 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
767                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
768                                 return;
769                         }
770                         String identifier = endListPeers.getIdentifier();
771                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
772                         if (peerListCallback == null) {
773                                 return;
774                         }
775                         peerListCallback.setDone();
776                 }
777
778                 /**
779                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
780                  *      net.pterodactylus.fcp.EndListPersistentRequests)
781                  */
782                 @SuppressWarnings("synthetic-access")
783                 public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
784                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
785                                 return;
786                         }
787                         synchronized (syncObject) {
788                                 if (HighLevelClient.this.requestListCallback == null) {
789                                         logger.log(Level.WARNING, "got EndListPersistentRequests without running request!");
790                                         return;
791                                 }
792                                 requestListCallback.setDone();
793                                 requestListCallback = null;
794                         }
795                 }
796
797                 /**
798                  * @see net.pterodactylus.fcp.FcpListener#receivedFCPPluginReply(net.pterodactylus.fcp.FcpConnection,
799                  *      net.pterodactylus.fcp.FCPPluginReply)
800                  */
801                 public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
802                         /* TODO */
803                 }
804
805                 /**
806                  * @see net.pterodactylus.fcp.FcpListener#receivedGetFailed(net.pterodactylus.fcp.FcpConnection,
807                  *      net.pterodactylus.fcp.GetFailed)
808                  */
809                 @SuppressWarnings("synthetic-access")
810                 public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) {
811                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
812                                 return;
813                         }
814                         String identifier = getFailed.getIdentifier();
815                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
816                         if (downloadCallback != null) {
817                                 downloadCallback.getIntermediaryResult().setFailed(true);
818                                 downloadCallback.setDone();
819                                 return;
820                         }
821                         /* unknown identifier? */
822                         logger.warning("unknown identifier for GetFailed: " + identifier);
823                 }
824
825                 /**
826                  * @see net.pterodactylus.fcp.FcpListener#receivedIdentifierCollision(net.pterodactylus.fcp.FcpConnection,
827                  *      net.pterodactylus.fcp.IdentifierCollision)
828                  */
829                 public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) {
830                         /* TODO */
831                 }
832
833                 /**
834                  * @see net.pterodactylus.fcp.FcpListener#receivedMessage(net.pterodactylus.fcp.FcpConnection,
835                  *      net.pterodactylus.fcp.FcpMessage)
836                  */
837                 public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
838                         /* TODO */
839                 }
840
841                 /**
842                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeData(net.pterodactylus.fcp.FcpConnection,
843                  *      net.pterodactylus.fcp.NodeData)
844                  */
845                 public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) {
846                         /* TODO */
847                 }
848
849                 /**
850                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeHello(net.pterodactylus.fcp.FcpConnection,
851                  *      net.pterodactylus.fcp.NodeHello)
852                  */
853                 @SuppressWarnings("synthetic-access")
854                 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
855                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
856                                 return;
857                         }
858                         synchronized (syncObject) {
859                                 connectCallback.getIntermediaryResult().setFailed(false);
860                                 connectCallback.setDone();
861                                 connectCallback = null;
862                         }
863                         fireClientConnected();
864                 }
865
866                 /**
867                  * @see net.pterodactylus.fcp.FcpListener#receivedPeer(net.pterodactylus.fcp.FcpConnection,
868                  *      net.pterodactylus.fcp.Peer)
869                  */
870                 @SuppressWarnings("synthetic-access")
871                 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
872                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
873                                 return;
874                         }
875                         String identifier = peer.getIdentifier();
876                         if (identifier == null) {
877                                 return;
878                         }
879                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.get(identifier);
880                         if (peerListCallback != null) {
881                                 peerListCallback.getIntermediaryResult().addPeer(peer);
882                                 return;
883                         }
884                         HighLevelCallback<PeerResult> peerResult = peerCallbacks.remove(identifier);
885                         if (peerResult != null) {
886                                 peerResult.getIntermediaryResult().setPeer(peer);
887                                 peerResult.setDone();
888                                 return;
889                         }
890                         logger.warning("got Peer message with unknown identifier: " + identifier);
891                 }
892
893                 /**
894                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerNote(net.pterodactylus.fcp.FcpConnection,
895                  *      net.pterodactylus.fcp.PeerNote)
896                  */
897                 public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) {
898                         /* TODO */
899                 }
900
901                 /**
902                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerRemoved(net.pterodactylus.fcp.FcpConnection,
903                  *      net.pterodactylus.fcp.PeerRemoved)
904                  */
905                 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
906                         /* TODO */
907                 }
908
909                 /**
910                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentGet(net.pterodactylus.fcp.FcpConnection,
911                  *      net.pterodactylus.fcp.PersistentGet)
912                  */
913                 @SuppressWarnings("synthetic-access")
914                 public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) {
915                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
916                                 return;
917                         }
918                         synchronized (syncObject) {
919                                 if (requestListCallback != null) {
920                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
921                                         requestListResult.addRequestResult(new GetRequestResult(persistentGet));
922                                         return;
923                                 }
924                         }
925                         String identifier = persistentGet.getIdentifier();
926                         if (downloadCallbacks.containsKey(identifier)) {
927                                 /* TODO */
928                                 return;
929                         }
930                 }
931
932                 /**
933                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
934                  *      net.pterodactylus.fcp.PersistentPut)
935                  */
936                 @SuppressWarnings("synthetic-access")
937                 public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
938                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
939                                 return;
940                         }
941                         synchronized (syncObject) {
942                                 if (requestListCallback != null) {
943                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
944                                         requestListResult.addRequestResult(new PutRequestResult(persistentPut));
945                                         return;
946                                 }
947                         }
948                 }
949
950                 /**
951                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
952                  *      net.pterodactylus.fcp.PersistentPutDir)
953                  */
954                 @SuppressWarnings("synthetic-access")
955                 public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
956                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
957                                 return;
958                         }
959                         synchronized (syncObject) {
960                                 if (requestListCallback != null) {
961                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
962                                         requestListResult.addRequestResult(new PutDirRequestResult(persistentPutDir));
963                                         return;
964                                 }
965                         }
966                 }
967
968                 /**
969                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestModified(net.pterodactylus.fcp.FcpConnection,
970                  *      net.pterodactylus.fcp.PersistentRequestModified)
971                  */
972                 public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) {
973                         /* TODO */
974                 }
975
976                 /**
977                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestRemoved(net.pterodactylus.fcp.FcpConnection,
978                  *      net.pterodactylus.fcp.PersistentRequestRemoved)
979                  */
980                 public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) {
981                         /* TODO */
982                 }
983
984                 /**
985                  * @see net.pterodactylus.fcp.FcpListener#receivedPluginInfo(net.pterodactylus.fcp.FcpConnection,
986                  *      net.pterodactylus.fcp.PluginInfo)
987                  */
988                 public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) {
989                         /* TODO */
990                 }
991
992                 /**
993                  * @see net.pterodactylus.fcp.FcpListener#receivedProtocolError(net.pterodactylus.fcp.FcpConnection,
994                  *      net.pterodactylus.fcp.ProtocolError)
995                  */
996                 @SuppressWarnings("synthetic-access")
997                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
998                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
999                                 return;
1000                         }
1001                         String identifier = protocolError.getIdentifier();
1002                         if (identifier == null) {
1003                                 return;
1004                         }
1005                         cancelIdentifier(identifier);
1006                 }
1007
1008                 /**
1009                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFailed(net.pterodactylus.fcp.FcpConnection,
1010                  *      net.pterodactylus.fcp.PutFailed)
1011                  */
1012                 public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) {
1013                         /* TODO */
1014                 }
1015
1016                 /**
1017                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFetchable(net.pterodactylus.fcp.FcpConnection,
1018                  *      net.pterodactylus.fcp.PutFetchable)
1019                  */
1020                 public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) {
1021                         /* TODO */
1022                 }
1023
1024                 /**
1025                  * @see net.pterodactylus.fcp.FcpListener#receivedPutSuccessful(net.pterodactylus.fcp.FcpConnection,
1026                  *      net.pterodactylus.fcp.PutSuccessful)
1027                  */
1028                 public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) {
1029                         /* TODO */
1030                 }
1031
1032                 /**
1033                  * @see net.pterodactylus.fcp.FcpListener#receivedSSKKeypair(net.pterodactylus.fcp.FcpConnection,
1034                  *      net.pterodactylus.fcp.SSKKeypair)
1035                  */
1036                 @SuppressWarnings("synthetic-access")
1037                 public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) {
1038                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1039                                 return;
1040                         }
1041                         HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(sskKeypair.getIdentifier());
1042                         if (keyGenerationCallback == null) {
1043                                 return;
1044                         }
1045                         KeyGenerationResult keyGenerationResult = keyGenerationCallback.getIntermediaryResult();
1046                         keyGenerationResult.setInsertURI(sskKeypair.getInsertURI());
1047                         keyGenerationResult.setRequestURI(sskKeypair.getRequestURI());
1048                         keyGenerationCallback.setDone();
1049                 }
1050
1051                 /**
1052                  * @see net.pterodactylus.fcp.FcpListener#receivedSimpleProgress(net.pterodactylus.fcp.FcpConnection,
1053                  *      net.pterodactylus.fcp.SimpleProgress)
1054                  */
1055                 @SuppressWarnings("synthetic-access")
1056                 public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) {
1057                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1058                                 return;
1059                         }
1060                         String identifier = simpleProgress.getIdentifier();
1061                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
1062                         if (downloadCallback != null) {
1063                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
1064                                 downloadResult.setTotalBlocks(simpleProgress.getTotal());
1065                                 downloadResult.setRequiredBlocks(simpleProgress.getRequired());
1066                                 downloadResult.setSuccessfulBlocks(simpleProgress.getSucceeded());
1067                                 downloadResult.setFailedBlocks(simpleProgress.getFailed());
1068                                 downloadResult.setFatallyFailedBlocks(simpleProgress.getFatallyFailed());
1069                                 downloadResult.setTotalFinalized(simpleProgress.isFinalizedTotal());
1070                                 downloadCallback.progressUpdated();
1071                         }
1072                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, simpleProgress.getTotal(), simpleProgress.getRequired(), simpleProgress.getSucceeded(), simpleProgress.getFailed(), simpleProgress.getFatallyFailed(), simpleProgress.isFinalizedTotal());
1073                         fireProgressReceived(identifier, highLevelProgress);
1074                 }
1075
1076                 /**
1077                  * @see net.pterodactylus.fcp.FcpListener#receivedStartedCompression(net.pterodactylus.fcp.FcpConnection,
1078                  *      net.pterodactylus.fcp.StartedCompression)
1079                  */
1080                 public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) {
1081                         /* TODO */
1082                 }
1083
1084                 /**
1085                  * @see net.pterodactylus.fcp.FcpListener#receivedSubscribedUSKUpdate(net.pterodactylus.fcp.FcpConnection,
1086                  *      net.pterodactylus.fcp.SubscribedUSKUpdate)
1087                  */
1088                 public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) {
1089                         /* TODO */
1090                 }
1091
1092                 /**
1093                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAComplete(net.pterodactylus.fcp.FcpConnection,
1094                  *      net.pterodactylus.fcp.TestDDAComplete)
1095                  */
1096                 @SuppressWarnings("synthetic-access")
1097                 public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) {
1098                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1099                                 return;
1100                         }
1101                         String directory = testDDAComplete.getDirectory();
1102                         if (directory == null) {
1103                                 return;
1104                         }
1105                         HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(directory);
1106                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallback.getIntermediaryResult();
1107                         cleanFiles(directDiskAccessResult);
1108                         directDiskAccessResult.setReadAllowed(testDDAComplete.isReadDirectoryAllowed());
1109                         directDiskAccessResult.setWriteAllowed(testDDAComplete.isWriteDirectoryAllowed());
1110                         directDiskAccessCallback.setDone();
1111                 }
1112
1113                 /**
1114                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAReply(net.pterodactylus.fcp.FcpConnection,
1115                  *      net.pterodactylus.fcp.TestDDAReply)
1116                  */
1117                 @SuppressWarnings("synthetic-access")
1118                 public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) {
1119                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1120                                 return;
1121                         }
1122                         String directory = testDDAReply.getDirectory();
1123                         if (directory == null) {
1124                                 return;
1125                         }
1126                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallbacks.get(directory).getIntermediaryResult();
1127                         String readFilename = testDDAReply.getReadFilename();
1128                         String readContent = readContent(readFilename);
1129                         String writeFilename = testDDAReply.getWriteFilename();
1130                         String writeContent = testDDAReply.getContentToWrite();
1131                         writeContent(directDiskAccessResult, writeFilename, writeContent);
1132                         TestDDAResponse testDDAResponse = new TestDDAResponse(directory, readContent);
1133                         try {
1134                                 fcpConnection.sendMessage(testDDAResponse);
1135                         } catch (IOException e) {
1136                                 /* swallow. I’m verry unhappy about this. */
1137                         }
1138                 }
1139
1140                 /**
1141                  * @see net.pterodactylus.fcp.FcpListener#receivedURIGenerated(net.pterodactylus.fcp.FcpConnection,
1142                  *      net.pterodactylus.fcp.URIGenerated)
1143                  */
1144                 public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) {
1145                         /* TODO */
1146                 }
1147
1148                 /**
1149                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownNodeIdentifier(net.pterodactylus.fcp.FcpConnection,
1150                  *      net.pterodactylus.fcp.UnknownNodeIdentifier)
1151                  */
1152                 public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) {
1153                         /* TODO */
1154                 }
1155
1156                 /**
1157                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownPeerNoteType(net.pterodactylus.fcp.FcpConnection,
1158                  *      net.pterodactylus.fcp.UnknownPeerNoteType)
1159                  */
1160                 public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) {
1161                         /* TODO */
1162                 }
1163
1164                 /**
1165                  * @see net.pterodactylus.fcp.FcpListener#receviedFinishedCompression(net.pterodactylus.fcp.FcpConnection,
1166                  *      net.pterodactylus.fcp.FinishedCompression)
1167                  */
1168                 public void receviedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) {
1169                         /* TODO */
1170                 }
1171
1172         }
1173
1174 }