2 * jSite2 - FpcConnection.java -
3 * Copyright © 2008 David Roden
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 package net.pterodactylus.util.fcp;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.net.InetAddress;
26 import java.net.Socket;
27 import java.net.UnknownHostException;
28 import java.util.ArrayList;
29 import java.util.List;
31 import net.pterodactylus.util.io.Closer;
32 import net.pterodactylus.util.io.LimitedInputStream;
35 * An FCP connection to a Freenet node.
37 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
40 public class FcpConnection {
42 /** The default port for FCP v2. */
43 public static final int DEFAULT_PORT = 9481;
45 /** The list of FCP listeners. */
46 private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
48 /** The address of the node. */
49 private final InetAddress address;
51 /** The port number of the node’s FCP port. */
52 private final int port;
54 /** The remote socket. */
55 private Socket remoteSocket;
57 /** The input stream from the node. */
58 private InputStream remoteInputStream;
60 /** The output stream to the node. */
61 private OutputStream remoteOutputStream;
63 /** The connection handler. */
64 private FcpConnectionHandler connectionHandler;
67 * Creates a new FCP connection to the Freenet node running on the given
68 * host, listening on the default port.
71 * The hostname of the Freenet node
72 * @throws UnknownHostException
73 * if <code>host</code> can not be resolved
75 public FcpConnection(String host) throws UnknownHostException {
76 this(host, DEFAULT_PORT);
80 * Creates a new FCP connection to the Freenet node running on the given
81 * host, listening on the given port.
84 * The hostname of the Freenet node
86 * The port number of the node’s FCP port
87 * @throws UnknownHostException
88 * if <code>host</code> can not be resolved
90 public FcpConnection(String host, int port) throws UnknownHostException {
91 this(InetAddress.getByName(host), port);
95 * Creates a new FCP connection to the Freenet node running at the given
96 * address, listening on the default port.
99 * The address of the Freenet node
101 public FcpConnection(InetAddress address) {
102 this(address, DEFAULT_PORT);
106 * Creates a new FCP connection to the Freenet node running at the given
107 * address, listening on the given port.
110 * The address of the Freenet node
112 * The port number of the node’s FCP port
114 public FcpConnection(InetAddress address, int port) {
115 this.address = address;
120 // LISTENER MANAGEMENT
124 * Adds the given listener to the list of listeners.
127 * The listener to add
129 public void addFcpListener(FcpListener fcpListener) {
130 fcpListeners.add(fcpListener);
134 * Removes the given listener from the list of listeners.
137 * The listener to remove
139 public void removeFcpListener(FcpListener fcpListener) {
140 fcpListeners.remove(fcpListener);
144 * Notifies listeners that a “NodeHello” message was received.
146 * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
148 * The “NodeHello” message
150 private void fireReceivedNodeHello(NodeHello nodeHello) {
151 for (FcpListener fcpListener: fcpListeners) {
152 fcpListener.receivedNodeHello(this, nodeHello);
157 * Notifies listeners that a “CloseConnectionDuplicateClientName” message
160 * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
161 * CloseConnectionDuplicateClientName)
162 * @param closeConnectionDuplicateClientName
163 * The “CloseConnectionDuplicateClientName” message
165 private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
166 for (FcpListener fcpListener: fcpListeners) {
167 fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
172 * Notifies listeners that a “SSKKeypair” message was received.
174 * @see FcpListener#receivedSSKKeypair(FcpConnection, SSKKeypair)
176 * The “SSKKeypair” message
178 private void fireReceivedSSKKeypair(SSKKeypair sskKeypair) {
179 for (FcpListener fcpListener: fcpListeners) {
180 fcpListener.receivedSSKKeypair(this, sskKeypair);
185 * Notifies listeners that a “Peer” message was received.
187 * @see FcpListener#receivedPeer(FcpConnection, Peer)
191 private void fireReceivedPeer(Peer peer) {
192 for (FcpListener fcpListener: fcpListeners) {
193 fcpListener.receivedPeer(this, peer);
198 * Notifies all listeners that an “EndListPeers” message was received.
200 * @see FcpListener#receivedEndListPeers(FcpConnection, EndListPeers)
201 * @param endListPeers
202 * The “EndListPeers” message
204 private void fireReceivedEndListPeers(EndListPeers endListPeers) {
205 for (FcpListener fcpListener: fcpListeners) {
206 fcpListener.receivedEndListPeers(this, endListPeers);
211 * Notifies all listeners that a “PeerNote” message was received.
213 * @see FcpListener#receivedPeerNote(FcpConnection, PeerNote)
216 private void fireReceivedPeerNote(PeerNote peerNote) {
217 for (FcpListener fcpListener: fcpListeners) {
218 fcpListener.receivedPeerNote(this, peerNote);
223 * Notifies all listeners that an “EndListPeerNotes” message was received.
225 * @see FcpListener#receivedEndListPeerNotes(FcpConnection,
227 * @param endListPeerNotes
228 * The “EndListPeerNotes” message
230 private void fireReceivedEndListPeerNotes(EndListPeerNotes endListPeerNotes) {
231 for (FcpListener fcpListener: fcpListeners) {
232 fcpListener.receivedEndListPeerNotes(this, endListPeerNotes);
237 * Notifies all listeners that a “PeerRemoved” message was received.
239 * @see FcpListener#receivedPeerRemoved(FcpConnection, PeerRemoved)
241 * The “PeerRemoved” message
243 private void fireReceivedPeerRemoved(PeerRemoved peerRemoved) {
244 for (FcpListener fcpListener: fcpListeners) {
245 fcpListener.receivedPeerRemoved(this, peerRemoved);
250 * Notifies all listeners that a “NodeData” message was received.
252 * @see FcpListener#receivedNodeData(FcpConnection, NodeData)
254 * The “NodeData” message
256 private void fireReceivedNodeData(NodeData nodeData) {
257 for (FcpListener fcpListener: fcpListeners) {
258 fcpListener.receivedNodeData(this, nodeData);
263 * Notifies all listeners that a “TestDDAReply” message was received.
265 * @see FcpListener#receivedTestDDAReply(FcpConnection, TestDDAReply)
266 * @param testDDAReply
267 * The “TestDDAReply” message
269 private void fireReceivedTestDDAReply(TestDDAReply testDDAReply) {
270 for (FcpListener fcpListener: fcpListeners) {
271 fcpListener.receivedTestDDAReply(this, testDDAReply);
276 * Notifies all listeners that a “TestDDAComplete” message was received.
278 * @see FcpListener#receivedTestDDAComplete(FcpConnection, TestDDAComplete)
279 * @param testDDAComplete
280 * The “TestDDAComplete” message
282 private void fireReceivedTestDDAComplete(TestDDAComplete testDDAComplete) {
283 for (FcpListener fcpListener: fcpListeners) {
284 fcpListener.receivedTestDDAComplete(this, testDDAComplete);
289 * Notifies all listeners that a “PersistentPut” message was received.
291 * @see FcpListener#receivedPersistentPut(FcpConnection, PersistentPut)
292 * @param persistentPut
293 * The “PersistentPut” message
295 private void fireReceivedPersistentPut(PersistentPut persistentPut) {
296 for (FcpListener fcpListener: fcpListeners) {
297 fcpListener.receivedPersistentPut(this, persistentPut);
302 * Notifies all listeners that a “EndListPersistentRequests” message was
305 * @param endListPersistentRequests
306 * The “EndListPersistentRequests” message
308 private void fireReceivedEndListPersistentRequests(EndListPersistentRequests endListPersistentRequests) {
309 for (FcpListener fcpListener: fcpListeners) {
310 fcpListener.receivedEndListPersistentRequests(this, endListPersistentRequests);
315 * Notifies all listeners that a “URIGenerated” message was received.
317 * @param uriGenerated
318 * The “URIGenerated” message
320 private void fireReceivedURIGenerated(URIGenerated uriGenerated) {
321 for (FcpListener fcpListener: fcpListeners) {
322 fcpListener.receivedURIGenerated(this, uriGenerated);
327 * Notifies all listeners that an “AllData” message was received.
330 * The “AllData” message
332 private void fireReceivedAllData(AllData allData) {
333 for (FcpListener fcpListener: fcpListeners) {
334 fcpListener.receivedAllData(this, allData);
339 * Notifies all listeners that a “SimpleProgress” message was received.
341 * @param simpleProgress
342 * The “SimpleProgress” message
344 private void fireReceivedSimpleProgress(SimpleProgress simpleProgress) {
345 for (FcpListener fcpListener: fcpListeners) {
346 fcpListener.receivedSimpleProgress(this, simpleProgress);
351 * Notifies all listeners that a “StartedCompression” message was received.
353 * @param startedCompression
354 * The “StartedCompression” message
356 private void fireReceivedStartedCompression(StartedCompression startedCompression) {
357 for (FcpListener fcpListener: fcpListeners) {
358 fcpListener.receivedStartedCompression(this, startedCompression);
363 * Notifies all listeners that a “FinishedCompression” message was received.
365 * @param finishedCompression
366 * The “FinishedCompression” message
368 private void fireReceivedFinishedCompression(FinishedCompression finishedCompression) {
369 for (FcpListener fcpListener: fcpListeners) {
370 fcpListener.receviedFinishedCompression(this, finishedCompression);
375 * Notifies all listeners that a “ProtocolError” message was received.
377 * @param protocolError
378 * The “ProtocolError” message
380 private void fireReceivedProtocolError(ProtocolError protocolError) {
381 for (FcpListener fcpListener: fcpListeners) {
382 fcpListener.receivedProtocolError(this, protocolError);
387 * Notifies all registered listeners that a message has been received.
389 * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
391 * The message that was received
393 private void fireMessageReceived(FcpMessage fcpMessage) {
394 for (FcpListener fcpListener: fcpListeners) {
395 fcpListener.receivedMessage(this, fcpMessage);
404 * Connects to the node.
406 * @throws IOException
407 * if an I/O error occurs
408 * @throws IllegalStateException
409 * if there is already a connection to the node
411 public synchronized void connect() throws IOException, IllegalStateException {
412 if (connectionHandler != null) {
413 throw new IllegalStateException("already connected, disconnect first");
415 remoteSocket = new Socket(address, port);
416 remoteInputStream = remoteSocket.getInputStream();
417 remoteOutputStream = remoteSocket.getOutputStream();
418 new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
422 * Disconnects from the node. If there is no connection to the node, this
423 * method does nothing.
425 public synchronized void disconnect() {
426 if (connectionHandler == null) {
429 Closer.close(remoteSocket);
430 connectionHandler.stop();
431 connectionHandler = null;
435 * Sends the given FCP message.
438 * The FCP message to send
439 * @throws IOException
440 * if an I/O error occurs
442 public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
443 System.out.println("sending message: " + fcpMessage.getName());
444 fcpMessage.write(remoteOutputStream);
448 // PACKAGE-PRIVATE METHODS
452 * Handles the given message, notifying listeners. This message should only
453 * be called by {@link FcpConnectionHandler}.
456 * The received message
458 void handleMessage(FcpMessage fcpMessage) {
459 String messageName = fcpMessage.getName();
460 if ("SimpleProgress".equals(messageName)) {
461 fireReceivedSimpleProgress(new SimpleProgress(fcpMessage));
462 } else if ("ProtocolError".equals(messageName)) {
463 fireReceivedProtocolError(new ProtocolError(fcpMessage));
464 } else if ("PersistentPut".equals(messageName)) {
465 fireReceivedPersistentPut(new PersistentPut(fcpMessage));
466 } else if ("URIGenerated".equals(messageName)) {
467 fireReceivedURIGenerated(new URIGenerated(fcpMessage));
468 } else if ("EndListPersistentRequests".equals(messageName)) {
469 fireReceivedEndListPersistentRequests(new EndListPersistentRequests(fcpMessage));
470 } else if ("Peer".equals(messageName)) {
471 fireReceivedPeer(new Peer(fcpMessage));
472 } else if ("PeerNote".equals(messageName)) {
473 fireReceivedPeerNote(new PeerNote(fcpMessage));
474 } else if ("StartedCompression".equals(messageName)) {
475 fireReceivedStartedCompression(new StartedCompression(fcpMessage));
476 } else if ("FinishedCompression".equals(messageName)) {
477 fireReceivedFinishedCompression(new FinishedCompression(fcpMessage));
478 } else if ("AllData".equals(messageName)) {
481 dataLength = Long.valueOf(fcpMessage.getField("DataLength"));
482 } catch (NumberFormatException nfe1) {
485 LimitedInputStream payloadInputStream = new LimitedInputStream(remoteInputStream, dataLength);
486 fireReceivedAllData(new AllData(fcpMessage, payloadInputStream));
488 payloadInputStream.consume();
489 } catch (IOException ioe1) {
490 /* FIXME - what now? */
491 /* well, ignore. when the connection handler fails, all fails. */
493 } else if ("EndListPeerNotes".equals(messageName)) {
494 fireReceivedEndListPeerNotes(new EndListPeerNotes(fcpMessage));
495 } else if ("EndListPeers".equals(messageName)) {
496 fireReceivedEndListPeers(new EndListPeers(fcpMessage));
497 } else if ("SSKKeypair".equals(messageName)) {
498 fireReceivedSSKKeypair(new SSKKeypair(fcpMessage));
499 } else if ("PeerRemoved".equals(messageName)) {
500 fireReceivedPeerRemoved(new PeerRemoved(fcpMessage));
501 } else if ("NodeData".equals(messageName)) {
502 fireReceivedNodeData(new NodeData(fcpMessage));
503 } else if ("TestDDAReply".equals(messageName)) {
504 fireReceivedTestDDAReply(new TestDDAReply(fcpMessage));
505 } else if ("TestDDAComplete".equals(messageName)) {
506 fireReceivedTestDDAComplete(new TestDDAComplete(fcpMessage));
507 } else if ("NodeHello".equals(messageName)) {
508 fireReceivedNodeHello(new NodeHello(fcpMessage));
509 } else if ("CloseConnectionDuplicateClientName".equals(messageName)) {
510 fireReceivedCloseConnectionDuplicateClientName(new CloseConnectionDuplicateClientName(fcpMessage));
512 fireMessageReceived(fcpMessage);