improved (and hopefully final) design
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 9 Apr 2008 09:43:44 +0000 (09:43 +0000)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 9 Apr 2008 09:43:44 +0000 (09:43 +0000)
git-svn-id: http://trooper/svn/projects/jSite/trunk@654 c3eda9e8-030b-0410-8277-bc7414b0a119

13 files changed:
src/net/pterodactylus/util/fcp/FcpConnection.java
src/net/pterodactylus/util/fcp/FcpListener.java
src/net/pterodactylus/util/fcp/FcpMessage.java
src/net/pterodactylus/util/fcp/FcpUtils.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java [deleted file]
src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java [deleted file]
src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java [deleted file]
src/net/pterodactylus/util/fcp/message/BaseMessage.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/ClientHello.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/CloseConnectionDuplicateClientName.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/GenerateSSK.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/NodeHello.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/message/package-info.java [new file with mode: 0644]

index 484b46c..4582eca 100644 (file)
@@ -26,55 +26,95 @@ import java.net.InetAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.Map.Entry;
 
+import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.NodeHello;
 import net.pterodactylus.util.io.Closer;
 
 /**
- * TODO
+ * An FCP connection to a Freenet node.
  * 
  * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
  * @version $Id$
  */
 public class FcpConnection {
 
+       /** The default port for FCP v2. */
        public static final int DEFAULT_PORT = 9481;
 
-       private final Object messageWaitSync = new Object();
-       private FcpMessage receivedMessage = null;
-
+       /** The list of FCP listeners. */
        private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
 
+       /** The address of the node. */
        private final InetAddress address;
+
+       /** The port number of the node’s FCP port. */
        private final int port;
-       private final String clientName;
 
+       /** The remote socket. */
        private Socket remoteSocket;
+
+       /** The input stream from the node. */
        private InputStream remoteInputStream;
+
+       /** The output stream to the node. */
        private OutputStream remoteOutputStream;
+
+       /** The connection handler. */
        private FcpConnectionHandler connectionHandler;
-       private boolean connected;
 
-       public FcpConnection(String host, String clientName) throws UnknownHostException {
-               this(host, DEFAULT_PORT, clientName);
+       /**
+        * Creates a new FCP connection to the Freenet node running on the given
+        * host, listening on the default port.
+        * 
+        * @param host
+        *            The hostname of the Freenet node
+        * @throws UnknownHostException
+        *             if <code>host</code> can not be resolved
+        */
+       public FcpConnection(String host) throws UnknownHostException {
+               this(host, DEFAULT_PORT);
        }
 
-       public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
-               this(InetAddress.getByName(host), port, clientName);
+       /**
+        * Creates a new FCP connection to the Freenet node running on the given
+        * host, listening on the given port.
+        * 
+        * @param host
+        *            The hostname of the Freenet node
+        * @param port
+        *            The port number of the node’s FCP port
+        * @throws UnknownHostException
+        *             if <code>host</code> can not be resolved
+        */
+       public FcpConnection(String host, int port) throws UnknownHostException {
+               this(InetAddress.getByName(host), port);
        }
 
-       public FcpConnection(InetAddress address, String clientName) {
-               this(address, DEFAULT_PORT, clientName);
+       /**
+        * Creates a new FCP connection to the Freenet node running at the given
+        * address, listening on the default port.
+        * 
+        * @param address
+        *            The address of the Freenet node
+        */
+       public FcpConnection(InetAddress address) {
+               this(address, DEFAULT_PORT);
        }
 
-       public FcpConnection(InetAddress address, int port, String clientName) {
+       /**
+        * Creates a new FCP connection to the Freenet node running at the given
+        * address, listening on the given port.
+        * 
+        * @param address
+        *            The address of the Freenet node
+        * @param port
+        *            The port number of the node’s FCP port
+        */
+       public FcpConnection(InetAddress address, int port) {
                this.address = address;
                this.port = port;
-               this.clientName = clientName;
        }
 
        //
@@ -102,6 +142,34 @@ public class FcpConnection {
        }
 
        /**
+        * Notifies listeners that a “NodeHello” message was received.
+        * 
+        * @see FcpListener#receivedNodeHello(FcpConnection, NodeHello)
+        * @param nodeHello
+        *            The “NodeHello” message
+        */
+       private void fireReceivedNodeHello(NodeHello nodeHello) {
+               for (FcpListener fcpListener: fcpListeners) {
+                       fcpListener.receivedNodeHello(this, nodeHello);
+               }
+       }
+
+       /**
+        * Notifies listeners that a “CloseConnectionDuplicateClientName” message
+        * was received.
+        * 
+        * @see FcpListener#receivedCloseConnectionDuplicateClientName(FcpConnection,
+        *      CloseConnectionDuplicateClientName)
+        * @param closeConnectionDuplicateClientName
+        *            The “CloseConnectionDuplicateClientName” message
+        */
+       private void fireReceivedCloseConnectionDuplicateClientName(CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
+               for (FcpListener fcpListener: fcpListeners) {
+                       fcpListener.receivedCloseConnectionDuplicateClientName(this, closeConnectionDuplicateClientName);
+               }
+       }
+
+       /**
         * Notifies all registered listeners that a message has been received.
         * 
         * @see FcpListener#receivedMessage(FcpConnection, FcpMessage)
@@ -133,7 +201,6 @@ public class FcpConnection {
                remoteSocket = new Socket(address, port);
                remoteInputStream = remoteSocket.getInputStream();
                remoteOutputStream = remoteSocket.getOutputStream();
-               connected = true;
                new Thread(connectionHandler = new FcpConnectionHandler(this, remoteInputStream)).start();
        }
 
@@ -145,158 +212,43 @@ public class FcpConnection {
                if (connectionHandler == null) {
                        return;
                }
-               connected = false;
                Closer.close(remoteSocket);
                connectionHandler.stop();
                connectionHandler = null;
        }
 
-       public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
-               System.out.println("sending message: " + fcpMessage.getName());
-               fcpMessage.write(remoteOutputStream);
-       }
-
        /**
-        * Sends a “ListPeer” command to the node and returns the properties of the
-        * peer.
+        * Sends the given FCP message.
         * 
-        * @param nodeIdentifier
-        *            The name (except for OpenNet nodes), the identity or the
-        *            node’s “address:port” pair
-        * @return The properties of the peer, or <code>null</code> if the peer is
-        *         unknown
+        * @param fcpMessage
+        *            The FCP message to send
         * @throws IOException
-        * @throws FcpException
+        *             if an I/O error occurs
         */
-       public Map<String, String> sendListPeer(String nodeIdentifier) throws IOException, FcpException {
-               FcpMessage listPeerMessage = new FcpMessage("ListPeer");
-               listPeerMessage.setField("NodeIdentifier", nodeIdentifier);
-               sendMessage(listPeerMessage);
-               FcpMessage returnMessage = waitForMessage("Peer", "UnknownNodeIdentifier");
-               if (returnMessage.getName().equals("Peer")) {
-                       return returnMessage.getFields();
-               }
-               return null;
-       }
-       
-       void handleMessage(FcpMessage fcpMessage) {
-               fireMessageReceived(fcpMessage);
-       }
-
-       public List<Map<String, String>> sendListPeers(boolean withMetadata, boolean withVolatile) throws IOException, FcpException {
-               FcpMessage listPeersMessage = new FcpMessage("ListPeers");
-               listPeersMessage.setField("WithMetadata", String.valueOf(withMetadata));
-               listPeersMessage.setField("WithVolatile", String.valueOf(withVolatile));
-               sendMessage(listPeersMessage);
-               List<Map<String, String>> peers = new ArrayList<Map<String, String>>();
-               while (true) {
-                       FcpMessage returnMessage = waitForMessage("Peer", "EndListPeers");
-                       if (returnMessage.getName().equals("EndListPeers")) {
-                               break;
-                       }
-                       peers.add(returnMessage.getFields());
-               }
-               return peers;
-       }
-
-       public List<Map<String, String>> sendListPeerNotes(String nodeIdentifier) throws IOException, FcpException {
-               FcpMessage listPeerNotesMessage = new FcpMessage("ListPeerNotes");
-               listPeerNotesMessage.setField("NodeIdentifier", nodeIdentifier);
-               sendMessage(listPeerNotesMessage);
-               List<Map<String, String>> peerNotes = new ArrayList<Map<String, String>>();
-               while (true) {
-                       FcpMessage returnMessage = waitForMessage("PeerNote", "EndListPeerNotes");
-                       if (returnMessage.getName().equals("EndListPeerNotes")) {
-                               break;
-                       }
-                       peerNotes.add(returnMessage.getFields());
-               }
-               return peerNotes;
-       }
-
-       public void sendTestDDARequest(String directory, boolean wantReadDirectory, boolean wantWriteDirectory) throws IOException, FcpException {
-               FcpMessage testDDARequestMessage = new FcpMessage("TestDDARequest");
-               testDDARequestMessage.setField("Directory", directory);
-               testDDARequestMessage.setField("WantReadDirectory", String.valueOf(wantReadDirectory));
-               testDDARequestMessage.setField("WantWriteDirectory", String.valueOf(wantWriteDirectory));
-               sendMessage(testDDARequestMessage);
-       }
-
-       public FcpKeyPair generateSSK() throws IOException, FcpException {
-               FcpMessage generateSSKMessage = new FcpMessage("GenerateSSK");
-               String identifier = hashCode() + String.valueOf(System.currentTimeMillis());
-               generateSSKMessage.setField("Identifier", identifier);
-               sendMessage(generateSSKMessage);
-               FcpMessage returnMessage = waitForMessage("SSKKeypair(Identifier=" + identifier + ")");
-               String publicKey = returnMessage.getField("RequestURI");
-               String privateKey = returnMessage.getField("InsertURI");
-               return new FcpKeyPair(publicKey, privateKey);
+       public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
+               System.out.println("sending message: " + fcpMessage.getName());
+               fcpMessage.write(remoteOutputStream);
        }
 
        //
-       // PRIVATE METHODS
+       // PACKAGE-PRIVATE METHODS
        //
 
-       public FcpMessage waitForMessage(String... messageNames) throws FcpException {
-               FcpMessage oldMessage = null;
-               synchronized (messageWaitSync) {
-                       while (true) {
-                               while (receivedMessage == oldMessage) {
-                                       System.out.println("waiting for receivedMessage");
-                                       try {
-                                               messageWaitSync.wait();
-                                       } catch (InterruptedException ie1) {
-                                       }
-                               }
-                               System.out.println("got message: " + receivedMessage.getName());
-                               String receivedMessageName = receivedMessage.getName();
-                               if ("ProtocolError".equals(receivedMessageName)) {
-                                       int code = Integer.valueOf(receivedMessage.getField("Code"));
-                                       boolean fatal = Boolean.valueOf(receivedMessage.getField("Fatal"));
-                                       boolean global = Boolean.valueOf(receivedMessage.getField("Global"));
-                                       String codeDescription = receivedMessage.getField("CodeDescription");
-                                       String extraDescription = receivedMessage.getField("ExtraDescription");
-                                       String identifier = receivedMessage.getField("Identifier");
-                                       FcpProtocolException fcpProtocolException = new FcpProtocolException(code, fatal, global);
-                                       fcpProtocolException.setCodeDescription(codeDescription);
-                                       fcpProtocolException.setExtraDescription(extraDescription);
-                                       fcpProtocolException.setIdentifier(identifier);
-                                       throw fcpProtocolException;
-                               }
-                               for (String messageName: messageNames) {
-                                       int firstBracket = messageName.indexOf('(');
-                                       Map<String, String> wantedIdentifiers = new HashMap<String, String>();
-                                       if (firstBracket > -1) {
-                                               StringTokenizer identifierTokens = new StringTokenizer(messageName.substring(firstBracket), "()");
-                                               while (identifierTokens.hasMoreTokens()) {
-                                                       String identifierToken = identifierTokens.nextToken();
-                                                       int equalSign = identifierToken.indexOf('=');
-                                                       if (equalSign > -1) {
-                                                               wantedIdentifiers.put(identifierToken.substring(0, equalSign), identifierToken.substring(equalSign + 1));
-                                                       }
-                                               }
-                                               messageName = messageName.substring(0, firstBracket);
-                                       }
-                                       if (receivedMessageName.equals(messageName)) {
-                                               boolean found = true;
-                                               for (Entry<String, String> wantedIdentifier: wantedIdentifiers.entrySet()) {
-                                                       System.out.println("key: " + wantedIdentifier.getKey() + ", value: " + wantedIdentifier.getValue() + ", msg: " + receivedMessage.getField(wantedIdentifier.getKey()));
-                                                       if (!wantedIdentifier.getValue().equals(receivedMessage.getField(wantedIdentifier.getKey()))) {
-                                                               found = false;
-                                                               break;
-                                                       }
-                                               }
-                                               if (found) {
-                                                       System.out.println("message found");
-                                                       FcpMessage foundMessage = receivedMessage;
-                                                       receivedMessage = null;
-                                                       messageWaitSync.notifyAll();
-                                                       return foundMessage;
-                                               }
-                                       }
-                               }
-                               oldMessage = receivedMessage;
-                       }
+       /**
+        * Handles the given message, notifying listeners. This message should only
+        * be called by {@link FcpConnectionHandler}.
+        * 
+        * @param fcpMessage
+        *            The received message
+        */
+       void handleMessage(FcpMessage fcpMessage) {
+               String messageName = fcpMessage.getName();
+               if ("NodeHello".equals(messageName)) {
+                       fireReceivedNodeHello(new NodeHello(fcpMessage));
+               } else if ("CloseConnectionDuplicateClientName".equals(messageName)) {
+                       fireReceivedCloseConnectionDuplicateClientName(new CloseConnectionDuplicateClientName(fcpMessage));
+               } else {
+                       fireMessageReceived(fcpMessage);
                }
        }
 
index 1efcad8..21caa9c 100644 (file)
@@ -21,6 +21,9 @@ package net.pterodactylus.util.fcp;
 
 import java.util.EventListener;
 
+import net.pterodactylus.util.fcp.message.CloseConnectionDuplicateClientName;
+import net.pterodactylus.util.fcp.message.NodeHello;
+
 /**
  * Interface for objects that want to be notified on certain FCP events.
  * 
@@ -30,7 +33,30 @@ import java.util.EventListener;
 public interface FcpListener extends EventListener {
 
        /**
-        * Notifies listeners that a message has been received.
+        * Notifies listeners that a “NodeHello” message was received.
+        * 
+        * @param fcpConnection
+        *            The connection that received the message
+        * @param nodeHello
+        *            The “NodeHello” message
+        */
+       public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello);
+
+       /**
+        * Notifies all listeners that a “CloseConnectionDuplicateClientName”
+        * message was received.
+        * 
+        * @param fcpConnection
+        *            The connection that received the message
+        * @param closeConnectionDuplicateClientName
+        *            The “CloseConnectionDuplicateClientName” message
+        */
+       public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName);
+
+       /**
+        * Notifies listeners that a message has been received. This method is only
+        * called if {@link FcpConnection#handleMessage(FcpMessage)} does not
+        * recognize the message. Should that ever happen, please file a bug report!
         * 
         * @param fcpConnection
         *            The connection that received the message
index 6ff4ec6..3a23928 100644 (file)
@@ -56,26 +56,29 @@ public class FcpMessage implements Iterable<String> {
        public String getName() {
                return name;
        }
-       
+
        public void setField(String field, String value) {
+               if ((field == null) || (value == null)) {
+                       throw new NullPointerException(((field == null) ? "field " : "value ") + "must not be null");
+               }
                fields.put(field, value);
        }
-       
+
        public String getField(String field) {
                return fields.get(field);
        }
-       
+
        public Map<String, String> getFields() {
                return Collections.unmodifiableMap(fields);
        }
-       
+
        /**
         * {@inheritDoc}
         */
        public Iterator<String> iterator() {
                return fields.keySet().iterator();
        }
-       
+
        public void setDataInputStream(InputStream dataInputStream) {
                this.dataInputStream = dataInputStream;
        }
diff --git a/src/net/pterodactylus/util/fcp/FcpUtils.java b/src/net/pterodactylus/util/fcp/FcpUtils.java
new file mode 100644 (file)
index 0000000..9892610
--- /dev/null
@@ -0,0 +1,28 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Helper class with utility methods for the FCP protocol.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class FcpUtils {
+
+       /** Counter for unique identifiers. */
+       private static AtomicLong counter = new AtomicLong();
+
+       /**
+        * Returns a unique identifier.
+        * 
+        * @return A unique identifier
+        */
+       public static String getUniqueIdentifier() {
+               return new StringBuilder().append(System.currentTimeMillis()).append('-').append(counter.getAndIncrement()).toString();
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java b/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java
deleted file mode 100644 (file)
index 5e02dc6..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * jSite2 - FcpHighLevelClient.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import net.pterodactylus.util.fcp.FcpConnection;
-import net.pterodactylus.util.fcp.FcpException;
-import net.pterodactylus.util.fcp.FcpListener;
-import net.pterodactylus.util.fcp.FcpMessage;
-
-/**
- * High-level client for FCP connections.
- * 
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- */
-public class FcpHighLevelClient implements FcpListener {
-
-       /** The FCP connection of this client. */
-       private final FcpConnection fcpConnection;
-
-       /** Storage for incoming messages. */
-       private final List<FcpMessage> incomingMessages = Collections.synchronizedList(new ArrayList<FcpMessage>());
-
-       /** Registered handlers for incoming messages. */
-       private final List<Callback<?>> incomingMessageCallbacks = Collections.synchronizedList(new ArrayList<Callback<?>>());
-       
-       /**
-        * Creates a new high-level client that operates on the given FCP
-        * connection.
-        * 
-        * @param fcpConnection
-        *            The FCP connection to operate on
-        */
-       public FcpHighLevelClient(FcpConnection fcpConnection) {
-               this.fcpConnection = fcpConnection;
-               fcpConnection.addFcpListener(this);
-       }
-
-       //
-       // ACTIONS
-       //
-
-       /**
-        * Stops the high-level client.
-        */
-       public void stop() {
-               fcpConnection.removeFcpListener(this);
-       }
-
-       public Map<String, String> login(String clientName, final NodeHelloCallback nodeHelloCallback) throws FcpException, IOException {
-               FcpMessage clientHelloMessage = new FcpMessage("ClientHello");
-               clientHelloMessage.setField("Name", clientName);
-               clientHelloMessage.setField("ExpectedVersion", "2.0");
-               Callback<Map<String, String>> callback = new Callback<Map<String, String>>() {
-                       /**
-                        * {@inheritDoc}
-                        */
-                       @SuppressWarnings("synthetic-access")
-                       public CallbackContainer<Map<String, String>> consumeMessage(FcpMessage fcpMessage) throws FcpException {
-                               String messageName = fcpMessage.getName();
-                               if ("CloseConnectionDuplicateClientName".equals(messageName)) {
-                                       incomingMessages.remove(0);
-                                       if (nodeHelloCallback == null) {
-                                               throw new FcpException("duplicate client name");
-                                       }
-                                       nodeHelloCallback.closeConnectionDuplicateClientName();
-                               }
-                               if ("NodeHello".equals(messageName)) {
-                                       incomingMessages.remove(0);
-                                       return new CallbackContainer<Map<String, String>>(true, fcpMessage.getFields());
-                               }
-                               return new CallbackContainer<Map<String, String>>(false, null);
-                       }
-               };
-               synchronized (incomingMessages) {
-                       synchronized (incomingMessageCallbacks) {
-                               fcpConnection.sendMessage(clientHelloMessage);
-                               if (nodeHelloCallback != null) {
-                                       incomingMessageCallbacks.add(callback);
-                                       return null;
-                               }
-                       }
-                       return processMessages(callback);
-               }
-       }
-
-       //
-       // PRIVATE METHODS
-       //
-
-       private <T> T processMessages(Callback<T> callback) throws FcpException {
-               while (true) {
-                       FcpMessage fcpMessage;
-                       synchronized (incomingMessages) {
-                               while (incomingMessages.isEmpty()) {
-                                       try {
-                                               incomingMessages.wait();
-                                       } catch (InterruptedException ie1) {
-                                       }
-                               }
-                               fcpMessage = incomingMessages.get(0);
-                       }
-                       CallbackContainer<T> callbackContainer = callback.consumeMessage(fcpMessage);
-                       if (callbackContainer.isConsumed()) {
-                               incomingMessages.remove(0);
-                               return callbackContainer.getContent();
-                       }
-               }
-       }
-
-       //
-       // INTERFACE FcpListener
-       //
-
-       /**
-        * {@inheritDoc}
-        */
-       public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
-               if (this.fcpConnection != fcpConnection) {
-                       return;
-               }
-               synchronized (incomingMessages) {
-                       incomingMessages.add(fcpMessage);
-                       incomingMessages.notifyAll();
-               }
-               synchronized (incomingMessageCallbacks) {
-                       for (Callback<?> callback: incomingMessageCallbacks) {
-                               try {
-                                       CallbackContainer<?> callbackContainer = callback.consumeMessage(fcpMessage);
-                               } catch (FcpException e) {
-                               }
-                               
-                       }
-               }
-       }
-
-       public static interface Callback<T> {
-               
-               /**
-                * TODO
-                * 
-                * @param fcpMessage
-                * @return
-                */
-               public CallbackContainer<T> consumeMessage(FcpMessage fcpMessage) throws FcpException;
-               
-       }
-       
-       public static class CallbackContainer<T> {
-               
-               private final boolean consumed;
-               private final T content;
-               
-               public CallbackContainer(boolean consumed, T content) {
-                       this.consumed = consumed;
-                       this.content = content;
-               }
-               
-               /**
-                * TODO
-                * 
-                * @return
-                */
-               public boolean isConsumed() {
-                       return consumed;
-               }
-               
-               /**
-                * TODO
-                * 
-                * @return
-                */
-               public T getContent() {
-                       return content;
-               }
-               
-       }
-       
-}
diff --git a/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java b/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java
deleted file mode 100644 (file)
index 5163446..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * jSite2 - FcpNodeInformation.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-/**
- * Container for node information. This information is transmitted to the client
- * in the “NodeHello” response.
- * 
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- */
-public class FcpNodeInformation {
-
-       /** The “FCPVersion” field. */
-       private final String fcpVersion;
-
-       /** The “Version” field. */
-       private final String version;
-
-       /** The “Node” field. */
-       private final String node;
-
-       /** The “Testnet” field. */
-       private final boolean testnet;
-
-       /** The “CompressionCodecs” field. */
-       private final int compressionCodecs;
-
-       /** The “ConnectionIdentifier” field. */
-       private final String connectionIdentifier;
-
-       /** The “NodeLanguage” field. */
-       private final String nodeLanguage;
-
-       /**
-        * Creates a new node information object.
-        * 
-        * @param fcpVersion
-        *            The “FCPVersion” field
-        * @param version
-        *            The “Version” field
-        * @param node
-        *            The “Node” field
-        * @param testnet
-        *            The “Testnet” field
-        * @param compressionCodecs
-        *            The “CompressionCodecs” field
-        * @param connectionIdentifier
-        *            The “ConnectionIdentifier” field
-        * @param nodeLanguage
-        *            The “NodeLanguage” field
-        */
-       public FcpNodeInformation(String fcpVersion, String version, String node, boolean testnet, int compressionCodecs, String connectionIdentifier, String nodeLanguage) {
-               this.fcpVersion = fcpVersion;
-               this.version = version;
-               this.node = node;
-               this.testnet = testnet;
-               this.compressionCodecs = compressionCodecs;
-               this.connectionIdentifier = connectionIdentifier;
-               this.nodeLanguage = nodeLanguage;
-       }
-
-       /**
-        * Returns the “FCPVersion” field.
-        * 
-        * @return The “FCPVersion” field
-        */
-       public String getFcpVersion() {
-               return fcpVersion;
-       }
-
-       /**
-        * Returns the “Version” field.
-        * 
-        * @return The “Version” field
-        */
-       public String getVersion() {
-               return version;
-       }
-
-       /**
-        * Returns the “Node” field.
-        * 
-        * @return The “Node” field
-        */
-       public String getNode() {
-               return node;
-       }
-
-       /**
-        * Returns the “Testnet” field.
-        * 
-        * @return The “Testnet” field
-        */
-       public boolean isTestnet() {
-               return testnet;
-       }
-
-       /**
-        * Returns the “CompressionCodecs” field.
-        * 
-        * @return The “CompressionCodecs” field
-        */
-       public int getCompressionCodecs() {
-               return compressionCodecs;
-       }
-
-       /**
-        * Returns the “ConnectionIdentifier” field.
-        * 
-        * @return The “ConnectionIdentifier” field
-        */
-       public String getConnectionIdentifier() {
-               return connectionIdentifier;
-       }
-
-       /**
-        * Returns the “NodeLanguage” field.
-        * 
-        * @return The “NodeLanguage” field
-        */
-       public String getNodeLanguage() {
-               return nodeLanguage;
-       }
-
-}
diff --git a/src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java b/src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java
deleted file mode 100644 (file)
index ee0b893..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * jSite2 - NodeHelloCallback.java -
- * Copyright © 2008 David Roden
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-package net.pterodactylus.util.fcp.client;
-
-
-/**
- * TODO
- * @author David Roden &lt;droden@gmail.com&gt;
- * @version $Id$
- */
-public interface NodeHelloCallback {
-
-       public void nodeHello(FcpNodeInformation fcpNodeInformation);
-       public void closeConnectionDuplicateClientName();
-       
-}
diff --git a/src/net/pterodactylus/util/fcp/message/BaseMessage.java b/src/net/pterodactylus/util/fcp/message/BaseMessage.java
new file mode 100644 (file)
index 0000000..03613a6
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A basic message abstraction that wraps a received FCP message.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class BaseMessage {
+
+       /** The received message, wrapped here. */
+       private final FcpMessage receivedMessage;
+
+       /**
+        * Creates a new base message that wraps the given message.
+        * 
+        * @param receivedMessage
+        *            The FCP message that was received
+        */
+       public BaseMessage(FcpMessage receivedMessage) {
+               this.receivedMessage = receivedMessage;
+       }
+
+       /**
+        * Returns the name of the message.
+        * 
+        * @return The name of the message
+        */
+       public String getName() {
+               return receivedMessage.getName();
+       }
+
+       /**
+        * Returns the content of the field.
+        * 
+        * @param field
+        *            The name of the field
+        * @return The content of the field, or <code>null</code> if there is no
+        *         such field
+        */
+       public String getField(String field) {
+               return receivedMessage.getField(field);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/ClientHello.java b/src/net/pterodactylus/util/fcp/message/ClientHello.java
new file mode 100644 (file)
index 0000000..fe68321
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpConnection;
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A “ClientHello” message that <i>must</i> be sent to the node first thing
+ * after calling {@link FcpConnection#connect()}.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class ClientHello extends FcpMessage {
+
+       /**
+        * Creates a new “ClientHello” message with the given client name. The
+        * client name has to be unique to the node otherwise you will get a
+        * {@link CloseConnectionDuplicateClientName} response from the node!
+        * 
+        * @param clientName
+        *            The unique client name
+        */
+       public ClientHello(String clientName) {
+               this(clientName, "2.0");
+       }
+
+       /**
+        * Creates a new “ClientHello” message with the given client name. The
+        * client name has to be unique to the node otherwise you will get a
+        * {@link CloseConnectionDuplicateClientName} response from the node!
+        * 
+        * The expected FCP version is currently ignored by the node.
+        * 
+        * @param clientName
+        *            The unique client name
+        * @param expectedVersion
+        *            The FCP version that the node is expected to talk
+        */
+       public ClientHello(String clientName, String expectedVersion) {
+               super("ClientHello");
+               setField("Name", clientName);
+               setField("ExpectedVersion", expectedVersion);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/CloseConnectionDuplicateClientName.java b/src/net/pterodactylus/util/fcp/message/CloseConnectionDuplicateClientName.java
new file mode 100644 (file)
index 0000000..6d7aa64
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * A “CloseConnectionDuplicateClientName” message.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class CloseConnectionDuplicateClientName extends BaseMessage {
+
+       /**
+        * Creates a new CloseConnectionDuplicateClientName message that wraps the
+        * given message.
+        * 
+        * @param receivedMessage
+        *            The received message
+        */
+       public CloseConnectionDuplicateClientName(FcpMessage receivedMessage) {
+               super(receivedMessage);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/GenerateSSK.java b/src/net/pterodactylus/util/fcp/message/GenerateSSK.java
new file mode 100644 (file)
index 0000000..f6f2929
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+import net.pterodactylus.util.fcp.FcpUtils;
+
+/**
+ * A “GenerateSSK” message. This message tells the node to generate a new SSK
+ * key pair.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class GenerateSSK extends FcpMessage {
+
+       /**
+        * Creates a new “GenerateSSK” message.
+        */
+       public GenerateSSK() {
+               this(FcpUtils.getUniqueIdentifier());
+       }
+
+       /**
+        * Creates a new “GenerateSSK” message with the given client identifier.
+        * 
+        * @param clientIdentifier
+        *            The client identifier
+        */
+       public GenerateSSK(String clientIdentifier) {
+               super("GenerateSSK");
+               setField("Identifier", clientIdentifier);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/NodeHello.java b/src/net/pterodactylus/util/fcp/message/NodeHello.java
new file mode 100644 (file)
index 0000000..e704127
--- /dev/null
@@ -0,0 +1,207 @@
+/**
+ * © 2008 INA Service GmbH
+ */
+package net.pterodactylus.util.fcp.message;
+
+import net.pterodactylus.util.fcp.FcpMessage;
+
+/**
+ * Some convenience methods for parsing a “NodeHello” message from the node.
+ * 
+ * @author <a href="mailto:dr@ina-germany.de">David Roden</a>
+ * @version $Id$
+ */
+public class NodeHello extends BaseMessage {
+
+       /**
+        * @param receivedMessage
+        *            The received FCP message
+        */
+       public NodeHello(FcpMessage receivedMessage) {
+               super(receivedMessage);
+       }
+
+       /**
+        * Returns the build of the node. This may not be a number but also a string
+        * like “@custom@” in case you built the node yourself.
+        * 
+        * @return The build of the node
+        */
+       public String getBuild() {
+               return getField("Build");
+       }
+
+       /**
+        * Returns the build number of the node. This may not be a number but also a
+        * string like “@custom@” in case you built the node yourself.
+        * 
+        * @return The build number of the node, or <code>-1</code> if the build
+        *         number could not be determined
+        */
+       public int getBuildNumber() {
+               String build = getBuild();
+               try {
+                       return Integer.valueOf(build);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return -1;
+       }
+
+       /**
+        * Returns the number of compression codecs.
+        * 
+        * @return The number of compression codecs
+        */
+       public String getCompressionCodecs() {
+               return getField("CompressionCodecs");
+       }
+
+       /**
+        * Returns the number of compression codecs.
+        * 
+        * @return The number of compression codecs, or <code>-1</code> if the
+        *         number of compression codecs could not be determined
+        */
+       public int getCompressionCodecsNumber() {
+               String compressionCodecs = getCompressionCodecs();
+               try {
+                       return Integer.valueOf(compressionCodecs);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return -1;
+       }
+
+       /**
+        * Returns the unique connection identifier.
+        * 
+        * @return The connection identifier
+        */
+       public String getConnectionIdentifier() {
+               return getField("ConnectionIdentifier");
+       }
+
+       /**
+        * Returns the build of the external library file.
+        * 
+        * @return The build of the external library file
+        */
+       public String getExtBuild() {
+               return getField("ExtBuild");
+       }
+
+       /**
+        * Returns the build number of the external library file.
+        * 
+        * @return The build number of the external library file, or <code>-1</code>
+        *         if the build number could not be determined
+        */
+       public int getExtBuildNumber() {
+               String extBuild = getExtBuild();
+               try {
+                       return Integer.valueOf(extBuild);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return -1;
+       }
+
+       /**
+        * Returns the revision of the external library file.
+        * 
+        * @return The revision of the external library file
+        */
+       public String getExtRevision() {
+               return getField("ExtRevision");
+       }
+
+       /**
+        * Returns the revision number of the external library file.
+        * 
+        * @return The revision number of the external library file, or
+        *         <code>-1</code> if the revision number could not be determined
+        */
+       public int getExtRevisionNumber() {
+               String extRevision = getExtRevision();
+               try {
+                       return Integer.valueOf(extRevision);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return -1;
+       }
+
+       /**
+        * Returns the FCP version the node speaks.
+        * 
+        * @return The FCP version the node speaks
+        */
+       public String getFCPVersion() {
+               return getField("FCPVersion");
+       }
+
+       /**
+        * Returns the make of the node, e.g. “Fred” (freenet reference
+        * implementation).
+        * 
+        * @return The make of the node
+        */
+       public String getNode() {
+               return getField("Node");
+       }
+
+       /**
+        * Returns the language of the node as 2-letter code, e.g. “en” or “de”.
+        * 
+        * @return The language of the node
+        */
+       public String getNodeLanguage() {
+               return getField("NodeLanguage");
+       }
+
+       /**
+        * Returns the revision of the node.
+        * 
+        * @return The revision of the node
+        */
+       public String getRevision() {
+               return getField("Revision");
+       }
+
+       /**
+        * Returns the revision number of the node.
+        * 
+        * @return The revision number of the node, or <code>-1</code> if the
+        *         revision number coult not be determined
+        */
+       public int getRevisionNumber() {
+               String revision = getRevision();
+               try {
+                       return Integer.valueOf(revision);
+               } catch (NumberFormatException nfe1) {
+                       /* ignore. */
+               }
+               return -1;
+       }
+
+       /**
+        * Returns whether the node is currently is testnet mode.
+        * 
+        * @return <code>true</code> if the node is currently in testnet mode,
+        *         <code>false</code> otherwise
+        */
+       public boolean getTestnet() {
+               return Boolean.valueOf(getField("Testnet"));
+       }
+
+       /**
+        * Returns the version of the node.
+        * 
+        * @return The version of the node
+        */
+       public String getVersion() {
+               return getField("Version");
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/message/package-info.java b/src/net/pterodactylus/util/fcp/message/package-info.java
new file mode 100644 (file)
index 0000000..5426433
--- /dev/null
@@ -0,0 +1,4 @@
+/**
+ * Package that holds all the message types that are used in the communication with a Freenet Node.
+ */
+package net.pterodactylus.util.fcp.message;
\ No newline at end of file