From 2dde2a75e0e627466d5af8196aee1066b0f5c5d5 Mon Sep 17 00:00:00 2001 From: Joshua Kissoon Date: Sat, 5 Apr 2014 21:07:57 +0530 Subject: [PATCH] Setup Content Updating - Didn't do this before, but now it's setup: content will be updated on the DHT if a StoreContentMessage is sent with a newer version of the content GetParameter - Updated the GetParameter so that it can be constructed from a KadContent object or a StorageEntryMetadata object - Move it to the DHT package Others - Added a few methods to StorageEntryMetadata, StorageEntryManager& DHT to simplify conversions between KadContent, GetParameter & StorageEntryMetadata Tests - Written a test to check content updating --- src/kademlia/Kademlia.java | 2 +- src/kademlia/dht/DHT.java | 44 +++++++++++-- src/kademlia/{core => dht}/GetParameter.java | 46 ++++++++++++- src/kademlia/dht/StorageEntryManager.java | 19 +++--- src/kademlia/dht/StorageEntryMetadata.java | 10 ++- .../message/ContentLookupMessage.java | 2 +- .../operation/ContentLookupOperation.java | 2 +- src/kademlia/tests/ContentSendingTest.java | 2 +- src/kademlia/tests/ContentUpdatingTest.java | 64 +++++++++++++++++++ src/kademlia/tests/DHTContentImpl.java | 12 +++- src/kademlia/tests/RefreshOperationTest.java | 2 +- src/kademlia/tests/SaveStateTest2.java | 2 +- 12 files changed, 177 insertions(+), 30 deletions(-) rename src/kademlia/{core => dht}/GetParameter.java (62%) create mode 100644 src/kademlia/tests/ContentUpdatingTest.java diff --git a/src/kademlia/Kademlia.java b/src/kademlia/Kademlia.java index 9ad3c07..76b1bb9 100644 --- a/src/kademlia/Kademlia.java +++ b/src/kademlia/Kademlia.java @@ -14,7 +14,7 @@ import java.util.NoSuchElementException; import java.util.Timer; import java.util.TimerTask; import kademlia.core.DefaultConfiguration; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; diff --git a/src/kademlia/dht/DHT.java b/src/kademlia/dht/DHT.java index 8e2ef51..735c578 100644 --- a/src/kademlia/dht/DHT.java +++ b/src/kademlia/dht/DHT.java @@ -9,7 +9,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; -import kademlia.core.GetParameter; import kademlia.core.KadConfiguration; import kademlia.exceptions.ContentExistException; import kademlia.exceptions.ContentNotFoundException; @@ -77,13 +76,43 @@ public class DHT * * @param content The DHT content to store * + * @return boolean true if we stored the content, false if the content already exists and is up to date + * * @throws java.io.IOException */ - public void store(StorageEntry content) throws IOException + public boolean store(StorageEntry content) throws IOException { - /* Keep track of this content in the entries manager */ + /* Lets check if we have this content and it's the updated version */ + if (this.entriesManager.contains(content.getContentMetadata())) + { + StorageEntryMetadata current = this.entriesManager.get(content.getContentMetadata()); + if (current.getLastUpdatedTimestamp() >= content.getContentMetadata().getLastUpdatedTimestamp()) + { + /* We have the current content, no need to update it! just leave this method now */ + return false; + } + else + { + /* We have this content, but not the latest version, lets delete it so the new version will be added below */ + try + { + this.remove(content.getContentMetadata()); + } + catch (ContentNotFoundException ex) + { + /* @todo Log an error here */ + } + } + } + + /** + * If we got here means we don't have this content, or we need to update the content + * If we need to update the content, the code above would've already deleted it, so we just need to re-add it + */ try { + System.out.println("Adding new content."); + /* Keep track of this content in the entries manager */ StorageEntryMetadata sEntry = this.entriesManager.put(content.getContentMetadata()); /* Now we store the content locally in a file */ @@ -94,16 +123,19 @@ public class DHT { this.getSerializer().write(content, dout); } + return true; } catch (ContentExistException e) { - /* Content already exist on the DHT, no need to do anything here */ + /* @todo Content already exist on the DHT, log an error here */ + return false; } + } - public void store(KadContent content) throws IOException + public boolean store(KadContent content) throws IOException { - this.store(new StorageEntry(content)); + return this.store(new StorageEntry(content)); } /** diff --git a/src/kademlia/core/GetParameter.java b/src/kademlia/dht/GetParameter.java similarity index 62% rename from src/kademlia/core/GetParameter.java rename to src/kademlia/dht/GetParameter.java index 169ed49..ecc996a 100644 --- a/src/kademlia/core/GetParameter.java +++ b/src/kademlia/dht/GetParameter.java @@ -1,4 +1,4 @@ -package kademlia.core; +package kademlia.dht; import kademlia.node.NodeId; @@ -35,15 +35,55 @@ public class GetParameter * Construct a GetParameter to search for data by NodeId, owner, type * * @param key - * @param owner * @param type + * @param owner */ - public GetParameter(NodeId key, String owner, String type) + public GetParameter(NodeId key, String type, String owner) { this(key, owner); this.type = type; } + /** + * Construct our get parameter from a Content + * + * @param c + */ + public GetParameter(KadContent c) + { + this.key = c.getKey(); + + if (c.getType() != null) + { + this.type = c.getType(); + } + + if (c.getOwnerId() != null) + { + this.ownerId = c.getOwnerId(); + } + } + + /** + * Construct our get parameter from a StorageEntryMeta data + * + * @param md + */ + public GetParameter(StorageEntryMetadata md) + { + this.key = md.getKey(); + + if (md.getType() != null) + { + this.type = md.getType(); + } + + if (md.getOwnerId() != null) + { + this.ownerId = md.getOwnerId(); + } + } + public NodeId getKey() { return this.key; diff --git a/src/kademlia/dht/StorageEntryManager.java b/src/kademlia/dht/StorageEntryManager.java index c9b5255..7669b86 100644 --- a/src/kademlia/dht/StorageEntryManager.java +++ b/src/kademlia/dht/StorageEntryManager.java @@ -5,7 +5,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; -import kademlia.core.GetParameter; import kademlia.exceptions.ContentExistException; import kademlia.exceptions.ContentNotFoundException; import kademlia.node.NodeId; @@ -76,7 +75,6 @@ class StorageEntryManager { if (this.entries.containsKey(param.getKey())) { - System.out.println("Does contain the key"); /* Content with this key exist, check if any match the rest of the search criteria */ for (StorageEntryMetadata e : this.entries.get(param.getKey())) { @@ -89,7 +87,6 @@ class StorageEntryManager } else { - System.out.println("Does not contain the key"); System.out.println(this); } return false; @@ -100,20 +97,15 @@ class StorageEntryManager */ public boolean contains(KadContent content) { - return this.contains(new StorageEntryMetadata(content)); + return this.contains(new GetParameter(content)); } /** * Check if a StorageEntry exist on this DHT */ - private boolean contains(StorageEntryMetadata entry) + public boolean contains(StorageEntryMetadata entry) { - if (this.entries.containsKey(entry.getKey())) - { - return this.entries.get(entry.getKey()).contains(entry); - } - - return false; + return this.contains(new GetParameter(entry)); } /** @@ -148,6 +140,11 @@ class StorageEntryManager } } + public StorageEntryMetadata get(StorageEntryMetadata md) + { + return this.get(new GetParameter(md)); + } + /** * @return A list of all storage entries */ diff --git a/src/kademlia/dht/StorageEntryMetadata.java b/src/kademlia/dht/StorageEntryMetadata.java index aa923cd..0fa6fe9 100644 --- a/src/kademlia/dht/StorageEntryMetadata.java +++ b/src/kademlia/dht/StorageEntryMetadata.java @@ -1,7 +1,6 @@ package kademlia.dht; import java.util.Objects; -import kademlia.core.GetParameter; import kademlia.node.NodeId; /** @@ -18,7 +17,7 @@ public class StorageEntryMetadata private final String ownerId; private final String type; private final int contentHash; - private final long lastUpdated; + private final long updatedTs; public StorageEntryMetadata(KadContent content) { @@ -26,7 +25,7 @@ public class StorageEntryMetadata this.ownerId = content.getOwnerId(); this.type = content.getType(); this.contentHash = content.hashCode(); - this.lastUpdated = content.getLastUpdatedTimestamp(); + this.updatedTs = content.getLastUpdatedTimestamp(); } public NodeId getKey() @@ -48,6 +47,11 @@ public class StorageEntryMetadata { return this.contentHash; } + + public long getLastUpdatedTimestamp() + { + return this.updatedTs; + } /** * When a node is looking for content, he sends the search criteria in a GetParameter object diff --git a/src/kademlia/message/ContentLookupMessage.java b/src/kademlia/message/ContentLookupMessage.java index 9f91c79..1b8b9bb 100644 --- a/src/kademlia/message/ContentLookupMessage.java +++ b/src/kademlia/message/ContentLookupMessage.java @@ -3,7 +3,7 @@ package kademlia.message; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.node.Node; import kademlia.serializer.JsonSerializer; diff --git a/src/kademlia/operation/ContentLookupOperation.java b/src/kademlia/operation/ContentLookupOperation.java index 312d360..398fe27 100644 --- a/src/kademlia/operation/ContentLookupOperation.java +++ b/src/kademlia/operation/ContentLookupOperation.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.StorageEntry; diff --git a/src/kademlia/tests/ContentSendingTest.java b/src/kademlia/tests/ContentSendingTest.java index b9f75c8..aa0684e 100644 --- a/src/kademlia/tests/ContentSendingTest.java +++ b/src/kademlia/tests/ContentSendingTest.java @@ -2,7 +2,7 @@ package kademlia.tests; import java.io.IOException; import java.util.List; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.Kademlia; import kademlia.dht.StorageEntry; import kademlia.node.NodeId; diff --git a/src/kademlia/tests/ContentUpdatingTest.java b/src/kademlia/tests/ContentUpdatingTest.java new file mode 100644 index 0000000..580b867 --- /dev/null +++ b/src/kademlia/tests/ContentUpdatingTest.java @@ -0,0 +1,64 @@ +package kademlia.tests; + +import java.io.IOException; +import java.util.List; +import kademlia.dht.GetParameter; +import kademlia.Kademlia; +import kademlia.dht.StorageEntry; +import kademlia.node.NodeId; + +/** + * Testing sending and receiving content between 2 Nodes on a network + * + * @author Joshua Kissoon + * @since 20140224 + */ +public class ContentUpdatingTest +{ + + public static void main(String[] args) + { + try + { + /* Setting up 2 Kad networks */ + Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567467"), 7574); + System.out.println("Created Node Kad 1: " + kad1.getNode().getNodeId()); + Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASERTKJDHGVHERJHGFLK"), 7572); + System.out.println("Created Node Kad 2: " + kad2.getNode().getNodeId()); + kad2.bootstrap(kad1.getNode()); + + /* Lets create the content and share it */ + DHTContentImpl c = new DHTContentImpl(kad2.getOwnerId(), "Some Data"); + kad2.put(c); + + /* Lets retrieve the content */ + System.out.println("Retrieving Content"); + GetParameter gp = new GetParameter(c.getKey(), DHTContentImpl.TYPE, c.getOwnerId()); + System.out.println("Get Parameter: " + gp); + List conte = kad2.get(gp, 4); + for (StorageEntry cc : conte) + { + System.out.println("Content Found: " + new DHTContentImpl().fromBytes(cc.getContent())); + System.out.println("Content Metadata: " + cc.getContentMetadata()); + } + + /* Lets update the content and put it again */ + c.setData("Some New Data"); + kad2.put(c); + + /* Lets retrieve the content */ + System.out.println("Retrieving Content Again"); + conte = kad2.get(gp, 4); + for (StorageEntry cc : conte) + { + System.out.println("Content Found: " + new DHTContentImpl().fromBytes(cc.getContent())); + System.out.println("Content Metadata: " + cc.getContentMetadata()); + } + + } + catch (IOException e) + { + e.printStackTrace(); + } + } +} diff --git a/src/kademlia/tests/DHTContentImpl.java b/src/kademlia/tests/DHTContentImpl.java index 46166e8..dcc5368 100644 --- a/src/kademlia/tests/DHTContentImpl.java +++ b/src/kademlia/tests/DHTContentImpl.java @@ -18,7 +18,8 @@ public class DHTContentImpl implements KadContent private NodeId key; private String data; private String ownerId; - private long createTs, updateTs; + private final long createTs; + private long updateTs; { @@ -46,6 +47,7 @@ public class DHTContentImpl implements KadContent public void setData(String newData) { this.data = newData; + this.setUpdated(); } @Override @@ -66,6 +68,14 @@ public class DHTContentImpl implements KadContent return this.ownerId; } + /** + * Set the content as updated + */ + public void setUpdated() + { + this.updateTs = System.currentTimeMillis() / 1000L; + } + @Override public long getCreatedTimestamp() { diff --git a/src/kademlia/tests/RefreshOperationTest.java b/src/kademlia/tests/RefreshOperationTest.java index 2c04fca..9dd2331 100644 --- a/src/kademlia/tests/RefreshOperationTest.java +++ b/src/kademlia/tests/RefreshOperationTest.java @@ -2,7 +2,7 @@ package kademlia.tests; import java.io.IOException; import java.util.List; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.Kademlia; import kademlia.dht.StorageEntry; import kademlia.node.NodeId; diff --git a/src/kademlia/tests/SaveStateTest2.java b/src/kademlia/tests/SaveStateTest2.java index 20c379b..43baaae 100644 --- a/src/kademlia/tests/SaveStateTest2.java +++ b/src/kademlia/tests/SaveStateTest2.java @@ -2,7 +2,7 @@ package kademlia.tests; import java.util.List; import kademlia.Kademlia; -import kademlia.core.GetParameter; +import kademlia.dht.GetParameter; import kademlia.dht.StorageEntry; import kademlia.node.NodeId;