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