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.fcp.message.CloseConnectionDuplicateClientName;
32 import net.pterodactylus.util.fcp.message.EndListPeers;
33 import net.pterodactylus.util.fcp.message.NodeHello;
34 import net.pterodactylus.util.fcp.message.Peer;
35 import net.pterodactylus.util.fcp.message.SSKKeypair;
36 import net.pterodactylus.util.io.Closer;
39 * An FCP connection to a Freenet node.
41 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
44 public class FcpConnection {
46 /** The default port for FCP v2. */
47 public static final int DEFAULT_PORT = 9481;
49 /** The list of FCP listeners. */
50 private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
52 /** The address of the node. */
53 private final InetAddress address;
55 /** The port number of the node’s FCP port. */
56 private final int port;
58 /** The remote socket. */
59 private Socket remoteSocket;
61 /** The input stream from the node. */
62 private InputStream remoteInputStream;
64 /** The output stream to the node. */
65 private OutputStream remoteOutputStream;
67 /** The connection handler. */
68 private FcpConnectionHandler connectionHandler;
71 * Creates a new FCP connection to the Freenet node running on the given
72 * host, listening on the default port.
75 * The hostname of the Freenet node
76 * @throws UnknownHostException
77 * if <code>host</code> can not be resolved
79 public FcpConnection(String host) throws UnknownHostException {
80 this(host, DEFAULT_PORT);
84 * Creates a new FCP connection to the Freenet node running on the given
85 * host, listening on the given port.
88 * The hostname of the Freenet node
90 * The port number of the node’s FCP port
91 * @throws UnknownHostException
92 * if <code>host</code> can not be resolved
94 public FcpConnection(String host, int port) throws UnknownHostException {
95 this(InetAddress.getByName(host), port);
99 * Creates a new FCP connection to the Freenet node running at the given
100 * address, listening on the default port.
103 * The address of the Freenet node
105 public FcpConnection(InetAddress address) {
106 this(address, DEFAULT_PORT);
110 * Creates a new FCP connection to the Freenet node running at the given
111 * address, listening on the given port.
114 * The address of the Freenet node
116 * The port number of the node’s FCP port
118 public FcpConnection(InetAddress address, int port) {
119 this.address = address;
124 // LISTENER MANAGEMENT
128 * Adds the given listener to the list of listeners.
131 * The listener to add
133 public void addFcpListener(FcpListener fcpListener) {
134 fcpListeners.add(fcpListener);
138 * Removes the given listener from the list of listeners.
141 * The listener to remove
143 public void removeFcpListener(FcpListener fcpListener) {
144 fcpListeners.remove(fcpListener);
148 * Notifies listeners that a “NodeHello” message was received.
150 * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
152 * The “NodeHello” message
154 private void fireReceivedNodeHello(NodeHello nodeHello) {
155 for (FcpListener fcpListener: fcpListeners) {
156 fcpListener.receivedNodeHello(this, nodeHello);
161 * Notifies listeners that a “CloseConnectionDuplicateClientName” message
164 * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
165 * CloseConnectionDuplicateClientName)
166 * @param closeConnectionDuplicateClientName
167 * The “CloseConnectionDuplicateClientName” message
169 private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
170 for (FcpListener fcpListener: fcpListeners) {
171 fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
176 * Notifies listeners that a “SSKKeypair” message was received.
178 * @see FcpListener#receivedSSKKeypair(FcpConnection, SSKKeypair)
180 * The “SSKKeypair” message
182 private void fireReceivedSSKKeypair(SSKKeypair sskKeypair) {
183 for (FcpListener fcpListener: fcpListeners) {
184 fcpListener.receivedSSKKeypair(this, sskKeypair);
189 * Notifies listeners that a “Peer” message was received.
194 private void fireReceivedPeer(Peer peer) {
195 for (FcpListener fcpListener: fcpListeners) {
196 fcpListener.receivedPeer(this, peer);
201 * Notifies all listeners that an “EndListPeers” message was received.
203 * @param endListPeers
204 * The “EndListPeers” message
206 private void fireReceivedEndListPeers(EndListPeers endListPeers) {
207 for (FcpListener fcpListener: fcpListeners) {
208 fcpListener.receivedEndListPeers(this, endListPeers);
213 * Notifies all registered listeners that a message has been received.
215 * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
217 * The message that was received
219 private void fireMessageReceived(FcpMessage fcpMessage) {
220 for (FcpListener fcpListener: fcpListeners) {
221 fcpListener.receivedMessage(this, fcpMessage);
230 * Connects to the node.
232 * @throws IOException
233 * if an I/O error occurs
234 * @throws IllegalStateException
235 * if there is already a connection to the node
237 public synchronized void connect() throws IOException, IllegalStateException {
238 if (connectionHandler != null) {
239 throw new IllegalStateException("already connected, disconnect first");
241 remoteSocket = new Socket(address, port);
242 remoteInputStream = remoteSocket.getInputStream();
243 remoteOutputStream = remoteSocket.getOutputStream();
244 new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
248 * Disconnects from the node. If there is no connection to the node, this
249 * method does nothing.
251 public synchronized void disconnect() {
252 if (connectionHandler == null) {
255 Closer.close(remoteSocket);
256 connectionHandler.stop();
257 connectionHandler = null;
261 * Sends the given FCP message.
264 * The FCP message to send
265 * @throws IOException
266 * if an I/O error occurs
268 public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
269 System.out.println("sending message: " + fcpMessage.getName());
270 fcpMessage.write(remoteOutputStream);
274 // PACKAGE-PRIVATE METHODS
278 * Handles the given message, notifying listeners. This message should only
279 * be called by {@link FcpConnectionHandler}.
282 * The received message
284 void handleMessage(FcpMessage fcpMessage) {
285 String messageName = fcpMessage.getName();
286 if ("Peer".equals(messageName)) {
287 fireReceivedPeer(new Peer(fcpMessage));
288 } else if ("EndListPeers".equals(messageName)) {
289 fireReceivedEndListPeers(new EndListPeers(fcpMessage));
290 } else if ("SSKKeypair".equals(messageName)) {
291 fireReceivedSSKKeypair(new SSKKeypair(fcpMessage));
292 } else if ("NodeHello".equals(messageName)) {
293 fireReceivedNodeHello(new NodeHello(fcpMessage));
294 } else if ("CloseConnectionDuplicateClientName".equals(messageName)) {
295 fireReceivedCloseConnectionDuplicateClientName(new CloseConnectionDuplicateClientName(fcpMessage));
297 fireMessageReceived(fcpMessage);