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.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Timer; import java.util.Timer;
@ -26,6 +25,7 @@ import kademlia.operation.Operation;
import kademlia.operation.KadRefreshOperation; import kademlia.operation.KadRefreshOperation;
import kademlia.operation.StoreOperation; import kademlia.operation.StoreOperation;
import kademlia.routing.RoutingTable; import kademlia.routing.RoutingTable;
import kademlia.serializer.JsonRoutingTableSerializer;
import kademlia.serializer.JsonSerializer; import kademlia.serializer.JsonSerializer;
/** /**
@ -69,6 +69,7 @@ public class Kademlia
* @param ownerId The Name of this node used for storage * @param ownerId The Name of this node used for storage
* @param localNode The Local Node for this Kad instance * @param localNode The Local Node for this Kad instance
* @param udpPort The UDP port to use for routing messages * @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 * @throws IOException If an error occurred while reading id or local map
* from disk <i>or</i> a network error occurred while * 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 * @return A Kademlia instance loaded from a stored state in a file
* *
* @throws java.io.FileNotFoundException * @throws java.io.FileNotFoundException
* @throws java.lang.ClassNotFoundException
* *
* @todo Boot up this Kademlia instance from a saved file state * @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;
DataInputStream din = new DataInputStream(new FileInputStream(getStateStorageFolderName() + File.separator + ownerId + ".kns"));
/* 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 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 * Here we handle properly shutting down the Kademlia instance
* *
* @throws java.io.FileNotFoundException * @throws java.io.FileNotFoundException
*/ */
public void shutdown() throws FileNotFoundException, IOException, ClassNotFoundException public void shutdown() throws FileNotFoundException, IOException
{ {
/* Shut down the server */ /* Shut down the server */
this.server.shutdown(); this.server.shutdown();
@ -257,11 +282,7 @@ public class Kademlia
{ {
/* Save the system state */ /* Save the system state */
this.saveKadState(); 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 * This will cause a serialization recursion, and in turn a Stack Overflow
*/ */
dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "routingtable.kns")); 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 * @section Save the DHT

View File

@ -24,7 +24,7 @@ public class Node implements Streamable
private InetAddress inetAddress; private InetAddress inetAddress;
private int port; private int port;
private transient final RoutingTable routingTable; private transient RoutingTable routingTable;
{ {
@ -119,6 +119,16 @@ public class Node implements Streamable
return this.routingTable; 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 @Override
public boolean equals(Object o) public boolean equals(Object o)
{ {

View File

@ -17,7 +17,7 @@ import kademlia.message.Streamable;
public class NodeId implements Streamable public class NodeId implements Streamable
{ {
public final static int ID_LENGTH = 160; public final transient static int ID_LENGTH = 160;
private byte[] keyBytes; 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; package kademlia.routing;
import java.util.ArrayList; import java.util.ArrayList;
@ -12,13 +7,18 @@ import java.util.Map;
import kademlia.node.Node; import kademlia.node.Node;
import kademlia.node.NodeId; import kademlia.node.NodeId;
/**
* A bucket in the Kademlia routing table
*
* @author Joshua Kissoon
* @created 20140215
*/
public class KadBucket implements Bucket public class KadBucket implements Bucket
{ {
private final int depth; private final int depth;
private final Map<NodeId, Node> nodes; private final Map<NodeId, Node> nodes;
{ {
nodes = new HashMap<>(); nodes = new HashMap<>();
} }

View File

@ -1,8 +1,3 @@
/**
* @author Joshua Kissoon
* @created 20140215
* @desc Implementation of a Kademlia routing table
*/
package kademlia.routing; package kademlia.routing;
import java.util.ArrayList; import java.util.ArrayList;
@ -10,11 +5,20 @@ import java.util.List;
import kademlia.node.Node; import kademlia.node.Node;
import kademlia.node.NodeId; 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 public class RoutingTable
{ {
private final Node localNode; // The current node private final Node localNode; // The current node
private final KadBucket[] buckets; private transient KadBucket[] buckets;
{ {
@ -26,10 +30,10 @@ public class RoutingTable
this.localNode = localNode; this.localNode = localNode;
/* Initialize all of the buckets to a specific depth */ /* Initialize all of the buckets to a specific depth */
for (int i = 0; i < NodeId.ID_LENGTH; i++) this.initializeBuckets();
{
buckets[i] = new KadBucket(i); /* @todo Insert the local node */
} //this.insert(localNode);
} }
/** /**
@ -37,10 +41,10 @@ public class RoutingTable
* *
* @param n The contact to add * @param n The contact to add
*/ */
public void insert(Node n) public final void insert(Node n)
{ {
/* bucketId is the distance between these nodes */ /* 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); //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 * @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 */ /* Find the first set bit: how far this node is away from the contact node */
int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId()); int bucketId = this.localNode.getNodeId().getDistance(n.getNodeId());
@ -73,7 +77,7 @@ public class RoutingTable
* *
* @return List A List of contacts closest to target * @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); List<Node> closest = new ArrayList<>(num);
@ -146,7 +150,7 @@ public class RoutingTable
/** /**
* @return List A List of all Nodes in this RoutingTable * @return List A List of all Nodes in this RoutingTable
*/ */
public List getAllNodes() public final List getAllNodes()
{ {
List<Node> nodes = new ArrayList<>(); List<Node> nodes = new ArrayList<>();
@ -158,8 +162,38 @@ public class RoutingTable
return nodes; 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 @Override
public String toString() public final String toString()
{ {
StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n"); StringBuilder sb = new StringBuilder("\nPrinting Routing Table Started ***************** \n");
for (KadBucket b : this.buckets) for (KadBucket b : this.buckets)

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); String className = gson.fromJson(reader, String.class);
/* Read and return the Content*/ /* 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"); System.out.println("\n\n\nShutting down Kad instance");
kad1.shutdown(); kad1.shutdown();
//System.out.println("\n\n\nReloading down Kad instance from file"); System.out.println("\n\n\nReloading down Kad instance from file");
//Kademlia.loadFromFile("JoshuaK"); Kademlia kad3 = Kademlia.loadFromFile("JoshuaK");
//System.out.println(kad3); System.out.println(kad3);
} }
catch (Exception e) catch (Exception e)
{ {