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.HashMap;
30 import java.util.List;
32 import java.util.StringTokenizer;
33 import java.util.Map.Entry;
35 import net.pterodactylus.util.io.Closer;
40 * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
43 public class FcpConnection {
45 public static final int DEFAULT_PORT = 9481;
47 private final Object messageWaitSync = new Object();
48 private FcpMessage receivedMessage = null;
50 private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
52 private final InetAddress address;
53 private final int port;
54 private final String clientName;
56 private Socket remoteSocket;
57 private InputStream remoteInputStream;
58 private OutputStream remoteOutputStream;
59 private FcpConnectionHandler connectionHandler;
60 private boolean connected;
62 public FcpConnection(String host, String clientName) throws UnknownHostException {
63 this(host, DEFAULT_PORT, clientName);
66 public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
67 this(InetAddress.getByName(host), port, clientName);
70 public FcpConnection(InetAddress address, String clientName) {
71 this(address, DEFAULT_PORT, clientName);
74 public FcpConnection(InetAddress address, int port, String clientName) {
75 this.address = address;
77 this.clientName = clientName;
81 // LISTENER MANAGEMENT
85 * Adds the given listener to the list of listeners.
90 public void addFcpListener(FcpListener fcpListener) {
91 fcpListeners.add(fcpListener);
95 * Removes the given listener from the list of listeners.
98 * The listener to remove
100 public void removeFcpListener(FcpListener fcpListener) {
101 fcpListeners.remove(fcpListener);
105 * Notifies all registered listeners that a message has been received.
107 * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
109 * The message that was received
111 private void fireMessageReceived(FcpMessage fcpMessage) {
112 for (FcpListener fcpListener: fcpListeners) {
113 fcpListener.receivedMessage(this, fcpMessage);
122 * Connects to the node.
124 * @throws IOException
125 * if an I/O error occurs
126 * @throws IllegalStateException
127 * if there is already a connection to the node
129 public synchronized void connect() throws IOException, IllegalStateException {
130 if (connectionHandler != null) {
131 throw new IllegalStateException("already connected, disconnect first");
133 remoteSocket = new Socket(address, port);
134 remoteInputStream = remoteSocket.getInputStream();
135 remoteOutputStream = remoteSocket.getOutputStream();
137 new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
141 * Disconnects from the node. If there is no connection to the node, this
142 * method does nothing.
144 public synchronized void disconnect() {
145 if (connectionHandler == null) {
149 Closer.close(remoteSocket);
150 connectionHandler.stop();
151 connectionHandler = null;
154 public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
155 System.out.println("sending message: " + fcpMessage.getName());
156 fcpMessage.write(remoteOutputStream);
160 * Sends a “ListPeer” command to the node and returns the properties of the
163 * @param nodeIdentifier
164 * The name (except for OpenNet nodes), the identity or the
165 * node’s “address:port” pair
166 * @return The properties of the peer, or <code>null</code> if the peer is
168 * @throws IOException
169 * @throws FcpException
171 public Map<String, String> sendListPeer(String nodeIdentifier) throws IOException, FcpException {
172 FcpMessage listPeerMessage = new FcpMessage("ListPeer");
173 listPeerMessage.setField("NodeIdentifier", nodeIdentifier);
174 sendMessage(listPeerMessage);
175 FcpMessage returnMessage = waitForMessage("Peer", "UnknownNodeIdentifier");
176 if (returnMessage.getName().equals("Peer")) {
177 return returnMessage.getFields();
182 void handleMessage(FcpMessage fcpMessage) {
183 fireMessageReceived(fcpMessage);
186 public List<Map<String, String>> sendListPeers(boolean withMetadata, boolean withVolatile) throws IOException, FcpException {
187 FcpMessage listPeersMessage = new FcpMessage("ListPeers");
188 listPeersMessage.setField("WithMetadata", String.valueOf(withMetadata));
189 listPeersMessage.setField("WithVolatile", String.valueOf(withVolatile));
190 sendMessage(listPeersMessage);
191 List<Map<String, String>> peers = new ArrayList<Map<String, String>>();
193 FcpMessage returnMessage = waitForMessage("Peer", "EndListPeers");
194 if (returnMessage.getName().equals("EndListPeers")) {
197 peers.add(returnMessage.getFields());
202 public List<Map<String, String>> sendListPeerNotes(String nodeIdentifier) throws IOException, FcpException {
203 FcpMessage listPeerNotesMessage = new FcpMessage("ListPeerNotes");
204 listPeerNotesMessage.setField("NodeIdentifier", nodeIdentifier);
205 sendMessage(listPeerNotesMessage);
206 List<Map<String, String>> peerNotes = new ArrayList<Map<String, String>>();
208 FcpMessage returnMessage = waitForMessage("PeerNote", "EndListPeerNotes");
209 if (returnMessage.getName().equals("EndListPeerNotes")) {
212 peerNotes.add(returnMessage.getFields());
217 public void sendTestDDARequest(String directory, boolean wantReadDirectory, boolean wantWriteDirectory) throws IOException, FcpException {
218 FcpMessage testDDARequestMessage = new FcpMessage("TestDDARequest");
219 testDDARequestMessage.setField("Directory", directory);
220 testDDARequestMessage.setField("WantReadDirectory", String.valueOf(wantReadDirectory));
221 testDDARequestMessage.setField("WantWriteDirectory", String.valueOf(wantWriteDirectory));
222 sendMessage(testDDARequestMessage);
225 public FcpKeyPair generateSSK() throws IOException, FcpException {
226 FcpMessage generateSSKMessage = new FcpMessage("GenerateSSK");
227 String identifier = hashCode() + String.valueOf(System.currentTimeMillis());
228 generateSSKMessage.setField("Identifier", identifier);
229 sendMessage(generateSSKMessage);
230 FcpMessage returnMessage = waitForMessage("SSKKeypair(Identifier=" + identifier + ")");
231 String publicKey = returnMessage.getField("RequestURI");
232 String privateKey = returnMessage.getField("InsertURI");
233 return new FcpKeyPair(publicKey, privateKey);
240 public FcpMessage waitForMessage(String... messageNames) throws FcpException {
241 FcpMessage oldMessage = null;
242 synchronized (messageWaitSync) {
244 while (receivedMessage == oldMessage) {
245 System.out.println("waiting for receivedMessage");
247 messageWaitSync.wait();
248 } catch (InterruptedException ie1) {
251 System.out.println("got message: " + receivedMessage.getName());
252 String receivedMessageName = receivedMessage.getName();
253 if ("ProtocolError".equals(receivedMessageName)) {
254 int code = Integer.valueOf(receivedMessage.getField("Code"));
255 boolean fatal = Boolean.valueOf(receivedMessage.getField("Fatal"));
256 boolean global = Boolean.valueOf(receivedMessage.getField("Global"));
257 String codeDescription = receivedMessage.getField("CodeDescription");
258 String extraDescription = receivedMessage.getField("ExtraDescription");
259 String identifier = receivedMessage.getField("Identifier");
260 FcpProtocolException fcpProtocolException = new FcpProtocolException(code, fatal, global);
261 fcpProtocolException.setCodeDescription(codeDescription);
262 fcpProtocolException.setExtraDescription(extraDescription);
263 fcpProtocolException.setIdentifier(identifier);
264 throw fcpProtocolException;
266 for (String messageName: messageNames) {
267 int firstBracket = messageName.indexOf('(');
268 Map<String, String> wantedIdentifiers = new HashMap<String, String>();
269 if (firstBracket > -1) {
270 StringTokenizer identifierTokens = new StringTokenizer(messageName.substring(firstBracket), "()");
271 while (identifierTokens.hasMoreTokens()) {
272 String identifierToken = identifierTokens.nextToken();
273 int equalSign = identifierToken.indexOf('=');
274 if (equalSign > -1) {
275 wantedIdentifiers.put(identifierToken.substring(0, equalSign), identifierToken.substring(equalSign + 1));
278 messageName = messageName.substring(0, firstBracket);
280 if (receivedMessageName.equals(messageName)) {
281 boolean found = true;
282 for (Entry<String, String> wantedIdentifier: wantedIdentifiers.entrySet()) {
283 System.out.println("key: " + wantedIdentifier.getKey() + ", value: " + wantedIdentifier.getValue() + ", msg: " + receivedMessage.getField(wantedIdentifier.getKey()));
284 if (!wantedIdentifier.getValue().equals(receivedMessage.getField(wantedIdentifier.getKey()))) {
290 System.out.println("message found");
291 FcpMessage foundMessage = receivedMessage;
292 receivedMessage = null;
293 messageWaitSync.notifyAll();
298 oldMessage = receivedMessage;