Got system state storage and retrieval of state to work!

This commit is contained in:
Joshua Kissoon 2014-03-10 13:45:13 +05:30
parent e1e6e4e40d
commit e2ca9326c9
8 changed files with 223 additions and 54 deletions

View File

@ -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 <i>or</i> 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<Integer>().read(din);
/**
* @section Read Basic Kad data
*/
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "kad.kns"));
Kademlia ikad = new JsonSerializer<Kademlia>().read(din);
/* Read the node state */
// Node rN = new JsonSerializer<Node>().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<DHT>().read(din);
/**
* @section Read the node state
*/
din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "node.kns"));
Node inode = new JsonSerializer<Node>().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<DHT>().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<RoutingTable>().write(this.localNode.getRoutingTable(), dout);
new JsonRoutingTableSerializer().write(this.localNode.getRoutingTable(), dout);
/**
* @section Save the DHT

View File

@ -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)
{

View File

@ -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;
/**

View File

@ -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<NodeId, Node> nodes;
{
nodes = new HashMap<>();

View File

@ -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<Node> findClosest(NodeId target, int num)
public final List<Node> findClosest(NodeId target, int num)
{
List<Node> 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<Node> 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();
}
}

View File

@ -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, Node>
* ---- 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<RoutingTable>
{
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<List<Node>>()
{
}.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<List<Node>>()
{
}.getType();
List<Node> nodes = gson.fromJson(reader, collectionType);
tbl.initializeBuckets();
for (Node n : nodes)
{
tbl.insert(n);
}
reader.endArray();
/* Read and return the Content*/
return tbl;
}
}
}

View File

@ -58,7 +58,11 @@ public class JsonSerializer<T> implements KadSerializer<T>
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;
}
}
}

View File

@ -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)
{