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;
34 * An FCP connection to a Freenet node.
36 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
39 public class FcpConnection {
41 /** The default port for FCP v2. */
42 public static final int DEFAULT_PORT = 9481;
44 /** The list of FCP listeners. */
45 private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
47 /** The address of the node. */
48 private final InetAddress address;
50 /** The port number of the node’s FCP port. */
51 private final int port;
53 /** The remote socket. */
54 private Socket remoteSocket;
56 /** The input stream from the node. */
57 private InputStream remoteInputStream;
59 /** The output stream to the node. */
60 private OutputStream remoteOutputStream;
62 /** The connection handler. */
63 private FcpConnectionHandler connectionHandler;
66 * Creates a new FCP connection to the Freenet node running on the given
67 * host, listening on the default port.
70 * The hostname of the Freenet node
71 * @throws UnknownHostException
72 * if <code>host</code> can not be resolved
74 public FcpConnection(String host) throws UnknownHostException {
75 this(host, DEFAULT_PORT);
79 * Creates a new FCP connection to the Freenet node running on the given
80 * host, listening on the given port.
83 * The hostname of the Freenet node
85 * The port number of the node’s FCP port
86 * @throws UnknownHostException
87 * if <code>host</code> can not be resolved
89 public FcpConnection(String host, int port) throws UnknownHostException {
90 this(InetAddress.getByName(host), port);
94 * Creates a new FCP connection to the Freenet node running at the given
95 * address, listening on the default port.
98 * The address of the Freenet node
100 public FcpConnection(InetAddress address) {
101 this(address, DEFAULT_PORT);
105 * Creates a new FCP connection to the Freenet node running at the given
106 * address, listening on the given port.
109 * The address of the Freenet node
111 * The port number of the node’s FCP port
113 public FcpConnection(InetAddress address, int port) {
114 this.address = address;
119 // LISTENER MANAGEMENT
123 * Adds the given listener to the list of listeners.
126 * The listener to add
128 public void addFcpListener(FcpListener fcpListener) {
129 fcpListeners.add(fcpListener);
133 * Removes the given listener from the list of listeners.
136 * The listener to remove
138 public void removeFcpListener(FcpListener fcpListener) {
139 fcpListeners.remove(fcpListener);
143 * Notifies listeners that a “NodeHello” message was received.
145 * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
147 * The “NodeHello” message
149 private void fireReceivedNodeHello(NodeHello nodeHello) {
150 for (FcpListener fcpListener: fcpListeners) {
151 fcpListener.receivedNodeHello(this, nodeHello);
156 * Notifies listeners that a “CloseConnectionDuplicateClientName” message
159 * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
160 * CloseConnectionDuplicateClientName)
161 * @param closeConnectionDuplicateClientName
162 * The “CloseConnectionDuplicateClientName” message
164 private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
165 for (FcpListener fcpListener: fcpListeners) {
166 fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
171 * Notifies listeners that a “SSKKeypair” message was received.
173 * @see FcpListener#receivedSSKKeypair(FcpConnection, SSKKeypair)
175 * The “SSKKeypair” message
177 private void fireReceivedSSKKeypair(SSKKeypair sskKeypair) {
178 for (FcpListener fcpListener: fcpListeners) {
179 fcpListener.receivedSSKKeypair(this, sskKeypair);
184 * Notifies listeners that a “Peer” message was received.
186 * @see FcpListener#receivedPeer(FcpConnection, Peer)
190 private void fireReceivedPeer(Peer peer) {
191 for (FcpListener fcpListener: fcpListeners) {
192 fcpListener.receivedPeer(this, peer);
197 * Notifies all listeners that an “EndListPeers” message was received.
199 * @see FcpListener#receivedEndListPeers(FcpConnection, EndListPeers)
200 * @param endListPeers
201 * The “EndListPeers” message
203 private void fireReceivedEndListPeers(EndListPeers endListPeers) {
204 for (FcpListener fcpListener: fcpListeners) {
205 fcpListener.receivedEndListPeers(this, endListPeers);
210 * Notifies all listeners that a “PeerNote” message was received.
212 * @see FcpListener#receivedPeerNote(FcpConnection, PeerNote)
215 private void fireReceivedPeerNote(PeerNote peerNote) {
216 for (FcpListener fcpListener: fcpListeners) {
217 fcpListener.receivedPeerNote(this, peerNote);
222 * Notifies all listeners that an “EndListPeerNotes” message was received.
224 * @see FcpListener#receivedEndListPeerNotes(FcpConnection,
226 * @param endListPeerNotes
227 * The “EndListPeerNotes” message
229 private void fireReceivedEndListPeerNotes(EndListPeerNotes endListPeerNotes) {
230 for (FcpListener fcpListener: fcpListeners) {
231 fcpListener.receivedEndListPeerNotes(this, endListPeerNotes);
236 * Notifies all listeners that a “PeerRemoved” message was received.
238 * @see FcpListener#receivedPeerRemoved(FcpConnection, PeerRemoved)
240 * The “PeerRemoved” message
242 private void fireReceivedPeerRemoved(PeerRemoved peerRemoved) {
243 for (FcpListener fcpListener: fcpListeners) {
244 fcpListener.receivedPeerRemoved(this, peerRemoved);
249 * Notifies all listeners that a “NodeData” message was received.
251 * @see FcpListener#receivedNodeData(FcpConnection, NodeData)
253 * The “NodeData” message
255 private void fireReceivedNodeData(NodeData nodeData) {
256 for (FcpListener fcpListener: fcpListeners) {
257 fcpListener.receivedNodeData(this, nodeData);
262 * Notifies all listeners that a “TestDDAReply” message was received.
264 * @param testDDAReply
265 * The “TestDDAReply” message
267 private void fireReceivedTestDDAReply(TestDDAReply testDDAReply) {
268 for (FcpListener fcpListener: fcpListeners) {
269 fcpListener.receivedTestDDAReply(this, testDDAReply);
274 * Notifies all registered listeners that a message has been received.
276 * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
278 * The message that was received
280 private void fireMessageReceived(FcpMessage fcpMessage) {
281 for (FcpListener fcpListener: fcpListeners) {
282 fcpListener.receivedMessage(this, fcpMessage);
291 * Connects to the node.
293 * @throws IOException
294 * if an I/O error occurs
295 * @throws IllegalStateException
296 * if there is already a connection to the node
298 public synchronized void connect() throws IOException, IllegalStateException {
299 if (connectionHandler != null) {
300 throw new IllegalStateException("already connected, disconnect first");
302 remoteSocket = new Socket(address, port);
303 remoteInputStream = remoteSocket.getInputStream();
304 remoteOutputStream = remoteSocket.getOutputStream();
305 new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
309 * Disconnects from the node. If there is no connection to the node, this
310 * method does nothing.
312 public synchronized void disconnect() {
313 if (connectionHandler == null) {
316 Closer.close(remoteSocket);
317 connectionHandler.stop();
318 connectionHandler = null;
322 * Sends the given FCP message.
325 * The FCP message to send
326 * @throws IOException
327 * if an I/O error occurs
329 public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
330 System.out.println("sending message: " + fcpMessage.getName());
331 fcpMessage.write(remoteOutputStream);
335 // PACKAGE-PRIVATE METHODS
339 * Handles the given message, notifying listeners. This message should only
340 * be called by {@link FcpConnectionHandler}.
343 * The received message
345 void handleMessage(FcpMessage fcpMessage) {
346 String messageName = fcpMessage.getName();
347 if ("Peer".equals(messageName)) {
348 fireReceivedPeer(new Peer(fcpMessage));
349 } else if ("PeerNote".equals(messageName)) {
350 fireReceivedPeerNote(new PeerNote(fcpMessage));
351 } else if ("EndListPeerNotes".equals(messageName)) {
352 fireReceivedEndListPeerNotes(new EndListPeerNotes(fcpMessage));
353 } else if ("EndListPeers".equals(messageName)) {
354 fireReceivedEndListPeers(new EndListPeers(fcpMessage));
355 } else if ("SSKKeypair".equals(messageName)) {
356 fireReceivedSSKKeypair(new SSKKeypair(fcpMessage));
357 } else if ("PeerRemoved".equals(messageName)) {
358 fireReceivedPeerRemoved(new PeerRemoved(fcpMessage));
359 } else if ("NodeData".equals(messageName)) {
360 fireReceivedNodeData(new NodeData(fcpMessage));
361 } else if ("TestDDAReply".equals(messageName)) {
362 fireReceivedTestDDAReply(new TestDDAReply(fcpMessage));
363 } else if ("NodeHello".equals(messageName)) {
364 fireReceivedNodeHello(new NodeHello(fcpMessage));
365 } else if ("CloseConnectionDuplicateClientName".equals(messageName)) {
366 fireReceivedCloseConnectionDuplicateClientName(new CloseConnectionDuplicateClientName(fcpMessage));
368 fireMessageReceived(fcpMessage);