diff --git a/src/kademlia/core/Configuration.java b/src/kademlia/core/Configuration.java index 6969a0d..a4984a8 100644 --- a/src/kademlia/core/Configuration.java +++ b/src/kademlia/core/Configuration.java @@ -11,7 +11,7 @@ public class Configuration /** * Interval in milliseconds between execution of RestoreOperations. * */ - public static long RESTORE_INTERVAL = 60 * 60 * 1000; + public static long RESTORE_INTERVAL = 60 * 1000; // Default at 1 hour /** * If no reply received from a node in this period (in milliseconds) @@ -37,7 +37,7 @@ public class Configuration /** * Bucket size. * */ - public static int K = 5; + public static int K = 2; /** * Size of replacement cache. diff --git a/src/kademlia/dht/DHT.java b/src/kademlia/dht/DHT.java index 382ba26..7e1efdc 100644 --- a/src/kademlia/dht/DHT.java +++ b/src/kademlia/dht/DHT.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.NoSuchElementException; import kademlia.core.Configuration; import kademlia.core.GetParameter; +import kademlia.exceptions.ContentExistException; import kademlia.node.NodeId; import kademlia.serializer.JsonSerializer; @@ -54,12 +55,19 @@ public class DHT public void store(KadContent content) throws IOException { /* Keep track of this content in the entries manager */ - this.entriesManager.put(content); + try + { + StorageEntry sEntry = this.entriesManager.put(content); - /* Now we store the content locally in a file */ - String contentStorageFolder = this.getContentStorageFolderName(content.getKey()); - DataOutputStream dout = new DataOutputStream(new FileOutputStream(contentStorageFolder + File.separator + content.hashCode() + ".kct")); - contentSerializer.write(content, dout); + /* Now we store the content locally in a file */ + String contentStorageFolder = this.getContentStorageFolderName(content.getKey()); + DataOutputStream dout = new DataOutputStream(new FileOutputStream(contentStorageFolder + File.separator + sEntry.hashCode() + ".kct")); + contentSerializer.write(content, dout); + } + catch (ContentExistException e) + { + /* Content already exist on the DHT, no need to do anything here */ + } } /** @@ -102,7 +110,7 @@ public class DHT { try { - return this.retrieve(entry.getKey(), entry.getContentHash()); + return this.retrieve(entry.getKey(), entry.hashCode()); } catch (FileNotFoundException e) { @@ -133,7 +141,7 @@ public class DHT try { StorageEntry e = this.entriesManager.get(param); - return this.retrieve(e.getKey(), e.getContentHash()); + return this.retrieve(e.getKey(), e.hashCode()); } catch (FileNotFoundException e) { @@ -201,7 +209,14 @@ public class DHT { for (StorageEntry e : ientries) { - this.entriesManager.put(e); + try + { + this.entriesManager.put(e); + } + catch (ContentExistException ex) + { + /* Entry already exist, no need to store it again */ + } } } diff --git a/src/kademlia/dht/KadContent.java b/src/kademlia/dht/KadContent.java index 4ba274b..bb03c3a 100644 --- a/src/kademlia/dht/KadContent.java +++ b/src/kademlia/dht/KadContent.java @@ -29,6 +29,14 @@ public interface KadContent */ public long getCreatedTimestamp(); + /** + * Each content will have an update timestamp + * This allows the DHT to keep only the latest version of a content + * + * @return long The timestamp of when this content was last updated + */ + public long getLastUpdatedTimestamp(); + /** * @return The ID of the owner of this content */ diff --git a/src/kademlia/dht/StorageEntry.java b/src/kademlia/dht/StorageEntry.java index d62a430..5d6b422 100644 --- a/src/kademlia/dht/StorageEntry.java +++ b/src/kademlia/dht/StorageEntry.java @@ -18,6 +18,7 @@ public class StorageEntry private final String ownerId; private final String type; private final int contentHash; + private final long lastUpdated; public StorageEntry(KadContent content) { @@ -25,6 +26,7 @@ public class StorageEntry this.ownerId = content.getOwnerId(); this.type = content.getType(); this.contentHash = content.hashCode(); + this.lastUpdated = content.getLastUpdatedTimestamp(); } public NodeId getKey() @@ -109,7 +111,7 @@ public class StorageEntry 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 1b12137..9e09924 100644 --- a/src/kademlia/dht/StorageEntryManager.java +++ b/src/kademlia/dht/StorageEntryManager.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import kademlia.core.GetParameter; +import kademlia.exceptions.ContentExistException; import kademlia.node.NodeId; /** @@ -31,10 +32,9 @@ class StorageEntryManager * * @param content The content to store a reference to */ - public void put(KadContent content) + public StorageEntry put(KadContent content) throws ContentExistException { - StorageEntry entry = new StorageEntry(content); - this.put(entry); + return this.put(new StorageEntry(content)); } /** @@ -42,13 +42,24 @@ class StorageEntryManager * * @param entry The StorageEntry to store */ - public void put(StorageEntry entry) + public StorageEntry put(StorageEntry entry) throws ContentExistException { if (!this.entries.containsKey(entry.getKey())) { this.entries.put(entry.getKey(), new ArrayList()); } - this.entries.get(entry.getKey()).add(entry); + + /* If this entry doesn't already exist, then we add it */ + if (!this.contains(entry)) + { + this.entries.get(entry.getKey()).add(entry); + + return entry; + } + else + { + throw new ContentExistException("Content already exists on this DHT"); + } } /** @@ -77,6 +88,27 @@ class StorageEntryManager return false; } + /** + * Check if a content exist in the DHT + */ + public boolean contains(KadContent content) + { + return this.contains(new StorageEntry(content)); + } + + /** + * Check if a StorageEntry exist on this DHT + */ + private boolean contains(StorageEntry entry) + { + if (this.entries.containsKey(entry.getKey())) + { + return this.entries.get(entry.getKey()).contains(entry); + } + + return false; + } + /** * Checks if our DHT has a Content for the given criteria * diff --git a/src/kademlia/exceptions/ContentExistException.java b/src/kademlia/exceptions/ContentExistException.java new file mode 100644 index 0000000..96e7c2a --- /dev/null +++ b/src/kademlia/exceptions/ContentExistException.java @@ -0,0 +1,21 @@ +package kademlia.exceptions; + +/** + * An exception used to indicate that a content already exist on the DHT + * + * @author Joshua Kissoon + * @created 20140322 + */ +public class ContentExistException extends Exception +{ + + public ContentExistException() + { + super(); + } + + public ContentExistException(String message) + { + super(message); + } +} diff --git a/src/kademlia/tests/AutoRefreshOperationTest.java b/src/kademlia/tests/AutoRefreshOperationTest.java index 940027f..3281d21 100644 --- a/src/kademlia/tests/AutoRefreshOperationTest.java +++ b/src/kademlia/tests/AutoRefreshOperationTest.java @@ -1,5 +1,9 @@ package kademlia.tests; +import java.io.IOException; +import java.util.Timer; +import java.util.TimerTask; +import kademlia.core.Configuration; import kademlia.core.Kademlia; import kademlia.node.NodeId; @@ -17,31 +21,22 @@ public class AutoRefreshOperationTest try { /* Setting up 2 Kad networks */ - Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567463"), 12049); - Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASF45678947584567464"), 4585); - Kademlia kad3 = new Kademlia("Shameer", new NodeId("ASF45678947584567465"), 8104); - Kademlia kad4 = new Kademlia("Lokesh", new NodeId("ASF45678947584567466"), 8335); - Kademlia kad5 = new Kademlia("Chandu", new NodeId("ASF45678947584567467"), 13345); + final Kademlia kad1 = new Kademlia("JoshuaK", new NodeId("ASF45678947584567463"), 12049); + final Kademlia kad2 = new Kademlia("Crystal", new NodeId("ASF45678947584567464"), 4585); + final Kademlia kad3 = new Kademlia("Shameer", new NodeId("ASF45678947584567465"), 8104); + final Kademlia kad4 = new Kademlia("Lokesh", new NodeId("ASF45678947584567466"), 8335); + final Kademlia kad5 = new Kademlia("Chandu", new NodeId("ASF45678947584567467"), 13345); /* Connecting nodes */ - System.out.println("Connecting Nodes 1 & 2"); + System.out.println("Connecting Nodes"); kad2.bootstrap(kad1.getNode()); kad3.bootstrap(kad2.getNode()); kad4.bootstrap(kad2.getNode()); kad5.bootstrap(kad4.getNode()); - System.out.println(kad1); - System.out.println(kad2); - System.out.println(kad3); - System.out.println(kad4); - System.out.println(kad5); - - DHTContentImpl c = new DHTContentImpl(kad2.getOwnerId(), "Some Data"); - kad2.put(c); - System.out.println("\n\n\n\nSTORING CONTENT 2\n\n\n\n"); - DHTContentImpl c2 = new DHTContentImpl(kad2.getOwnerId(), "Some other Data"); - System.out.println(c2); - kad4.put(c2); + DHTContentImpl c = new DHTContentImpl(kad1.getOwnerId(), "Some Data"); + kad1.putLocally(c); + System.out.println("\nSTORING CONTENT 1 locally on " + kad1.getOwnerId() + "\n\n\n\n"); System.out.println(kad1); System.out.println(kad2); @@ -49,14 +44,24 @@ public class AutoRefreshOperationTest System.out.println(kad4); System.out.println(kad5); - /* Shutting down kad1 and restarting it */ - System.out.println("\n\n\nShutting down Kad instance"); - System.out.println(kad2); - kad1.shutdown(); - - System.out.println("\n\n\nReloading Kad instance from file"); - Kademlia kadR2 = Kademlia.loadFromFile("JoshuaK"); - System.out.println(kadR2); + /* Print the node states every few minutes */ + Timer timer = new Timer(true); + timer.schedule( + new TimerTask() + { + @Override + public void run() + { + System.out.println(kad1); + System.out.println(kad2); + System.out.println(kad3); + System.out.println(kad4); + System.out.println(kad5); + } + }, + // Delay // Interval + Configuration.RESTORE_INTERVAL, Configuration.RESTORE_INTERVAL + ); } catch (Exception e) diff --git a/src/kademlia/tests/DHTContentImpl.java b/src/kademlia/tests/DHTContentImpl.java index 37a5fc8..93b2a5e 100644 --- a/src/kademlia/tests/DHTContentImpl.java +++ b/src/kademlia/tests/DHTContentImpl.java @@ -15,13 +15,13 @@ public class DHTContentImpl implements KadContent private final NodeId key; private String data; private final String ownerId; - private final long createTs; + private final long createTs, updateTs; public static final String TYPE = "DHTContentImpl"; { - this.createTs = System.currentTimeMillis() / 1000L; + this.createTs = this.updateTs = System.currentTimeMillis() / 1000L; } public DHTContentImpl(String ownerId, String data) @@ -71,6 +71,13 @@ public class DHTContentImpl implements KadContent return this.createTs; } + @Override + public long getLastUpdatedTimestamp() + { + return this.updateTs; + } + + @Override public String toString() { return "DHTContentImpl[{data=" + this.data + "{ {key:" + this.key + "}]";