add ListPeer and ListPeers commands and replies
[jSite2.git] / src / net / pterodactylus / util / fcp / FcpConnection.java
1 /*
2  * jSite2 - FpcConnection.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.util.fcp;
21
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;
30
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;
37
38 /**
39  * An FCP connection to a Freenet node.
40  * 
41  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
42  * @version $Id$
43  */
44 public class FcpConnection {
45
46         /** The default port for FCP v2. */
47         public static final int DEFAULT_PORT = 9481;
48
49         /** The list of FCP listeners. */
50         private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
51
52         /** The address of the node. */
53         private final InetAddress address;
54
55         /** The port number of the node’s FCP port. */
56         private final int port;
57
58         /** The remote socket. */
59         private Socket remoteSocket;
60
61         /** The input stream from the node. */
62         private InputStream remoteInputStream;
63
64         /** The output stream to the node. */
65         private OutputStream remoteOutputStream;
66
67         /** The connection handler. */
68         private FcpConnectionHandler connectionHandler;
69
70         /**
71          * Creates a new FCP connection to the Freenet node running on the given
72          * host, listening on the default port.
73          * 
74          * @param host
75          *            The hostname of the Freenet node
76          * @throws UnknownHostException
77          *             if <code>host</code> can not be resolved
78          */
79         public FcpConnection(String host) throws UnknownHostException {
80                 this(host, DEFAULT_PORT);
81         }
82
83         /**
84          * Creates a new FCP connection to the Freenet node running on the given
85          * host, listening on the given port.
86          * 
87          * @param host
88          *            The hostname of the Freenet node
89          * @param port
90          *            The port number of the node’s FCP port
91          * @throws UnknownHostException
92          *             if <code>host</code> can not be resolved
93          */
94         public FcpConnection(String host, int port) throws UnknownHostException {
95                 this(InetAddress.getByName(host), port);
96         }
97
98         /**
99          * Creates a new FCP connection to the Freenet node running at the given
100          * address, listening on the default port.
101          * 
102          * @param address
103          *            The address of the Freenet node
104          */
105         public FcpConnection(InetAddress address) {
106                 this(address, DEFAULT_PORT);
107         }
108
109         /**
110          * Creates a new FCP connection to the Freenet node running at the given
111          * address, listening on the given port.
112          * 
113          * @param address
114          *            The address of the Freenet node
115          * @param port
116          *            The port number of the node’s FCP port
117          */
118         public FcpConnection(InetAddress address, int port) {
119                 this.address = address;
120                 this.port = port;
121         }
122
123         //
124         // LISTENER MANAGEMENT
125         //
126
127         /**
128          * Adds the given listener to the list of listeners.
129          * 
130          * @param fcpListener
131          *            The listener to add
132          */
133         public void addFcpListener(FcpListener fcpListener) {
134                 fcpListeners.add(fcpListener);
135         }
136
137         /**
138          * Removes the given listener from the list of listeners.
139          * 
140          * @param fcpListener
141          *            The listener to remove
142          */
143         public void removeFcpListener(FcpListener fcpListener) {
144                 fcpListeners.remove(fcpListener);
145         }
146
147         /**
148          * Notifies listeners that a “NodeHello” message was received.
149          * 
150          * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
151          * @param nodeHello
152          *            The “NodeHello” message
153          */
154         private void fireReceivedNodeHello(NodeHello nodeHello) {
155                 for (FcpListener fcpListener: fcpListeners) {
156                         fcpListener.receivedNodeHello(this, nodeHello);
157                 }
158         }
159
160         /**
161          * Notifies listeners that a “CloseConnectionDuplicateClientName” message
162          * was received.
163          * 
164          * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
165          *      CloseConnectionDuplicateClientName)
166          * @param closeConnectionDuplicateClientName
167          *            The “CloseConnectionDuplicateClientName” message
168          */
169         private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
170                 for (FcpListener fcpListener: fcpListeners) {
171                         fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
172                 }
173         }
174
175         /**
176          * Notifies listeners that a “SSKKeypair” message was received.
177          * 
178          * @see FcpListener#receivedSSKKeypair(FcpConnection, SSKKeypair)
179          * @param sskKeypair
180          *            The “SSKKeypair” message
181          */
182         private void fireReceivedSSKKeypair(SSKKeypair sskKeypair) {
183                 for (FcpListener fcpListener: fcpListeners) {
184                         fcpListener.receivedSSKKeypair(this, sskKeypair);
185                 }
186         }
187
188         /**
189          * Notifies listeners that a “Peer” message was received.
190          * 
191          * @param peer
192          *            The “Peer” message
193          */
194         private void fireReceivedPeer(Peer peer) {
195                 for (FcpListener fcpListener: fcpListeners) {
196                         fcpListener.receivedPeer(this, peer);
197                 }
198         }
199
200         /**
201          * Notifies all listeners that an “EndListPeers” message was received.
202          * 
203          * @param endListPeers
204          *            The “EndListPeers” message
205          */
206         private void fireReceivedEndListPeers(EndListPeers endListPeers) {
207                 for (FcpListener fcpListener: fcpListeners) {
208                         fcpListener.receivedEndListPeers(this, endListPeers);
209                 }
210         }
211
212         /**
213          * Notifies all registered listeners that a message has been received.
214          * 
215          * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
216          * @param fcpMessage
217          *            The message that was received
218          */
219         private void fireMessageReceived(FcpMessage fcpMessage) {
220                 for (FcpListener fcpListener: fcpListeners) {
221                         fcpListener.receivedMessage(this, fcpMessage);
222                 }
223         }
224
225         //
226         // ACTIONS
227         //
228
229         /**
230          * Connects to the node.
231          * 
232          * @throws IOException
233          *             if an I/O error occurs
234          * @throws IllegalStateException
235          *             if there is already a connection to the node
236          */
237         public synchronized void connect() throws IOException, IllegalStateException {
238                 if (connectionHandler != null) {
239                         throw new IllegalStateException("already connected, disconnect first");
240                 }
241                 remoteSocket = new Socket(address, port);
242                 remoteInputStream = remoteSocket.getInputStream();
243                 remoteOutputStream = remoteSocket.getOutputStream();
244                 new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
245         }
246
247         /**
248          * Disconnects from the node. If there is no connection to the node, this
249          * method does nothing.
250          */
251         public synchronized void disconnect() {
252                 if (connectionHandler == null) {
253                         return;
254                 }
255                 Closer.close(remoteSocket);
256                 connectionHandler.stop();
257                 connectionHandler = null;
258         }
259
260         /**
261          * Sends the given FCP message.
262          * 
263          * @param fcpMessage
264          *            The FCP message to send
265          * @throws IOException
266          *             if an I/O error occurs
267          */
268         public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
269                 System.out.println("sending message: " + fcpMessage.getName());
270                 fcpMessage.write(remoteOutputStream);
271         }
272
273         //
274         // PACKAGE-PRIVATE METHODS
275         //
276
277         /**
278          * Handles the given message, notifying listeners. This message should only
279          * be called by {@link FcpConnectionHandler}.
280          * 
281          * @param fcpMessage
282          *            The received message
283          */
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));
296                 } else {
297                         fireMessageReceived(fcpMessage);
298                 }
299         }
300
301 }