mirror of
https://github.com/ChronosX88/KademliaDHT.git
synced 2024-11-21 17:52:21 +00:00
Got system state storage and retrieval of state to work!
This commit is contained in:
parent
e1e6e4e40d
commit
e2ca9326c9
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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<>();
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
100
src/kademlia/serializer/JsonRoutingTableSerializer.java
Normal file
100
src/kademlia/serializer/JsonRoutingTableSerializer.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user