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
This commit is contained in:
Joshua Kissoon 2014-04-05 21:07:57 +05:30
parent cc1d03ba81
commit 2dde2a75e0
12 changed files with 177 additions and 30 deletions

View File

@ -14,7 +14,7 @@ import java.util.NoSuchElementException;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import kademlia.core.DefaultConfiguration; import kademlia.core.DefaultConfiguration;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.core.KadConfiguration; import kademlia.core.KadConfiguration;
import kademlia.core.KadServer; import kademlia.core.KadServer;
import kademlia.dht.DHT; import kademlia.dht.DHT;

View File

@ -9,7 +9,6 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import kademlia.core.GetParameter;
import kademlia.core.KadConfiguration; import kademlia.core.KadConfiguration;
import kademlia.exceptions.ContentExistException; import kademlia.exceptions.ContentExistException;
import kademlia.exceptions.ContentNotFoundException; import kademlia.exceptions.ContentNotFoundException;
@ -77,13 +76,43 @@ public class DHT
* *
* @param content The DHT content to store * @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 * @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 try
{ {
System.out.println("Adding new content.");
/* Keep track of this content in the entries manager */
StorageEntryMetadata sEntry = this.entriesManager.put(content.getContentMetadata()); StorageEntryMetadata sEntry = this.entriesManager.put(content.getContentMetadata());
/* Now we store the content locally in a file */ /* Now we store the content locally in a file */
@ -94,16 +123,19 @@ public class DHT
{ {
this.getSerializer().write(content, dout); this.getSerializer().write(content, dout);
} }
return true;
} }
catch (ContentExistException e) 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));
} }
/** /**

View File

@ -1,4 +1,4 @@
package kademlia.core; package kademlia.dht;
import kademlia.node.NodeId; import kademlia.node.NodeId;
@ -35,15 +35,55 @@ public class GetParameter
* Construct a GetParameter to search for data by NodeId, owner, type * Construct a GetParameter to search for data by NodeId, owner, type
* *
* @param key * @param key
* @param owner
* @param type * @param type
* @param owner
*/ */
public GetParameter(NodeId key, String owner, String type) public GetParameter(NodeId key, String type, String owner)
{ {
this(key, owner); this(key, owner);
this.type = type; 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() public NodeId getKey()
{ {
return this.key; return this.key;

View File

@ -5,7 +5,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import kademlia.core.GetParameter;
import kademlia.exceptions.ContentExistException; import kademlia.exceptions.ContentExistException;
import kademlia.exceptions.ContentNotFoundException; import kademlia.exceptions.ContentNotFoundException;
import kademlia.node.NodeId; import kademlia.node.NodeId;
@ -76,7 +75,6 @@ class StorageEntryManager
{ {
if (this.entries.containsKey(param.getKey())) 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 */ /* Content with this key exist, check if any match the rest of the search criteria */
for (StorageEntryMetadata e : this.entries.get(param.getKey())) for (StorageEntryMetadata e : this.entries.get(param.getKey()))
{ {
@ -89,7 +87,6 @@ class StorageEntryManager
} }
else else
{ {
System.out.println("Does not contain the key");
System.out.println(this); System.out.println(this);
} }
return false; return false;
@ -100,20 +97,15 @@ class StorageEntryManager
*/ */
public boolean contains(KadContent content) 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 * 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.contains(new GetParameter(entry));
{
return this.entries.get(entry.getKey()).contains(entry);
}
return false;
} }
/** /**
@ -148,6 +140,11 @@ class StorageEntryManager
} }
} }
public StorageEntryMetadata get(StorageEntryMetadata md)
{
return this.get(new GetParameter(md));
}
/** /**
* @return A list of all storage entries * @return A list of all storage entries
*/ */

View File

@ -1,7 +1,6 @@
package kademlia.dht; package kademlia.dht;
import java.util.Objects; import java.util.Objects;
import kademlia.core.GetParameter;
import kademlia.node.NodeId; import kademlia.node.NodeId;
/** /**
@ -18,7 +17,7 @@ public class StorageEntryMetadata
private final String ownerId; private final String ownerId;
private final String type; private final String type;
private final int contentHash; private final int contentHash;
private final long lastUpdated; private final long updatedTs;
public StorageEntryMetadata(KadContent content) public StorageEntryMetadata(KadContent content)
{ {
@ -26,7 +25,7 @@ public class StorageEntryMetadata
this.ownerId = content.getOwnerId(); this.ownerId = content.getOwnerId();
this.type = content.getType(); this.type = content.getType();
this.contentHash = content.hashCode(); this.contentHash = content.hashCode();
this.lastUpdated = content.getLastUpdatedTimestamp(); this.updatedTs = content.getLastUpdatedTimestamp();
} }
public NodeId getKey() public NodeId getKey()
@ -48,6 +47,11 @@ public class StorageEntryMetadata
{ {
return this.contentHash; 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 * When a node is looking for content, he sends the search criteria in a GetParameter object

View File

@ -3,7 +3,7 @@ package kademlia.message;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.node.Node; import kademlia.node.Node;
import kademlia.serializer.JsonSerializer; import kademlia.serializer.JsonSerializer;

View File

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.core.KadConfiguration; import kademlia.core.KadConfiguration;
import kademlia.core.KadServer; import kademlia.core.KadServer;
import kademlia.dht.StorageEntry; import kademlia.dht.StorageEntry;

View File

@ -2,7 +2,7 @@ package kademlia.tests;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.Kademlia; import kademlia.Kademlia;
import kademlia.dht.StorageEntry; import kademlia.dht.StorageEntry;
import kademlia.node.NodeId; import kademlia.node.NodeId;

View File

@ -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<StorageEntry> 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();
}
}
}

View File

@ -18,7 +18,8 @@ public class DHTContentImpl implements KadContent
private NodeId key; private NodeId key;
private String data; private String data;
private String ownerId; 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) public void setData(String newData)
{ {
this.data = newData; this.data = newData;
this.setUpdated();
} }
@Override @Override
@ -66,6 +68,14 @@ public class DHTContentImpl implements KadContent
return this.ownerId; return this.ownerId;
} }
/**
* Set the content as updated
*/
public void setUpdated()
{
this.updateTs = System.currentTimeMillis() / 1000L;
}
@Override @Override
public long getCreatedTimestamp() public long getCreatedTimestamp()
{ {

View File

@ -2,7 +2,7 @@ package kademlia.tests;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.Kademlia; import kademlia.Kademlia;
import kademlia.dht.StorageEntry; import kademlia.dht.StorageEntry;
import kademlia.node.NodeId; import kademlia.node.NodeId;

View File

@ -2,7 +2,7 @@ package kademlia.tests;
import java.util.List; import java.util.List;
import kademlia.Kademlia; import kademlia.Kademlia;
import kademlia.core.GetParameter; import kademlia.dht.GetParameter;
import kademlia.dht.StorageEntry; import kademlia.dht.StorageEntry;
import kademlia.node.NodeId; import kademlia.node.NodeId;