diff --git a/src/kademlia/core/Kademlia.java b/src/kademlia/core/Kademlia.java
index 45da8e2..c85d590 100644
--- a/src/kademlia/core/Kademlia.java
+++ b/src/kademlia/core/Kademlia.java
@@ -9,7 +9,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Timer;
@@ -26,6 +25,7 @@ import kademlia.operation.Operation;
import kademlia.operation.KadRefreshOperation;
import kademlia.operation.StoreOperation;
import kademlia.routing.RoutingTable;
+import kademlia.serializer.JsonRoutingTableSerializer;
import kademlia.serializer.JsonSerializer;
/**
@@ -69,6 +69,7 @@ public class Kademlia
* @param ownerId The Name of this node used for storage
* @param localNode The Local Node for this Kad instance
* @param udpPort The UDP port to use for routing messages
+ * @param dht The DHT for this instance
*
* @throws IOException If an error occurred while reading id or local map
* from disk or a network error occurred while
@@ -120,24 +121,40 @@ public class Kademlia
* @return A Kademlia instance loaded from a stored state in a file
*
* @throws java.io.FileNotFoundException
+ * @throws java.lang.ClassNotFoundException
*
* @todo Boot up this Kademlia instance from a saved file state
*/
- public static void loadFromFile(String ownerId) throws FileNotFoundException, IOException, ClassNotFoundException
+ public static Kademlia loadFromFile(String ownerId) throws FileNotFoundException, IOException, ClassNotFoundException
{
- /* Setup the file in which we store the state */
- DataInputStream din = new DataInputStream(new FileInputStream(getStateStorageFolderName() + File.separator + ownerId + ".kns"));
+ DataInputStream din;
- /* Read the UDP Port that this app is running on */
- Integer rPort = new JsonSerializer().read(din);
+ /**
+ * @section Read Basic Kad data
+ */
+ din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "kad.kns"));
+ Kademlia ikad = new JsonSerializer().read(din);
- /* Read the node state */
- // Node rN = new JsonSerializer().read(din);
+ /**
+ * @section Read the routing table
+ */
+ din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "routingtable.kns"));
+ RoutingTable irtbl = new JsonRoutingTableSerializer().read(din);
- /* Read the DHT */
- DHT rDht = new JsonSerializer().read(din);
+ /**
+ * @section Read the node state
+ */
+ din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "node.kns"));
+ Node inode = new JsonSerializer().read(din);
+ inode.setRoutingTable(irtbl);
- //return new Kademlia(ownerId, rN, rPort, rDht);
+ /**
+ * @section Read the DHT
+ */
+ din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "dht.kns"));
+ DHT idht = new JsonSerializer().read(din);
+ System.out.println("Finished reading data.");
+ return new Kademlia(ownerId, inode, ikad.getPort(), idht);
}
/**
@@ -242,12 +259,20 @@ public class Kademlia
return this.ownerId;
}
+ /**
+ * @return Integer The port on which this kad instance is running
+ */
+ public int getPort()
+ {
+ return this.udpPort;
+ }
+
/**
* Here we handle properly shutting down the Kademlia instance
*
* @throws java.io.FileNotFoundException
*/
- public void shutdown() throws FileNotFoundException, IOException, ClassNotFoundException
+ public void shutdown() throws FileNotFoundException, IOException
{
/* Shut down the server */
this.server.shutdown();
@@ -257,11 +282,7 @@ public class Kademlia
{
/* Save the system state */
this.saveKadState();
-
}
-
-
- /* Now we store the content locally in a file */
}
/**
@@ -292,7 +313,7 @@ public class Kademlia
* This will cause a serialization recursion, and in turn a Stack Overflow
*/
dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "routingtable.kns"));
- new JsonSerializer().write(this.localNode.getRoutingTable(), dout);
+ new JsonRoutingTableSerializer().write(this.localNode.getRoutingTable(), dout);
/**
* @section Save the DHT
diff --git a/src/kademlia/node/Node.java b/src/kademlia/node/Node.java
index d68303e..9426559 100644
--- a/src/kademlia/node/Node.java
+++ b/src/kademlia/node/Node.java
@@ -24,7 +24,7 @@ public class Node implements Streamable
private InetAddress inetAddress;
private int port;
- private transient final RoutingTable routingTable;
+ private transient RoutingTable routingTable;
{
@@ -119,6 +119,16 @@ public class Node implements Streamable
return this.routingTable;
}
+ /**
+ * Sets a new routing table to this node, mainly used when we retrieve the node from a saved state
+ *
+ * @param tbl The routing table to use
+ */
+ public void setRoutingTable(RoutingTable tbl)
+ {
+ this.routingTable = tbl;
+ }
+
@Override
public boolean equals(Object o)
{
diff --git a/src/kademlia/node/NodeId.java b/src/kademlia/node/NodeId.java
index e3b7582..dd5691f 100644
--- a/src/kademlia/node/NodeId.java
+++ b/src/kademlia/node/NodeId.java
@@ -17,7 +17,7 @@ import kademlia.message.Streamable;
public class NodeId implements Streamable
{
- public final static int ID_LENGTH = 160;
+ public final transient static int ID_LENGTH = 160;
private byte[] keyBytes;
/**
diff --git a/src/kademlia/routing/KadBucket.java b/src/kademlia/routing/KadBucket.java
index 925668a..733e5dc 100644
--- a/src/kademlia/routing/KadBucket.java
+++ b/src/kademlia/routing/KadBucket.java
@@ -1,8 +1,3 @@
-/**
- * @author Joshua Kissoon
- * @created 20140215
- * @desc A bucket in the Kademlia routing table
- */
package kademlia.routing;
import java.util.ArrayList;
@@ -12,12 +7,17 @@ import java.util.Map;
import kademlia.node.Node;
import kademlia.node.NodeId;
+/**
+ * A bucket in the Kademlia routing table
+ *
+ * @author Joshua Kissoon
+ * @created 20140215
+ */
public class KadBucket implements Bucket
{
private final int depth;
private final Map nodes;
-
{
nodes = new HashMap<>();
diff --git a/src/kademlia/routing/RoutingTable.java b/src/kademlia/routing/RoutingTable.java
index 069f6d1..61c9cb3 100644
--- a/src/kademlia/routing/RoutingTable.java
+++ b/src/kademlia/routing/RoutingTable.java
@@ -1,8 +1,3 @@
-/**
- * @author Joshua Kissoon
- * @created 20140215
- * @desc Implementation of a Kademlia routing table
- */
package kademlia.routing;
import java.util.ArrayList;
@@ -10,26 +5,35 @@ import java.util.List;
import kademlia.node.Node;
import kademlia.node.NodeId;
+/**
+ * Implementation of a Kademlia routing table
+ *
+ * @author Joshua Kissoon
+ * @created 20140215
+ *
+ * @todo Make the KadBucket represent the Bucket interface
+ * @todo Change the code to reflect the bucket interface and not the specific KadBucket implementation
+ */
public class RoutingTable
{
-
+
private final Node localNode; // The current node
- private final KadBucket[] buckets;
-
+ private transient KadBucket[] buckets;
+
{
buckets = new KadBucket[NodeId.ID_LENGTH]; // 160 buckets; 1 for each level in the tree
}
-
+
public RoutingTable(Node localNode)
{
this.localNode = localNode;
/* Initialize all of the buckets to a specific depth */
- for (int i = 0; i < NodeId.ID_LENGTH; i++)
- {
- buckets[i] = new KadBucket(i);
- }
+ this.initializeBuckets();
+
+ /* @todo Insert the local node */
+ //this.insert(localNode);
}
/**
@@ -37,10 +41,10 @@ public class RoutingTable
*
* @param n The contact to add
*/
- public void insert(Node n)
+ public final void insert(Node n)
{
/* bucketId is the distance between these nodes */
- int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId()) - 1;
+ int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
//System.out.println(this.localNode.getNodeId() + " Adding Node " + n.getNodeId() + " to bucket at depth: " + bucketId);
@@ -53,7 +57,7 @@ public class RoutingTable
*
* @param n The node to remove
*/
- public void remove(Node n)
+ public final void remove(Node n)
{
/* Find the first set bit: how far this node is away from the contact node */
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
@@ -73,7 +77,7 @@ public class RoutingTable
*
* @return List A List of contacts closest to target
*/
- public List findClosest(NodeId target, int num)
+ public final List findClosest(NodeId target, int num)
{
List closest = new ArrayList<>(num);
@@ -92,7 +96,7 @@ public class RoutingTable
break;
}
}
-
+
if (closest.size() >= num)
{
return closest;
@@ -139,27 +143,57 @@ public class RoutingTable
break;
}
}
-
+
return closest;
}
/**
* @return List A List of all Nodes in this RoutingTable
*/
- public List getAllNodes()
+ public final List getAllNodes()
{
List nodes = new ArrayList<>();
-
+
for (KadBucket b : this.buckets)
{
nodes.addAll(b.getNodes());
}
-
+
return nodes;
}
+ /**
+ * @return Bucket[] The buckets in this Kad Instance
+ */
+ public final KadBucket[] getBuckets()
+ {
+ return this.buckets;
+ }
+
+ /**
+ * Set the KadBuckets of this routing table, mainly used when retrieving saved state
+ *
+ * @param buckets
+ */
+ public final void setBuckets(KadBucket[] buckets)
+ {
+ this.buckets = buckets;
+ }
+
+ /**
+ * Initialize the kadBuckets to be empty
+ */
+ public final void initializeBuckets()
+ {
+ this.buckets = new KadBucket[NodeId.ID_LENGTH];
+ for (int i = 0; i < NodeId.ID_LENGTH; i++)
+ {
+ buckets[i] = new KadBucket(i);
+ }
+ }
+
@Override
- public String toString()
+ public final String toString()
{
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
for (KadBucket b : this.buckets)
@@ -177,8 +211,8 @@ public class RoutingTable
}
}
sb.append("\nPrinting Routing Table Ended ******************** ");
-
+
return sb.toString();
}
-
+
}
diff --git a/src/kademlia/serializer/JsonRoutingTableSerializer.java b/src/kademlia/serializer/JsonRoutingTableSerializer.java
new file mode 100644
index 0000000..ceafbfd
--- /dev/null
+++ b/src/kademlia/serializer/JsonRoutingTableSerializer.java
@@ -0,0 +1,100 @@
+package kademlia.serializer;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import kademlia.routing.RoutingTable;
+import java.lang.reflect.Type;
+import java.util.List;
+import kademlia.node.Node;
+
+/**
+ * A KadSerializer that serializes routing tables to JSON format
+ * The generic serializer is not working for routing tables
+ *
+ * Why a RoutingTable specific serializer?
+ * The routing table structure:
+ * - RoutingTable
+ * -- Buckets[]
+ * --- Map
+ * ---- NodeId:KeyBytes
+ * ---- Node: NodeId, InetAddress, Port
+ *
+ * The above structure seems to be causing some problem for Gson,
+ * especially at the Map part.
+ *
+ * Solution
+ * - Make the Buckets[] transient
+ * - Simply store all Nodes in the serialized object
+ * - When reloading, re-add all nodes to the RoutingTable
+ *
+ * @author Joshua Kissoon
+ *
+ * @since 20140225
+ */
+public class JsonRoutingTableSerializer implements KadSerializer
+{
+
+ private final Gson gson;
+
+
+ {
+ gson = new Gson();
+ }
+
+ @Override
+ public void write(RoutingTable data, DataOutputStream out) throws IOException
+ {
+ try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(out)))
+ {
+ writer.beginArray();
+
+ /* Write the basic RoutingTable */
+ gson.toJson(data, RoutingTable.class, writer);
+
+ /* Now Store the Nodes */
+ Type collectionType = new TypeToken>()
+ {
+ }.getType();
+ gson.toJson(data.getAllNodes(), collectionType, writer);
+
+ writer.endArray();
+ }
+
+ }
+
+ @Override
+ public RoutingTable read(DataInputStream in) throws IOException, ClassNotFoundException
+ {
+ try (DataInputStream din = new DataInputStream(in);
+ JsonReader reader = new JsonReader(new InputStreamReader(in)))
+ {
+ reader.beginArray();
+
+ /* Read the basic RoutingTable */
+ RoutingTable tbl = gson.fromJson(reader, RoutingTable.class);
+
+ /* Now get the nodes and add them back to the RoutingTable */
+ Type collectionType = new TypeToken>()
+ {
+ }.getType();
+ List nodes = gson.fromJson(reader, collectionType);
+ tbl.initializeBuckets();
+
+ for (Node n : nodes)
+ {
+ tbl.insert(n);
+ }
+
+ reader.endArray();
+ /* Read and return the Content*/
+ return tbl;
+ }
+ }
+}
diff --git a/src/kademlia/serializer/JsonSerializer.java b/src/kademlia/serializer/JsonSerializer.java
index 21addfb..8ad88f9 100644
--- a/src/kademlia/serializer/JsonSerializer.java
+++ b/src/kademlia/serializer/JsonSerializer.java
@@ -58,7 +58,11 @@ public class JsonSerializer implements KadSerializer
String className = gson.fromJson(reader, String.class);
/* Read and return the Content*/
- return gson.fromJson(reader, Class.forName(className));
+ T ret = gson.fromJson(reader, Class.forName(className));
+
+ reader.endArray();
+
+ return ret;
}
}
}
diff --git a/src/kademlia/tests/SaveStateTest.java b/src/kademlia/tests/SaveStateTest.java
index eb16b1a..e0ecd68 100644
--- a/src/kademlia/tests/SaveStateTest.java
+++ b/src/kademlia/tests/SaveStateTest.java
@@ -31,9 +31,9 @@ public class SaveStateTest
System.out.println("\n\n\nShutting down Kad instance");
kad1.shutdown();
- //System.out.println("\n\n\nReloading down Kad instance from file");
- //Kademlia.loadFromFile("JoshuaK");
- //System.out.println(kad3);
+ System.out.println("\n\n\nReloading down Kad instance from file");
+ Kademlia kad3 = Kademlia.loadFromFile("JoshuaK");
+ System.out.println(kad3);
}
catch (Exception e)
{