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