current state
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 8 Apr 2008 08:39:53 +0000 (08:39 +0000)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Tue, 8 Apr 2008 08:39:53 +0000 (08:39 +0000)
git-svn-id: http://trooper/svn/projects/jSite/trunk@651 c3eda9e8-030b-0410-8277-bc7414b0a119

15 files changed:
src/net/pterodactylus/jsite/gui/MainWindow.java
src/net/pterodactylus/jsite/gui/ProjectPanel.java
src/net/pterodactylus/jsite/gui/SwingInterface.java
src/net/pterodactylus/jsite/i18n/jSite.properties
src/net/pterodactylus/util/fcp/FcpConnection.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpConnectionHandler.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpException.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpKeyPair.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpListener.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpMessage.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpProtocolException.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/FcpTest.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java [new file with mode: 0644]
src/net/pterodactylus/util/fcp/client/NodeHelloCallback.java [new file with mode: 0644]

index 19982e9..ed2b1db 100644 (file)
@@ -36,6 +36,7 @@ import javax.swing.JToolBar;
 import javax.swing.SwingConstants;
 import javax.swing.border.EmptyBorder;
 
+import net.pterodactylus.jsite.core.Project;
 import net.pterodactylus.jsite.i18n.I18n;
 import net.pterodactylus.jsite.i18n.I18nable;
 import net.pterodactylus.jsite.i18n.gui.I18nAction;
@@ -72,6 +73,9 @@ public class MainWindow extends JFrame implements I18nable {
 
        /** The about menu. */
        private I18nMenu aboutMenu;
+       
+       /** The tabbed project pane. */
+       private JTabbedPane projectPane;
 
        /**
         * Creates a new main window that redirects all actions to the given swing
@@ -105,6 +109,19 @@ public class MainWindow extends JFrame implements I18nable {
                statusBar.setText(text);
        }
 
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Container getContentPane() {
+               return contentPane;
+       }
+
+       
+       public Project getSelectedProject() {
+               return null;
+       }
+
        //
        // PRIVATE METHODS
        //
@@ -187,11 +204,11 @@ public class MainWindow extends JFrame implements I18nable {
                getContentPane().add(upperPanel, BorderLayout.PAGE_START);
                contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));
 
-               JTabbedPane tabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
-               upperPanel.add(tabbedPane, BorderLayout.CENTER);
+               projectPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
+               upperPanel.add(projectPane, BorderLayout.CENTER);
 
                Box projectOverviewPanel = new Box(BoxLayout.PAGE_AXIS);
-               tabbedPane.add(I18n.get("mainWindow.pane.overview.title"), projectOverviewPanel);
+               projectPane.add(I18n.get("mainWindow.pane.overview.title"), projectOverviewPanel);
                projectOverviewPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
                projectOverviewPanel.add(Box.createVerticalGlue());
                JButton addProjectButton = new JButton(swingInterface.getAddProjectAction());
@@ -203,14 +220,6 @@ public class MainWindow extends JFrame implements I18nable {
 // getContentPane().add(lowerPanel, BorderLayout.CENTER);
        }
 
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public Container getContentPane() {
-               return contentPane;
-       }
-
        //
        // INTERFACE I18nable
        //
index c16f2a5..dd27016 100644 (file)
 package net.pterodactylus.jsite.gui;
 
 import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
 
 import javax.swing.BorderFactory;
+import javax.swing.JButton;
 import javax.swing.JPanel;
 
+import net.pterodactylus.jsite.core.Project;
+
 /**
  * A panel that contains all information about a project and lets the user edit
  * the properties of the project.
@@ -33,13 +38,35 @@ import javax.swing.JPanel;
  */
 public class ProjectPanel extends JPanel {
 
+       /** The Swing interface. */
+       private final SwingInterface swingInterface;
+       private final Project project;
+
        /**
         * Creates a new project panel.
+        * 
+        * @param swingInterface
+        *            The Swing interface
+        * @param project The project to display
         */
-       public ProjectPanel() {
+       public ProjectPanel(SwingInterface swingInterface, Project project) {
                super(new BorderLayout(12, 12));
+               this.swingInterface = swingInterface;
+               this.project = project;
                initComponents();
        }
+       
+       //
+       // ACCESSORS
+       //
+       
+       /**
+        * Returns the project that is displayed in this panel.
+        * @return The project of this panel
+        */
+       public Project getProject() {
+               return project;
+       }
 
        //
        // PRIVATE METHODS
@@ -50,7 +77,38 @@ public class ProjectPanel extends JPanel {
         */
        private void initComponents() {
                setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
-               
+
+               JPanel propertiesPanel = createPropertiesPanel();
+               add(propertiesPanel, BorderLayout.CENTER);
+
+               JPanel buttonPanel = createButtonPanel();
+               add(buttonPanel, BorderLayout.PAGE_END);
+       }
+
+       /**
+        * Creates the properties panel.
+        * 
+        * @return The properties panel
+        */
+       private JPanel createPropertiesPanel() {
+               JPanel propertiesPanel = new JPanel(new GridBagLayout());
+
+               return propertiesPanel;
+       }
+
+       /**
+        * Creates the button panel.
+        * 
+        * @return The button panel
+        */
+       private JPanel createButtonPanel() {
+               JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12));
+               buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12));
+
+               buttonPanel.add(new JButton(swingInterface.getDeleteProjectAction()));
+               buttonPanel.add(new JButton(swingInterface.getCloneProjectAction()));
+
+               return buttonPanel;
        }
 
 }
index 79b799d..d00ebe1 100644 (file)
@@ -34,6 +34,7 @@ import javax.swing.JOptionPane;
 import net.pterodactylus.jsite.core.Core;
 import net.pterodactylus.jsite.core.CoreListener;
 import net.pterodactylus.jsite.core.Node;
+import net.pterodactylus.jsite.core.Project;
 import net.pterodactylus.jsite.i18n.I18n;
 import net.pterodactylus.jsite.i18n.gui.I18nAction;
 import net.pterodactylus.util.io.Closer;
@@ -85,6 +86,12 @@ public class SwingInterface implements CoreListener {
        /** The “add project” action. */
        private I18nAction addProjectAction;
 
+       /** The “clone project” action. */
+       private I18nAction cloneProjectAction;
+
+       /** The “delete project” action. */
+       private I18nAction deleteProjectAction;
+
        /** The “about” dialog. */
        private AboutDialog aboutDialog;
 
@@ -226,6 +233,24 @@ public class SwingInterface implements CoreListener {
                return addProjectAction;
        }
 
+       /**
+        * Returns the “clone project” action.
+        * 
+        * @return The “clone project” action
+        */
+       I18nAction getCloneProjectAction() {
+               return cloneProjectAction;
+       }
+
+       /**
+        * Returns the “delete project” action.
+        * 
+        * @return The “delete project” action
+        */
+       I18nAction getDeleteProjectAction() {
+               return deleteProjectAction;
+       }
+
        //
        // ACTIONS
        //
@@ -252,7 +277,6 @@ public class SwingInterface implements CoreListener {
                /* initialize default stuff. */
                beautify = false;
                /* now read config. */
-               System.out.println("configDirectory: “" + configDirectory + "”");
                File configFile = new File(configDirectory, "swing-interface.properties");
                if (!configFile.exists() || !configFile.canRead() || !configFile.isFile()) {
                        System.err.println("could not find “" + configFile.getAbsolutePath() + "”!");
@@ -399,6 +423,26 @@ public class SwingInterface implements CoreListener {
                                addProject();
                        }
                };
+               cloneProjectAction = new I18nAction("mainWindow.button.cloneProject") {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               cloneProject();
+                       }
+               };
+               deleteProjectAction = new I18nAction("mainWindow.button.deleteProject") {
+
+                       /**
+                        * {@inheritDoc}
+                        */
+                       @SuppressWarnings("synthetic-access")
+                       public void actionPerformed(ActionEvent actionEvent) {
+                               deleteProject();
+                       }
+               };
        }
 
        /**
@@ -488,6 +532,21 @@ public class SwingInterface implements CoreListener {
         * Adds a project.
         */
        private void addProject() {
+               Project project = new Project();
+               project.setName("New Project");
+               project.setDescription("");
+       }
+
+       /**
+        * Clones a project.
+        */
+       private void cloneProject() {
+       }
+
+       /**
+        * Deletes a project.
+        */
+       private void deleteProject() {
        }
 
        //
index fd71773..1a79960 100644 (file)
@@ -107,6 +107,18 @@ mainWindow.button.addProject.accelerator: Ctrl-VK_A
 mainWindow.button.addProject.shortDescription: Add a project
 mainWindow.button.addProject.longDescription: Adds a project
 
+mainWindow.button.cloneProject.name: Clone Project
+mainWindow.button.cloneProject.mnemonic: VK_L
+mainWindow.button.cloneProject.accelerator: Ctrl-VK_L
+mainWindow.button.cloneProject.shortDescription: Clones the selected project
+mainWindow.button.cloneProject.longDescription: Clones the selected project
+
+mainWindow.button.deleteProject.name: Delete Project
+mainWindow.button.deleteProject.mnemonic: VK_E
+mainWindow.button.deleteProject.accelerator: Ctrl-VK_R
+mainWindow.button.deleteProject.shortDescription: Deletes the selected project
+mainWindow.button.deleteProject.longDescription: Deletes the selected project
+
 mainWindow.pane.overview.title: Project Overview
 
 #
diff --git a/src/net/pterodactylus/util/fcp/FcpConnection.java b/src/net/pterodactylus/util/fcp/FcpConnection.java
new file mode 100644 (file)
index 0000000..1b4d249
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * jSite2 - FpcConnection.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+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.io.Closer;
+
+/**
+ * TODO
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpConnection {
+
+       public static final int DEFAULT_PORT = 9481;
+
+       private final Object messageWaitSync = new Object();
+       private FcpMessage receivedMessage = null;
+
+       private final List<FcpListener> fcpListeners = new ArrayList<FcpListener>();
+
+       private final InetAddress address;
+       private final int port;
+       private final String clientName;
+
+       private Socket remoteSocket;
+       private InputStream remoteInputStream;
+       private OutputStream remoteOutputStream;
+       private boolean connected;
+
+       public FcpConnection(String host, String clientName) throws UnknownHostException {
+               this(host, DEFAULT_PORT, clientName);
+       }
+
+       public FcpConnection(String host, int port, String clientName) throws UnknownHostException {
+               this(InetAddress.getByName(host), port, clientName);
+       }
+
+       public FcpConnection(InetAddress address, String clientName) {
+               this(address, DEFAULT_PORT, clientName);
+       }
+
+       public FcpConnection(InetAddress address, int port, String clientName) {
+               this.address = address;
+               this.port = port;
+               this.clientName = clientName;
+       }
+
+       //
+       // LISTENER MANAGEMENT
+       //
+
+       public void addFcpListener(FcpListener fcpListener) {
+               fcpListeners.add(fcpListener);
+       }
+
+       public void removeFcpListener(FcpListener fcpListener) {
+               fcpListeners.remove(fcpListener);
+       }
+
+       private void fireNodeHello(Map<String, String> nodeProperties) {
+               for (FcpListener fcpListener: fcpListeners) {
+                       fcpListener.fcpNodeHello(this, nodeProperties);
+               }
+       }
+
+       //
+       // ACTIONS
+       //
+
+       public synchronized void connect() throws FcpException, IOException {
+               System.out.println("connecting...");
+               remoteSocket = new Socket(address, port);
+               remoteInputStream = remoteSocket.getInputStream();
+               remoteOutputStream = remoteSocket.getOutputStream();
+               connected = true;
+               System.out.println("connected.");
+               new Thread(new FcpConnectionHandler(this, remoteInputStream)).start();
+               sendMessage(clientHelloMessage);
+       }
+
+       public synchronized void disconnect() {
+               connected = false;
+               Closer.close(remoteSocket);
+       }
+
+       /**
+        * Sends a “ListPeer” command to the node and returns the properties of the
+        * peer.
+        * 
+        * @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
+        * @throws IOException
+        * @throws FcpException 
+        */
+       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;
+       }
+       
+       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);
+       }
+       
+       //
+       // PACKAGE-PRIVATE METHODS
+       //
+
+       void handleMessage(FcpMessage fcpMessage) {
+               synchronized (messageWaitSync) {
+                       while (receivedMessage != null) {
+                               /* previous message has not yet been consumed */
+                               System.out.println("waiting for message to be consumed...");
+                               try {
+                                       messageWaitSync.wait();
+                               } catch (InterruptedException ie1) {
+                               }
+                       }
+                       /* TODO - check whether to send events here or later. */
+                       if ("NodeHello".equals(fcpMessage.getName())) {
+                               fireNodeHello(fcpMessage.getFields());
+                       }
+                       System.out.println("setting receivedMessage");
+                       receivedMessage = fcpMessage;
+                       messageWaitSync.notifyAll();
+               }
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       public synchronized void sendMessage(FcpMessage fcpMessage) throws IOException {
+               System.out.println("sending message: " + fcpMessage.getName());
+               fcpMessage.write(remoteOutputStream);
+       }
+
+       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;
+                       }
+               }
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpConnectionHandler.java b/src/net/pterodactylus/util/fcp/FcpConnectionHandler.java
new file mode 100644 (file)
index 0000000..2693c1f
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * jSite2 - FcpConnectionHandler.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+/**
+ * TODO
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpConnectionHandler implements Runnable {
+
+       private final FcpConnection fcpConnection;
+       private final InputStream remoteInputStream;
+       
+       private boolean ignoreNextLinefeed;
+
+       public FcpConnectionHandler(FcpConnection fcpConnection, InputStream remoteInputStream) {
+               this.fcpConnection = fcpConnection;
+               this.remoteInputStream = remoteInputStream;
+       }
+
+       public void run() {
+               FcpMessage fcpMessage = null;
+               while (true) {
+                       try {
+                               String line = readLine();
+                               System.out.println("read line: " + line);
+                               if (line == null) {
+                                       break;
+                               }
+                               if (line.length() == 0) {
+                                       continue;
+                               }
+                               line = line.trim();
+                               if (fcpMessage == null) {
+                                       fcpMessage = new FcpMessage(line);
+                                       continue;
+                               }
+                               if ("EndMessage".equals(line)) {
+                                       fcpConnection.handleMessage(fcpMessage);
+                                       fcpMessage = null;
+                               }
+                               int equalSign = line.indexOf('=');
+                               if (equalSign == -1) {
+                                       /* something's fishy! */
+                                       continue;
+                               }
+                               String field = line.substring(0, equalSign);
+                               String value = line.substring(equalSign + 1);
+                               fcpMessage.setField(field, value);
+                       } catch (IOException e) {
+                       }
+               }
+       }
+
+       /**
+        * Reads bytes from {@link #remoteInputStream} until ‘\r’ or ‘\n’ are
+        * encountered and decodes the read bytes using UTF-8.
+        * 
+        * @return The decoded line
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       private String readLine() throws IOException {
+               byte[] readBytes = new byte[512];
+               int readIndex = 0;
+               while (true) {
+                       int nextByte = remoteInputStream.read();
+                       if (nextByte == -1) {
+                               if (readIndex == 0) {
+                                       return null;
+                               }
+                               break;
+                       }
+                       if (nextByte == 10) {
+                               if (!ignoreNextLinefeed) {
+                                       break;
+                               }
+                       }
+                       ignoreNextLinefeed = false;
+                       if (nextByte == 13) {
+                               ignoreNextLinefeed = true;
+                               break;
+                       }
+                       if (readIndex == readBytes.length) {
+                               /* recopy & enlarge array */
+                               byte[] newReadBytes = new byte[readBytes.length * 2];
+                               System.arraycopy(readBytes, 0, newReadBytes, 0, readBytes.length);
+                               readBytes = newReadBytes;
+                       }
+                       readBytes[readIndex++] = (byte) nextByte;
+               }
+               ByteBuffer byteBuffer = ByteBuffer.wrap(readBytes, 0, readIndex);
+               return Charset.forName("UTF-8").decode(byteBuffer).toString();
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpException.java b/src/net/pterodactylus/util/fcp/FcpException.java
new file mode 100644 (file)
index 0000000..8a998fe
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * jSite2 - FcpException.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;
+
+/**
+ * TODO
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpException extends Exception {
+
+       public FcpException() {
+       }
+
+       public FcpException(String message) {
+               super(message);
+       }
+
+       public FcpException(Throwable cause) {
+               super(cause);
+       }
+
+       public FcpException(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpKeyPair.java b/src/net/pterodactylus/util/fcp/FcpKeyPair.java
new file mode 100644 (file)
index 0000000..993ded2
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * jSite2 - FcpKeyPair.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;
+
+/**
+ * Container for an SSK keypair.
+ * 
+ * @author David Roden &lt;droden@gmail.com&gt;
+ * @version $Id$
+ */
+public class FcpKeyPair {
+
+       /** The public key. */
+       private final String publicKey;
+
+       /** The private key. */
+       private final String privateKey;
+
+       /**
+        * Creates a new keypair from the given keys.
+        * 
+        * @param publicKey
+        *            The public key
+        * @param privateKey
+        *            The private key
+        */
+       public FcpKeyPair(String publicKey, String privateKey) {
+               this.publicKey = publicKey;
+               this.privateKey = privateKey;
+       }
+
+       /**
+        * Returns the public key of this keypair.
+        * 
+        * @return The public key
+        */
+       public String getPublicKey() {
+               return publicKey;
+       }
+
+       /**
+        * Returns the private key of this keypair.
+        * 
+        * @return The private key
+        */
+       public String getPrivateKey() {
+               return privateKey;
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpListener.java b/src/net/pterodactylus/util/fcp/FcpListener.java
new file mode 100644 (file)
index 0000000..e3d6d00
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * jSite2 - FpcListener.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;
+
+import java.util.EventListener;
+import java.util.Map;
+
+
+/**
+ * TODO
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public interface FcpListener extends EventListener {
+
+       public void fcpConnected(FcpConnection fcpConnection);
+       public void fcpDisconnected(FcpConnection fcpConnection);
+       
+       //
+       // connection setup
+       //
+       
+       public void fcpNodeHello(FcpConnection fcpConnection, Map<String, String> nodeProperties);
+       public void fcpCloseConnectionDuplicateName(FcpConnection fcpConnection);
+       
+       
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpMessage.java b/src/net/pterodactylus/util/fcp/FcpMessage.java
new file mode 100644 (file)
index 0000000..6ff4ec6
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * jSite2 - FcpMessage.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import net.pterodactylus.util.io.StreamCopier;
+
+/**
+ * TODO
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpMessage implements Iterable<String> {
+
+       private static final String LINEFEED = "\r\n";
+
+       private final String name;
+       private final Map<String, String> fields = new HashMap<String, String>();
+       private InputStream dataInputStream;
+
+       public FcpMessage(String name) {
+               this(name, null);
+       }
+
+       public FcpMessage(String name, InputStream dataInputStream) {
+               this.name = name;
+               this.dataInputStream = dataInputStream;
+       }
+
+       public String getName() {
+               return name;
+       }
+       
+       public void setField(String field, String value) {
+               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;
+       }
+
+       public void write(OutputStream outputStream) throws IOException {
+               writeLine(outputStream, name);
+               for (Entry<String, String> fieldEntry: fields.entrySet()) {
+                       writeLine(outputStream, fieldEntry.getKey() + "=" + fieldEntry.getValue());
+               }
+               writeLine(outputStream, "EndMessage");
+               outputStream.flush();
+               if (dataInputStream != null) {
+                       StreamCopier.copy(dataInputStream, outputStream);
+               }
+               outputStream.flush();
+       }
+
+       private void writeLine(OutputStream outputStream, String line) throws IOException {
+               outputStream.write((line + LINEFEED).getBytes("UTF-8"));
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpProtocolException.java b/src/net/pterodactylus/util/fcp/FcpProtocolException.java
new file mode 100644 (file)
index 0000000..795798f
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * jSite2 - FcpProtocolException.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;
+
+/**
+ * TODO
+ * 
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpProtocolException extends FcpException {
+
+       private final int code;
+       private final boolean fatal;
+       private final boolean global;
+
+       private String codeDescription;
+       private String extraDescription;
+       private String identifier;
+
+       public FcpProtocolException(int code, boolean fatal, boolean global) {
+               this.code = code;
+               this.fatal = fatal;
+               this.global = global;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the code
+        */
+       int getCode() {
+               return code;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the fatal
+        */
+       boolean isFatal() {
+               return fatal;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the global
+        */
+       boolean isGlobal() {
+               return global;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the codeDescription
+        */
+       String getCodeDescription() {
+               return codeDescription;
+       }
+
+       /**
+        * TODO
+        * 
+        * @param codeDescription
+        *            the codeDescription to set
+        */
+       void setCodeDescription(String codeDescription) {
+               this.codeDescription = codeDescription;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the extraDescription
+        */
+       String getExtraDescription() {
+               return extraDescription;
+       }
+
+       /**
+        * TODO
+        * 
+        * @param extraDescription
+        *            the extraDescription to set
+        */
+       void setExtraDescription(String extraDescription) {
+               this.extraDescription = extraDescription;
+       }
+
+       /**
+        * TODO
+        * 
+        * @return the identifier
+        */
+       String getIdentifier() {
+               return identifier;
+       }
+
+       /**
+        * TODO
+        * 
+        * @param identifier
+        *            the identifier to set
+        */
+       void setIdentifier(String identifier) {
+               this.identifier = identifier;
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/FcpTest.java b/src/net/pterodactylus/util/fcp/FcpTest.java
new file mode 100644 (file)
index 0000000..cedce1d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * jSite2 - FcpTest.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;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+
+/**
+ * TODO
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public class FcpTest extends TestCase {
+
+       private FcpConnection fcpConnection;
+       
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void setUp() throws Exception {
+               fcpConnection = new FcpConnection("wing", "FcpTest");
+               fcpConnection.connect();
+       }
+       
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       protected void tearDown() throws Exception {
+               fcpConnection.disconnect();
+       }
+       
+       public void testFcpConnection() throws FcpException, IOException {
+       }
+       
+       public void testListPeers() throws FcpException, IOException {
+               List<Map<String, String>> peers = fcpConnection.sendListPeers(true, true);
+               for (Map<String, String> peer: peers) {
+                       for (Map.Entry<String, String> entry: peer.entrySet()) {
+                               System.out.println(entry.getKey() + ": " + entry.getValue());
+                       }
+               }
+       }
+       
+       public void testListPeerNotes() throws FcpException, IOException {
+               List<Map<String, String>> peers = fcpConnection.sendListPeerNotes("AY6rGAGjayoyQD2CkvQibf1Ct3mh6iwqyntzNpfRsiM");
+               for (Map<String, String> peer: peers) {
+                       for (Map.Entry<String, String> entry: peer.entrySet()) {
+                               System.out.println(entry.getKey() + ": " + entry.getValue());
+                       }
+               }
+       }
+       
+       public void testGenerateSSK() throws IOException, FcpException {
+               FcpKeyPair keyPair = fcpConnection.generateSSK();
+               System.out.println("PrivateKey: " + keyPair.getPrivateKey());
+               System.out.println("PublicKey: " + keyPair.getPublicKey());
+       }
+       
+}
diff --git a/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java b/src/net/pterodactylus/util/fcp/client/FcpHighLevelClient.java
new file mode 100644 (file)
index 0000000..8fd85b5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 net.pterodactylus.util.fcp.FcpConnection;
+import net.pterodactylus.util.fcp.FcpException;
+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 {
+
+       /** The FCP connection of this client. */
+       private final FcpConnection fcpConnection;
+
+       /**
+        * 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;
+       }
+       
+       public FcpNodeInformation login(String clientName, NodeHelloCallback nodeHelloCallback) throws FcpException, IOException {
+               FcpMessage clientHelloMessage = new FcpMessage("ClientHello");
+               clientHelloMessage.setField("Name", clientName);
+               clientHelloMessage.setField("ExpectedVersion", "2.0");
+               fcpConnection.sendMessage(clientHelloMessage);
+               FcpMessage nodeHelloMessage = fcpConnection.waitForMessage("NodeHello", "CloseConnectionDuplicateClientName");
+               if (nodeHelloMessage.getName().equals("CloseConnectionDuplicateClientName")) {
+                       throw new FcpException("duplicate client name");
+               }
+       }
+
+}
diff --git a/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java b/src/net/pterodactylus/util/fcp/client/FcpNodeInformation.java
new file mode 100644 (file)
index 0000000..5163446
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..6586b03
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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);
+       
+}