diff --git a/src/kademlia/core/Kademlia.java b/src/kademlia/core/Kademlia.java index c85d590..9638aba 100644 --- a/src/kademlia/core/Kademlia.java +++ b/src/kademlia/core/Kademlia.java @@ -25,6 +25,7 @@ import kademlia.operation.Operation; import kademlia.operation.KadRefreshOperation; import kademlia.operation.StoreOperation; import kademlia.routing.RoutingTable; +import kademlia.serializer.JsonDHTSerializer; import kademlia.serializer.JsonRoutingTableSerializer; import kademlia.serializer.JsonSerializer; @@ -42,6 +43,7 @@ import kademlia.serializer.JsonSerializer; * @todo If we're trying to send a message to this node, just cancel the sending process and handle the message right here * @todo Keep this node in it's own routing table - it helps for ContentRefresh operation - easy to check whether this node is one of the k-nodes for a content * @todo Move DHT.getContentStorageFolderName to the Configuration class + * @todo Implement Kademlia.ping() operation. * */ public class Kademlia @@ -152,7 +154,7 @@ public class Kademlia * @section Read the DHT */ din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "dht.kns")); - DHT idht = new JsonSerializer().read(din); + DHT idht = new JsonDHTSerializer().read(din); System.out.println("Finished reading data."); return new Kademlia(ownerId, inode, ikad.getPort(), idht); } @@ -319,7 +321,7 @@ public class Kademlia * @section Save the DHT */ dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "dht.kns")); - new JsonSerializer().write(this.dht, dout); + new JsonDHTSerializer().write(this.dht, dout); System.out.println("FInished saving state"); @@ -381,6 +383,11 @@ public class Kademlia sb.append(this.localNode.getRoutingTable()); sb.append("\n"); + sb.append("\n"); + sb.append("DHT: "); + sb.append(this.dht); + sb.append("\n"); + sb.append("\n\n\n"); return sb.toString(); diff --git a/src/kademlia/dht/DHT.java b/src/kademlia/dht/DHT.java index b5139b7..382ba26 100644 --- a/src/kademlia/dht/DHT.java +++ b/src/kademlia/dht/DHT.java @@ -23,15 +23,27 @@ import kademlia.serializer.JsonSerializer; public class DHT { - private final StorageEntryManager entriesManager; + private transient StorageEntryManager entriesManager; private transient final JsonSerializer contentSerializer; { - entriesManager = new StorageEntryManager(); contentSerializer = new JsonSerializer<>(); } + public DHT() + { + this.initialize(); + } + + /** + * Initialize this DHT to it's default state + */ + public final void initialize() + { + entriesManager = new StorageEntryManager(); + } + /** * Handle storing content locally * @@ -178,4 +190,24 @@ public class DHT { return entriesManager.getAllEntries(); } + + /** + * Used to add a list of storage entries for existing content to the DHT. + * Mainly used when retrieving StorageEntries from a saved state file. + * + * @param ientries The entries to add + */ + public void putStorageEntries(List ientries) + { + for (StorageEntry e : ientries) + { + this.entriesManager.put(e); + } + } + + @Override + public String toString() + { + return this.entriesManager.toString(); + } } diff --git a/src/kademlia/dht/StorageEntry.java b/src/kademlia/dht/StorageEntry.java index b087bc0..d62a430 100644 --- a/src/kademlia/dht/StorageEntry.java +++ b/src/kademlia/dht/StorageEntry.java @@ -70,12 +70,7 @@ public class StorageEntry } /* Check that key matches */ - if ((params.getKey() != null) && (!params.getKey().equals(this.key))) - { - return false; - } - - return true; + return ((params.getKey() != null) && (!params.getKey().equals(this.key))); } @Override @@ -98,4 +93,23 @@ public class StorageEntry hash = 23 * hash + Objects.hashCode(this.type); return hash; } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder("[StorageEntry: "); + + sb.append("{Key: "); + sb.append(this.key); + sb.append("} "); + sb.append("{Owner: "); + sb.append(this.ownerId); + sb.append("} "); + sb.append("{Type: "); + sb.append(this.type); + sb.append("} "); + sb.append("]"); + + return sb.toString(); + } } diff --git a/src/kademlia/dht/StorageEntryManager.java b/src/kademlia/dht/StorageEntryManager.java index 7267539..1b12137 100644 --- a/src/kademlia/dht/StorageEntryManager.java +++ b/src/kademlia/dht/StorageEntryManager.java @@ -16,7 +16,7 @@ import kademlia.node.NodeId; * @author Joshua Kissoon * @since 20140226 */ -public class StorageEntryManager +class StorageEntryManager { private final Map> entries; @@ -34,6 +34,16 @@ public class StorageEntryManager public void put(KadContent content) { StorageEntry entry = new StorageEntry(content); + this.put(entry); + } + + /** + * Add a new entry to our storage + * + * @param entry The StorageEntry to store + */ + public void put(StorageEntry entry) + { if (!this.entries.containsKey(entry.getKey())) { this.entries.put(entry.getKey(), new ArrayList()); @@ -116,4 +126,26 @@ public class StorageEntryManager return entriesRet; } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder("Stored Content: \n"); + for (List es : this.entries.values()) + { + if (entries.size() < 1) + { + continue; + } + + for (StorageEntry e : es) + { + sb.append(e); + sb.append("\n"); + } + } + + sb.append("\n"); + return sb.toString(); + } } diff --git a/src/kademlia/routing/RoutingTable.java b/src/kademlia/routing/RoutingTable.java index 61c9cb3..ab209ff 100644 --- a/src/kademlia/routing/RoutingTable.java +++ b/src/kademlia/routing/RoutingTable.java @@ -30,12 +30,24 @@ public class RoutingTable this.localNode = localNode; /* Initialize all of the buckets to a specific depth */ - this.initializeBuckets(); + this.initialize(); /* @todo Insert the local node */ //this.insert(localNode); } + /** + * Initialize the RoutingTable to it's default state + */ + public final void initialize() + { + this.buckets = new KadBucket[NodeId.ID_LENGTH]; + for (int i = 0; i < NodeId.ID_LENGTH; i++) + { + buckets[i] = new KadBucket(i); + } + } + /** * Adds a new node to the routing table * @@ -180,18 +192,6 @@ public class RoutingTable 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 final String toString() { diff --git a/src/kademlia/serializer/JsonDHTSerializer.java b/src/kademlia/serializer/JsonDHTSerializer.java new file mode 100644 index 0000000..050ed76 --- /dev/null +++ b/src/kademlia/serializer/JsonDHTSerializer.java @@ -0,0 +1,94 @@ +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 java.lang.reflect.Type; +import java.util.List; +import kademlia.dht.DHT; +import kademlia.dht.StorageEntry; + +/** + * A KadSerializer that serializes DHT to JSON format + * The generic serializer is not working for DHT + * + * Why a DHT specific serializer? + * The DHT structure: + * - DHT + * -- StorageEntriesManager + * --- Map> + * ---- NodeId:KeyBytes + * ---- List + * ----- StorageEntry: Key, OwnerId, Type, Hash + * + * The above structure seems to be causing some problem for Gson, especially at the Map part. + * + * Solution + * - Make the StorageEntriesManager transient + * - Simply store all StorageEntry in the serialized object + * - When reloading, re-add all StorageEntry to the DHT + * + * @author Joshua Kissoon + * + * @since 20140310 + */ +public class JsonDHTSerializer implements KadSerializer +{ + + private final Gson gson; + private final Type storageEntriesCollectionType; + + + { + gson = new Gson(); + + storageEntriesCollectionType = new TypeToken>() + { + }.getType(); + } + + @Override + public void write(DHT data, DataOutputStream out) throws IOException + { + try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(out))) + { + writer.beginArray(); + + /* Write the basic DHT */ + gson.toJson(data, DHT.class, writer); + + /* Now Store the Entries */ + gson.toJson(data.getStorageEntries(), this.storageEntriesCollectionType, writer); + + writer.endArray(); + } + + } + + @Override + public DHT read(DataInputStream in) throws IOException, ClassNotFoundException + { + try (DataInputStream din = new DataInputStream(in); + JsonReader reader = new JsonReader(new InputStreamReader(in))) + { + reader.beginArray(); + + /* Read the basic DHT */ + DHT dht = gson.fromJson(reader, DHT.class); + dht.initialize(); + + /* Now get the entries and add them back to the DHT */ + List entries = gson.fromJson(reader, this.storageEntriesCollectionType); + dht.putStorageEntries(entries); + + reader.endArray(); + return dht; + } + } +} diff --git a/src/kademlia/serializer/JsonRoutingTableSerializer.java b/src/kademlia/serializer/JsonRoutingTableSerializer.java index ceafbfd..f0ee30a 100644 --- a/src/kademlia/serializer/JsonRoutingTableSerializer.java +++ b/src/kademlia/serializer/JsonRoutingTableSerializer.java @@ -36,7 +36,7 @@ import kademlia.node.Node; * * @author Joshua Kissoon * - * @since 20140225 + * @since 20140310 */ public class JsonRoutingTableSerializer implements KadSerializer { @@ -85,7 +85,7 @@ public class JsonRoutingTableSerializer implements KadSerializer { }.getType(); List nodes = gson.fromJson(reader, collectionType); - tbl.initializeBuckets(); + tbl.initialize(); for (Node n : nodes) { diff --git a/src/kademlia/tests/DHTContentImpl.java b/src/kademlia/tests/DHTContentImpl.java index d9309a7..6a19c7c 100644 --- a/src/kademlia/tests/DHTContentImpl.java +++ b/src/kademlia/tests/DHTContentImpl.java @@ -73,6 +73,6 @@ public class DHTContentImpl implements KadContent public String toString() { - return "DHTContentImpl[data=" + this.data + "]"; + return "DHTContentImpl[{data=" + this.data + "{ {key:"+this.key + "}]"; } } diff --git a/src/kademlia/tests/SaveStateTest.java b/src/kademlia/tests/SaveStateTest.java index e0ecd68..33d5a3d 100644 --- a/src/kademlia/tests/SaveStateTest.java +++ b/src/kademlia/tests/SaveStateTest.java @@ -12,21 +12,28 @@ import kademlia.node.NodeId; public class SaveStateTest { - public static void main(String[] args) + public SaveStateTest() { try { /* Setting up 2 Kad networks */ - Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567467"), 7574); - Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASERTKJDHGVHERJHGFLK"), 7572); + Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567467"), 7529); + Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASERTKJDHGVHERJHGFLK"), 7532); /* Connecting 2 to 1 */ System.out.println("Connecting Kad 1 and Kad 2"); kad1.connect(kad2.getNode()); - + + synchronized (this) + { + DHTContentImpl c = new DHTContentImpl(kad2.getOwnerId(), "Some Data"); + System.out.println(c); + kad2.put(c); + } + System.out.println(kad1); System.out.println(kad2); - + /* Shutting down kad1 and restarting it */ System.out.println("\n\n\nShutting down Kad instance"); kad1.shutdown(); @@ -40,4 +47,9 @@ public class SaveStateTest e.printStackTrace();; } } + + public static void main(String[] args) + { + new SaveStateTest(); + } }