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 boolean connected;
61 public FcpConnection(String host, String clientName) throws UnknownHostException {
62 this(host, DEFAULT_PORT, clientName);
65 public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
66 this(InetAddress.getByName(host), port, clientName);
69 public FcpConnection(InetAddress address, String clientName) {
70 this(address, DEFAULT_PORT, clientName);
73 public FcpConnection(InetAddress address, int port, String clientName) {
74 this.address = address;
76 this.clientName = clientName;
80 // LISTENER MANAGEMENT
83 public void addFcpListener(FcpListener fcpListener) {
84 fcpListeners.add(fcpListener);
87 public void removeFcpListener(FcpListener fcpListener) {
88 fcpListeners.remove(fcpListener);
91 private void fireNodeHello(Map<String, String> nodeProperties) {
92 for (FcpListener fcpListener: fcpListeners) {
93 fcpListener.fcpNodeHello(this, nodeProperties);
101 public synchronized void connect() throws FcpException, IOException {
102 System.out.println("connecting...");
103 remoteSocket = new Socket(address, port);
104 remoteInputStream = remoteSocket.getInputStream();
105 remoteOutputStream = remoteSocket.getOutputStream();
107 System.out.println("connected.");
108 new Thread(new FcpConnectionHandler(this, remoteInputStream)).start();
109 sendMessage(clientHelloMessage);
112 public synchronized void disconnect() {
114 Closer.close(remoteSocket);
118 * Sends a “ListPeer” command to the node and returns the properties of the
121 * @param nodeIdentifier
122 * The name (except for OpenNet nodes), the identity or the
123 * node’s “address:port” pair
124 * @return The properties of the peer, or <code>null</code> if the peer is
126 * @throws IOException
127 * @throws FcpException
129 public Map<String, String> sendListPeer(String nodeIdentifier) throws IOException, FcpException {
130 FcpMessage listPeerMessage = new FcpMessage("ListPeer");
131 listPeerMessage.setField("NodeIdentifier", nodeIdentifier);
132 sendMessage(listPeerMessage);
133 FcpMessage returnMessage = waitForMessage("Peer", "UnknownNodeIdentifier");
134 if (returnMessage.getName().equals("Peer")) {
135 return returnMessage.getFields();
140 public List<Map<String, String>> sendListPeers(boolean withMetadata, boolean withVolatile) throws IOException, FcpException {
141 FcpMessage listPeersMessage = new FcpMessage("ListPeers");
142 listPeersMessage.setField("WithMetadata", String.valueOf(withMetadata));
143 listPeersMessage.setField("WithVolatile", String.valueOf(withVolatile));
144 sendMessage(listPeersMessage);
145 List<Map<String, String>> peers = new ArrayList<Map<String, String>>();
147 FcpMessage returnMessage = waitForMessage("Peer", "EndListPeers");
148 if (returnMessage.getName().equals("EndListPeers")) {
151 peers.add(returnMessage.getFields());
156 public List<Map<String, String>> sendListPeerNotes(String nodeIdentifier) throws IOException, FcpException {
157 FcpMessage listPeerNotesMessage = new FcpMessage("ListPeerNotes");
158 listPeerNotesMessage.setField("NodeIdentifier", nodeIdentifier);
159 sendMessage(listPeerNotesMessage);
160 List<Map<String, String>> peerNotes = new ArrayList<Map<String, String>>();
162 FcpMessage returnMessage = waitForMessage("PeerNote", "EndListPeerNotes");
163 if (returnMessage.getName().equals("EndListPeerNotes")) {
166 peerNotes.add(returnMessage.getFields());
171 public void sendTestDDARequest(String directory, boolean wantReadDirectory, boolean wantWriteDirectory) throws IOException, FcpException {
172 FcpMessage testDDARequestMessage = new FcpMessage("TestDDARequest");
173 testDDARequestMessage.setField("Directory", directory);
174 testDDARequestMessage.setField("WantReadDirectory", String.valueOf(wantReadDirectory));
175 testDDARequestMessage.setField("WantWriteDirectory", String.valueOf(wantWriteDirectory));
176 sendMessage(testDDARequestMessage);
179 public FcpKeyPair generateSSK() throws IOException, FcpException {
180 FcpMessage generateSSKMessage = new FcpMessage("GenerateSSK");
181 String identifier = hashCode() + String.valueOf(System.currentTimeMillis());
182 generateSSKMessage.setField("Identifier", identifier);
183 sendMessage(generateSSKMessage);
184 FcpMessage returnMessage = waitForMessage("SSKKeypair(Identifier=" + identifier + ")");
185 String publicKey = returnMessage.getField("RequestURI");
186 String privateKey = returnMessage.getField("InsertURI");
187 return new FcpKeyPair(publicKey, privateKey);
191 // PACKAGE-PRIVATE METHODS
194 void handleMessage(FcpMessage fcpMessage) {
195 synchronized (messageWaitSync) {
196 while (receivedMessage != null) {
197 /* previous message has not yet been consumed */
198 System.out.println("waiting for message to be consumed...");
200 messageWaitSync.wait();
201 } catch (InterruptedException ie1) {
204 /* TODO - check whether to send events here or later. */
205 if ("NodeHello".equals(fcpMessage.getName())) {
206 fireNodeHello(fcpMessage.getFields());
208 System.out.println("setting receivedMessage");
209 receivedMessage = fcpMessage;
210 messageWaitSync.notifyAll();
218 public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
219 System.out.println("sending message: " + fcpMessage.getName());
220 fcpMessage.write(remoteOutputStream);
223 public FcpMessage waitForMessage(String... messageNames) throws FcpException {
224 FcpMessage oldMessage = null;
225 synchronized (messageWaitSync) {
227 while (receivedMessage == oldMessage) {
228 System.out.println("waiting for receivedMessage");
230 messageWaitSync.wait();
231 } catch (InterruptedException ie1) {
234 System.out.println("got message: " + receivedMessage.getName());
235 String receivedMessageName = receivedMessage.getName();
236 if ("ProtocolError".equals(receivedMessageName)) {
237 int code = Integer.valueOf(receivedMessage.getField("Code"));
238 boolean fatal = Boolean.valueOf(receivedMessage.getField("Fatal"));
239 boolean global = Boolean.valueOf(receivedMessage.getField("Global"));
240 String codeDescription = receivedMessage.getField("CodeDescription");
241 String extraDescription = receivedMessage.getField("ExtraDescription");
242 String identifier = receivedMessage.getField("Identifier");
243 FcpProtocolException fcpProtocolException = new FcpProtocolException(code, fatal, global);
244 fcpProtocolException.setCodeDescription(codeDescription);
245 fcpProtocolException.setExtraDescription(extraDescription);
246 fcpProtocolException.setIdentifier(identifier);
247 throw fcpProtocolException;
249 for (String messageName: messageNames) {
250 int firstBracket = messageName.indexOf('(');
251 Map<String, String> wantedIdentifiers = new HashMap<String, String>();
252 if (firstBracket > -1) {
253 StringTokenizer identifierTokens = new StringTokenizer(messageName.substring(firstBracket), "()");
254 while (identifierTokens.hasMoreTokens()) {
255 String identifierToken = identifierTokens.nextToken();
256 int equalSign = identifierToken.indexOf('=');
257 if (equalSign > -1) {
258 wantedIdentifiers.put(identifierToken.substring(0, equalSign), identifierToken.substring(equalSign + 1));
261 messageName = messageName.substring(0, firstBracket);
263 if (receivedMessageName.equals(messageName)) {
264 boolean found = true;
265 for (Entry<String, String> wantedIdentifier: wantedIdentifiers.entrySet()) {
266 System.out.println("key: " + wantedIdentifier.getKey() + ", value: " + wantedIdentifier.getValue() + ", msg: " + receivedMessage.getField(wantedIdentifier.getKey()));
267 if (!wantedIdentifier.getValue().equals(receivedMessage.getField(wantedIdentifier.getKey()))) {
273 System.out.println("message found");
274 FcpMessage foundMessage = receivedMessage;
275 receivedMessage = null;
276 messageWaitSync.notifyAll();
281 oldMessage = receivedMessage;