rework node connections
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 3 May 2008 22:53:44 +0000 (22:53 +0000)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 3 May 2008 22:53:44 +0000 (22:53 +0000)
git-svn-id: http://trooper/svn/projects/jSite/trunk@787 c3eda9e8-030b-0410-8277-bc7414b0a119

src/net/pterodactylus/jsite/core/NodeListener.java [new file with mode: 0644]
src/net/pterodactylus/jsite/core/NodeManager.java

diff --git a/src/net/pterodactylus/jsite/core/NodeListener.java b/src/net/pterodactylus/jsite/core/NodeListener.java
new file mode 100644 (file)
index 0000000..80a01ce
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * jSite2 - NodeListener.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.jsite.core;
+
+import java.util.EventListener;
+
+/**
+ * Interface for listeners that want to be notified about node events.
+ *
+ * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
+ * @version $Id$
+ */
+public interface NodeListener extends EventListener {
+
+       /**
+        * Notifies a listener that a connection to the given node was established.
+        *
+        * @param node
+        *            The node that is now connected
+        */
+       public void nodeConnected(Node node);
+
+       /**
+        * Notifies a listener that a connection to the given node was severed. The
+        * listener is responsible for
+        *
+        * @param node
+        *            The node that is now disconnected
+        * @param throwable
+        *            The exception that caused the disconnect, or <code>null</code>
+        *            if there was no exception
+        */
+       public void nodeDisconnected(Node node, Throwable throwable);
+
+}
index 71bb95d..84867cd 100644 (file)
@@ -25,7 +25,6 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,9 +36,8 @@ import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import net.pterodactylus.fcp.highlevel.ConnectResult;
-import net.pterodactylus.fcp.highlevel.HighLevelCallback;
 import net.pterodactylus.fcp.highlevel.HighLevelClient;
+import net.pterodactylus.fcp.highlevel.HighLevelClientListener;
 import net.pterodactylus.util.io.Closer;
 
 /**
@@ -48,7 +46,7 @@ import net.pterodactylus.util.io.Closer;
  * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
  * @version $Id$
  */
-public class NodeManager {
+public class NodeManager implements HighLevelClientListener {
 
        /** Logger. */
        private static final Logger logger = Logger.getLogger(NodeManager.class.getName());
@@ -62,11 +60,14 @@ public class NodeManager {
        /** Object used for synchronization. */
        private final Object syncObject = new Object();
 
+       /** Node listeners. */
+       private List<NodeListener> nodeListeners = Collections.synchronizedList(new ArrayList<NodeListener>());
+
        /** All nodes. */
        private List<Node> nodes = Collections.synchronizedList(new ArrayList<Node>());
 
        /** All FCP connections. */
-       private Map<Node, HighLevelClient> nodeConnections = Collections.synchronizedMap(new HashMap<Node, HighLevelClient>());
+       private Map<Node, HighLevelClient> nodeClients = Collections.synchronizedMap(new HashMap<Node, HighLevelClient>());
 
        /** Keeps track of which connection is in use right now. */
        private Set<HighLevelClient> usedConnections = Collections.synchronizedSet(new HashSet<HighLevelClient>());
@@ -88,6 +89,57 @@ public class NodeManager {
        }
 
        //
+       // EVENT MANAGEMENT
+       //
+
+       /**
+        * Adds the given listener to the list of listeners.
+        *
+        * @param nodeListener
+        *            The listener to add
+        */
+       public void addNodeListener(NodeListener nodeListener) {
+               nodeListeners.add(nodeListener);
+       }
+
+       /**
+        * Removes the given listener from the list of listeners.
+        *
+        * @param nodeListener
+        *            The listener to remove
+        */
+       public void removeNodeListener(NodeListener nodeListener) {
+               nodeListeners.remove(nodeListener);
+       }
+
+       /**
+        * Notifies all listeners that the given node was connected.
+        *
+        * @param node
+        *            The node that is now connected
+        */
+       private void fireNodeConnected(Node node) {
+               for (NodeListener nodeListener: nodeListeners) {
+                       nodeListener.nodeConnected(node);
+               }
+       }
+
+       /**
+        * Notifies all listeners that the given node was disconnected.
+        *
+        * @param node
+        *            The node that is now disconnected
+        * @param throwable
+        *            The exception that caused the disconnect, or <code>null</code>
+        *            if there was no exception
+        */
+       private void fireNodeDisconnected(Node node, Throwable throwable) {
+               for (NodeListener nodeListener: nodeListeners) {
+                       nodeListener.nodeDisconnected(node, throwable);
+               }
+       }
+
+       //
        // ACCESSORS
        //
 
@@ -207,41 +259,72 @@ public class NodeManager {
        }
 
        /**
-        * Adds a connection to the given node. The connection is made instantly so
-        * this method may block. If the node can not be connected, it will not be
-        * added to the list of nodes.
+        * Adds the given node to this manager.
         *
+        * @see #connect(Node)
         * @param node
         *            The node to connect to
-        * @return <code>true</code> if the connection to the node could be
-        *         established
-        * @throws UnknownHostException
-        *             if the hostname of the node can not be resolved
-        * @throws IOException
-        *             if an I/O error occurs connecting to the node
         */
-       public boolean addNode(Node node) throws UnknownHostException, IOException {
-               if (nodes.contains(node)) {
-                       return true;
+       public void addNode(Node node) {
+               synchronized (syncObject) {
+                       if (!nodes.contains(node)) {
+                               nodes.add(node);
+                       }
                }
-               HighLevelClient highLevelClient = new HighLevelClient(clientName, node.getHostname(), node.getPort());
-               HighLevelCallback<ConnectResult> connectCallback = highLevelClient.connect();
-               ConnectResult connectResult = null;
-               while (connectResult == null) {
-                       try {
-                               connectResult = connectCallback.getResult();
-                       } catch (InterruptedException e) {
-                               /* ignore. */
+       }
+
+       /**
+        * Removes the given node from the node manager, disconnecting it if it is
+        * currently connected.
+        *
+        * @param node
+        *            The node to remove
+        */
+       public void removeNode(Node node) {
+               synchronized (syncObject) {
+                       if (!nodes.contains(node)) {
+                               return;
+                       }
+                       if (nodeClients.containsKey(node)) {
+                               disconnect(node);
                        }
                }
-               if (connectResult.isConnected()) {
+       }
+
+       /**
+        * Tries to establish a connection with the given node.
+        *
+        * @param node
+        *            The node to connect to
+        */
+       public void connect(Node node) {
+               try {
+                       HighLevelClient highLevelClient = new HighLevelClient(clientName, node.getHostname(), node.getPort());
                        synchronized (syncObject) {
-                               nodes.add(node);
-                               nodeConnections.put(node, highLevelClient);
                                clientNodes.put(highLevelClient, node);
+                               nodeClients.put(node, highLevelClient);
                        }
+                       highLevelClient.addHighLevelClientListener(this);
+                       highLevelClient.connect();
+               } catch (IOException ioe1) {
+                       fireNodeDisconnected(node, ioe1);
+               }
+       }
+
+       /**
+        * Disconnects the given node without removing it.
+        *
+        * @param node
+        *            The node to disconnect
+        */
+       public void disconnect(Node node) {
+               synchronized (syncObject) {
+                       if (!nodes.contains(node)) {
+                               return;
+                       }
+                       HighLevelClient highLevelClient = nodeClients.get(node);
+                       highLevelClient.disconnect();
                }
-               return connectResult.isConnected();
        }
 
        /**
@@ -250,7 +333,7 @@ public class NodeManager {
         * @return A list of all nodes
         */
        public List<Node> getNodes() {
-               return new ArrayList<Node>(clientNodes.values());
+               return Collections.unmodifiableList(nodes);
        }
 
        //
@@ -275,7 +358,7 @@ public class NodeManager {
                synchronized (syncObject) {
                        HighLevelClient freeHighLevelClient = null;
                        while (freeHighLevelClient == null) {
-                               for (HighLevelClient highLevelClient: nodeConnections.values()) {
+                               for (HighLevelClient highLevelClient: nodeClients.values()) {
                                        if (!usedConnections.contains(highLevelClient)) {
                                                freeHighLevelClient = highLevelClient;
                                                break;
@@ -296,4 +379,35 @@ public class NodeManager {
                }
        }
 
+       //
+       // INTERFACE HighLevelClientListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void clientConnected(HighLevelClient highLevelClient) {
+               Node node = clientNodes.get(highLevelClient);
+               if (node == null) {
+                       logger.log(Level.WARNING, "got event for unknown client");
+                       return;
+               }
+               fireNodeConnected(node);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       public void clientDisconnected(HighLevelClient highLevelClient, Throwable throwable) {
+               synchronized (syncObject) {
+                       Node node = clientNodes.remove(highLevelClient);
+                       if (node == null) {
+                               logger.log(Level.WARNING, "got event for unknown client");
+                               return;
+                       }
+                       nodeClients.remove(node);
+                       fireNodeDisconnected(node, throwable);
+               }
+       }
+
 }