diff --git a/src/kademlia/core/Kademlia.java b/src/kademlia/Kademlia.java similarity index 82% rename from src/kademlia/core/Kademlia.java rename to src/kademlia/Kademlia.java index 0c539fe..d9b2856 100644 --- a/src/kademlia/core/Kademlia.java +++ b/src/kademlia/Kademlia.java @@ -1,4 +1,4 @@ -package kademlia.core; +package kademlia; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -13,6 +13,10 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Timer; import java.util.TimerTask; +import kademlia.core.DefaultConfiguration; +import kademlia.core.GetParameter; +import kademlia.core.KadConfiguration; +import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.dht.KadContent; import kademlia.exceptions.RoutingException; @@ -57,6 +61,7 @@ public class Kademlia private final transient DHT dht; private final transient Timer timer; private final int udpPort; + private KadConfiguration config; /* Factories */ private final transient MessageFactory messageFactory; @@ -71,19 +76,21 @@ public class Kademlia * @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 + * @param config * * @throws IOException If an error occurred while reading id or local map * from disk or a network error occurred while * attempting to bootstrap to the network * */ - public Kademlia(String ownerId, Node localNode, int udpPort, DHT dht) throws IOException + public Kademlia(String ownerId, Node localNode, int udpPort, DHT dht, KadConfiguration config) throws IOException { this.ownerId = ownerId; this.udpPort = udpPort; this.localNode = localNode; this.dht = dht; - this.messageFactory = new MessageFactory(localNode, this.dht); - this.server = new KadServer(udpPort, this.messageFactory, this.localNode); + this.config = config; + this.messageFactory = new MessageFactory(localNode, this.dht, this.config); + this.server = new KadServer(udpPort, this.messageFactory, this.localNode, this.config); this.timer = new Timer(true); /* Schedule Recurring RestoreOperation */ @@ -105,17 +112,22 @@ public class Kademlia } }, // Delay // Interval - Configuration.RESTORE_INTERVAL, Configuration.RESTORE_INTERVAL + this.config.restoreInterval(), this.config.restoreInterval() ); } + public Kademlia(String ownerId, NodeId defaultId, int udpPort, KadConfiguration config) throws IOException + { + this(ownerId, new Node(defaultId, InetAddress.getLocalHost(), udpPort), udpPort, new DHT(ownerId, config), config); + } + public Kademlia(String ownerId, NodeId defaultId, int udpPort) throws IOException { - this(ownerId, new Node(defaultId, InetAddress.getLocalHost(), udpPort), udpPort, new DHT(ownerId)); + this(ownerId, new Node(defaultId, InetAddress.getLocalHost(), udpPort), udpPort, new DHT(ownerId, new DefaultConfiguration()), new DefaultConfiguration()); } /** - * Load Stored state + * Load Stored state using default configuration * * @param ownerId The ID of the owner for the stored state * @@ -127,35 +139,53 @@ public class Kademlia * @todo Boot up this Kademlia instance from a saved file state */ public static Kademlia loadFromFile(String ownerId) throws FileNotFoundException, IOException, ClassNotFoundException + { + return Kademlia.loadFromFile(ownerId, new DefaultConfiguration()); + } + + /** + * Load Stored state + * + * @param ownerId The ID of the owner for the stored state + * @param iconfig Configuration information to work with + * + * @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 Kademlia loadFromFile(String ownerId, KadConfiguration iconfig) throws FileNotFoundException, IOException, ClassNotFoundException { DataInputStream din; /** * @section Read Basic Kad data */ - din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "kad.kns")); + din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId, iconfig) + File.separator + "kad.kns")); Kademlia ikad = new JsonSerializer().read(din); /** * @section Read the routing table */ - din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "routingtable.kns")); + din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId, iconfig) + File.separator + "routingtable.kns")); RoutingTable irtbl = new JsonRoutingTableSerializer().read(din); /** * @section Read the node state */ - din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "node.kns")); + din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId, iconfig) + File.separator + "node.kns")); Node inode = new JsonSerializer().read(din); inode.setRoutingTable(irtbl); /** * @section Read the DHT */ - din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId) + File.separator + "dht.kns")); + din = new DataInputStream(new FileInputStream(getStateStorageFolderName(ownerId, iconfig) + File.separator + "dht.kns")); DHT idht = new JsonDHTSerializer().read(din); - return new Kademlia(ownerId, inode, ikad.getPort(), idht); + return new Kademlia(ownerId, inode, ikad.getPort(), idht, ikad.getCurrentConfiguration()); } /** @@ -182,6 +212,14 @@ public class Kademlia return this.dht; } + /** + * @return The current KadConfiguration object being used + */ + public KadConfiguration getCurrentConfiguration() + { + return this.config; + } + /** * Connect to an existing peer-to-peer network. * @@ -193,7 +231,7 @@ public class Kademlia * */ public synchronized final void bootstrap(Node n) throws IOException, RoutingException { - Operation op = new ConnectOperation(this.server, this.localNode, n); + Operation op = new ConnectOperation(this.server, this.localNode, n, this.config); op.execute(); } @@ -210,7 +248,7 @@ public class Kademlia */ public synchronized int put(KadContent content) throws IOException { - StoreOperation sop = new StoreOperation(this.server, this.localNode, content, this.dht); + StoreOperation sop = new StoreOperation(this.server, this.localNode, content, this.dht, this.config); sop.execute(); /* Return how many nodes the content was stored on */ @@ -252,7 +290,7 @@ public class Kademlia else { /* Seems like it doesn't exist in our DHT, get it from other Nodes */ - ContentLookupOperation clo = new ContentLookupOperation(server, localNode, param, numResultsReq); + ContentLookupOperation clo = new ContentLookupOperation(server, localNode, param, numResultsReq, this.config); clo.execute(); contentFound = clo.getContentFound(); } @@ -267,7 +305,7 @@ public class Kademlia */ public void refresh() throws IOException { - new KadRefreshOperation(this.server, this.localNode, this.dht).execute(); + new KadRefreshOperation(this.server, this.localNode, this.dht, this.config).execute(); } /** @@ -318,13 +356,13 @@ public class Kademlia /** * @section Store Basic Kad data */ - dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "kad.kns")); + dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId, this.config) + File.separator + "kad.kns")); new JsonSerializer().write(this, dout); /** * @section Save the node state */ - dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "node.kns")); + dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId, this.config) + File.separator + "node.kns")); new JsonSerializer().write(this.localNode, dout); /** @@ -332,13 +370,13 @@ public class Kademlia * We need to save the routing table separate from the node since the routing table will contain the node and the node will contain the routing table * This will cause a serialization recursion, and in turn a Stack Overflow */ - dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "routingtable.kns")); + dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId, this.config) + File.separator + "routingtable.kns")); new JsonRoutingTableSerializer().write(this.localNode.getRoutingTable(), dout); /** * @section Save the DHT */ - dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId) + File.separator + "dht.kns")); + dout = new DataOutputStream(new FileOutputStream(getStateStorageFolderName(this.ownerId, this.config) + File.separator + "dht.kns")); new JsonDHTSerializer().write(this.dht, dout); } @@ -348,10 +386,10 @@ public class Kademlia * * @return String The name of the folder to store node states */ - private static String getStateStorageFolderName(String ownerId) + private static String getStateStorageFolderName(String ownerId, KadConfiguration iconfig) { /* Setup the nodes storage folder if it doesn't exist */ - String path = Configuration.getNodeDataFolder(ownerId) + File.separator + "nodeState"; + String path = iconfig.getNodeDataFolder(ownerId) + File.separator + "nodeState"; File nodeStateFolder = new File(path); if (!nodeStateFolder.isDirectory()) { diff --git a/src/kademlia/KademliaBasic.java b/src/kademlia/KademliaBasic.java deleted file mode 100644 index f039034..0000000 --- a/src/kademlia/KademliaBasic.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package kademlia; - -/** - * - * @author Joshua - */ -public class KademliaBasic -{ - - /** - * @param args the command line arguments - */ - public static void main(String[] args) - { - // TODO code application logic here - } - -} diff --git a/src/kademlia/core/Configuration.java b/src/kademlia/core/Configuration.java deleted file mode 100644 index 4c0a37b..0000000 --- a/src/kademlia/core/Configuration.java +++ /dev/null @@ -1,86 +0,0 @@ -package kademlia.core; - -import java.io.File; - -/** - * A set of Kademlia configuration parameters. Default values are - * supplied and can be changed by the application as necessary. - * - */ -public class Configuration -{ - - /** - * Interval in milliseconds between execution of RestoreOperations. - * */ - public static long RESTORE_INTERVAL = 60 * 1000; // Default at 1 hour - - /** - * If no reply received from a node in this period (in milliseconds) - * consider the node unresponsive. - * */ - public static long RESPONSE_TIMEOUT = 1500; - - /** - * Maximum number of milliseconds for performing an operation. - * */ - public static long OPERATION_TIMEOUT = 3000; - - /** - * Maximum number of concurrent messages in transit. - * */ - public static int CONCURRENCY = 10; - - /** - * Log base exponent. - * */ - public static int B = 2; - - /** - * Bucket size. - * */ - public static int K = 2; - - /** - * Size of replacement cache. - * */ - public static int RCSIZE = 3; - - /** - * Number of times a node can be marked as stale before it is actually removed. - * */ - public static int STALE = 1; - - /** - * Local Storage location - Relative to the user's home folder (Cross-Platform) - */ - public static String LOCAL_FOLDER = "kademlia"; - - /** - * Creates the folder in which this node data is to be stored - * - * @param ownerId - * - * @return The folder path - */ - public static String getNodeDataFolder(String ownerId) - { - /* Setup the main storage folder if it doesn't exist */ - String path = System.getProperty("user.home") + File.separator + Configuration.LOCAL_FOLDER; - File folder = new File(path); - if (!folder.isDirectory()) - { - folder.mkdir(); - } - - /* Setup subfolder for this owner if it doesn't exist */ - File ownerFolder = new File(folder + File.separator + ownerId); - if (!ownerFolder.isDirectory()) - { - ownerFolder.mkdir(); - } - - /* Return the path */ - return ownerFolder.toString(); - } -} diff --git a/src/kademlia/core/DefaultConfiguration.java b/src/kademlia/core/DefaultConfiguration.java new file mode 100644 index 0000000..f244c05 --- /dev/null +++ b/src/kademlia/core/DefaultConfiguration.java @@ -0,0 +1,85 @@ +package kademlia.core; + +import java.io.File; + +/** + * A set of Kademlia configuration parameters. Default values are + * supplied and can be changed by the application as necessary. + * + */ +public class DefaultConfiguration implements KadConfiguration +{ + + private final static long RESTORE_INTERVAL = 60 * 1000; // Default at 1 hour + private final static long RESPONSE_TIMEOUT = 1500; + private final static long OPERATION_TIMEOUT = 3000; + private final static int CONCURRENCY = 10; + private final static int K = 2; + private final static int RCSIZE = 3; + private final static int STALE = 1; + private final static String LOCAL_FOLDER = "kademlia"; + + @Override + public long restoreInterval() + { + return RESTORE_INTERVAL; + } + + @Override + public long responseTimeout() + { + return RESPONSE_TIMEOUT; + } + + @Override + public long operationTimeout() + { + return OPERATION_TIMEOUT; + } + + @Override + public int maxConcurrentMessagesTransiting() + { + return CONCURRENCY; + } + + @Override + public int k() + { + return K; + } + + @Override + public int replacementCacheSize() + { + return RCSIZE; + } + + @Override + public int stale() + { + return STALE; + } + + @Override + public String getNodeDataFolder(String ownerId) + { + /* Setup the main storage folder if it doesn't exist */ + String path = System.getProperty("user.home") + File.separator + DefaultConfiguration.LOCAL_FOLDER; + File folder = new File(path); + if (!folder.isDirectory()) + { + folder.mkdir(); + } + + /* Setup subfolder for this owner if it doesn't exist */ + File ownerFolder = new File(folder + File.separator + ownerId); + if (!ownerFolder.isDirectory()) + { + ownerFolder.mkdir(); + } + + /* Return the path */ + return ownerFolder.toString(); + } +} diff --git a/src/kademlia/core/KadConfiguration.java b/src/kademlia/core/KadConfiguration.java new file mode 100644 index 0000000..d02b63c --- /dev/null +++ b/src/kademlia/core/KadConfiguration.java @@ -0,0 +1,58 @@ +package kademlia.core; + +/** + * Interface that defines a KadConfiguration object + * + * @author Joshua Kissoon + * @since 20140329 + */ +public interface KadConfiguration +{ + + /** + * @return Interval in milliseconds between execution of RestoreOperations. + */ + public long restoreInterval(); + + /** + * If no reply received from a node in this period (in milliseconds) + * consider the node unresponsive. + * + * @return The time it takes to consider a node unresponsive + */ + public long responseTimeout(); + + /** + * @return Maximum number of milliseconds for performing an operation. + */ + public long operationTimeout(); + + /** + * @return Maximum number of concurrent messages in transit. + */ + public int maxConcurrentMessagesTransiting(); + + /** + * @return K-Value used throughout Kademlia + */ + public int k(); + + /** + * @return Size of replacement cache. + */ + public int replacementCacheSize(); + + /** + * @return # of times a node can be marked as stale before it is actually removed. + */ + public int stale(); + + /** + * Creates the folder in which this node data is to be stored + * + * @param ownerId + * + * @return The folder path + */ + public String getNodeDataFolder(String ownerId); +} diff --git a/src/kademlia/core/KadServer.java b/src/kademlia/core/KadServer.java index 288eb59..0935ad9 100644 --- a/src/kademlia/core/KadServer.java +++ b/src/kademlia/core/KadServer.java @@ -30,6 +30,9 @@ public class KadServer /* Maximum size of a Datagram Packet */ private static final int DATAGRAM_BUFFER_SIZE = 64 * 1024; // 64KB + /* Basic Kad Objects */ + private final KadConfiguration config; + /* Server Objects */ private final int udpPort; private final DatagramSocket socket; @@ -57,12 +60,15 @@ public class KadServer * @param udpPort The port to listen on * @param mFactory Factory used to create messages * @param localNode Local node on which this server runs on + * @param config * * @throws java.net.SocketException */ - public KadServer(int udpPort, MessageFactory mFactory, Node localNode) throws SocketException + public KadServer(int udpPort, MessageFactory mFactory, Node localNode, KadConfiguration config) throws SocketException { this.udpPort = udpPort; + this.config = config; + this.socket = new DatagramSocket(udpPort); this.localNode = localNode; @@ -116,7 +122,7 @@ public class KadServer //System.out.println(this.localNode + " Putting Receiver for comm: " + comm + " Receiver: " + recv); receivers.put(comm, recv); TimerTask task = new TimeoutTask(comm, recv); - timer.schedule(task, Configuration.RESPONSE_TIMEOUT); + timer.schedule(task, this.config.responseTimeout()); tasks.put(comm, task); } diff --git a/src/kademlia/dht/DHT.java b/src/kademlia/dht/DHT.java index df5354a..f30780c 100644 --- a/src/kademlia/dht/DHT.java +++ b/src/kademlia/dht/DHT.java @@ -9,8 +9,9 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.NoSuchElementException; -import kademlia.core.Configuration; +import kademlia.core.DefaultConfiguration; import kademlia.core.GetParameter; +import kademlia.core.KadConfiguration; import kademlia.exceptions.ContentExistException; import kademlia.exceptions.ContentNotFoundException; import kademlia.node.NodeId; @@ -27,6 +28,7 @@ public class DHT private transient StorageEntryManager entriesManager; private transient final JsonSerializer contentSerializer; + private final KadConfiguration config; private final String ownerId; @@ -35,9 +37,10 @@ public class DHT contentSerializer = new JsonSerializer<>(); } - public DHT(String ownerId) + public DHT(String ownerId, KadConfiguration config) { this.ownerId = ownerId; + this.config = config; this.initialize(); } @@ -205,7 +208,7 @@ public class DHT * The name of the file containing the content is the hash of this content */ String folderName = key.hexRepresentation().substring(0, 10); - File contentStorageFolder = new File(Configuration.getNodeDataFolder(ownerId) + File.separator + folderName); + File contentStorageFolder = new File(this.config.getNodeDataFolder(ownerId) + File.separator + folderName); /* Create the content folder if it doesn't exist */ if (!contentStorageFolder.isDirectory()) diff --git a/src/kademlia/message/ContentLookupReceiver.java b/src/kademlia/message/ContentLookupReceiver.java index d2493ae..baa6cee 100644 --- a/src/kademlia/message/ContentLookupReceiver.java +++ b/src/kademlia/message/ContentLookupReceiver.java @@ -1,6 +1,7 @@ package kademlia.message; import java.io.IOException; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.node.Node; @@ -19,12 +20,14 @@ public class ContentLookupReceiver implements Receiver private final KadServer server; private final Node localNode; private final DHT dht; + private final KadConfiguration config; - public ContentLookupReceiver(KadServer server, Node localNode, DHT dht) + public ContentLookupReceiver(KadServer server, Node localNode, DHT dht, KadConfiguration config) { this.server = server; this.localNode = localNode; this.dht = dht; + this.config = config; } @Override @@ -47,7 +50,7 @@ public class ContentLookupReceiver implements Receiver * We create a NodeLookupReceiver and let this receiver handle this operation */ NodeLookupMessage lkpMsg = new NodeLookupMessage(msg.getOrigin(), msg.getParameters().getKey()); - new NodeLookupReceiver(server, localNode).receive(lkpMsg, comm); + new NodeLookupReceiver(server, localNode, this.config).receive(lkpMsg, comm); } } diff --git a/src/kademlia/message/MessageFactory.java b/src/kademlia/message/MessageFactory.java index 3f76b2a..91c1957 100644 --- a/src/kademlia/message/MessageFactory.java +++ b/src/kademlia/message/MessageFactory.java @@ -2,6 +2,7 @@ package kademlia.message; import java.io.DataInputStream; import java.io.IOException; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.node.Node; @@ -18,11 +19,13 @@ public class MessageFactory private final Node localNode; private final DHT dht; + private final KadConfiguration config; - public MessageFactory(Node local, DHT dht) + public MessageFactory(Node local, DHT dht, KadConfiguration config) { this.localNode = local; this.dht = dht; + this.config = config; } public Message createMessage(byte code, DataInputStream in) throws IOException @@ -59,9 +62,9 @@ public class MessageFactory case ConnectMessage.CODE: return new ConnectReceiver(server, this.localNode); case ContentLookupMessage.CODE: - return new ContentLookupReceiver(server, localNode, dht); + return new ContentLookupReceiver(server, this.localNode, this.dht, this.config); case NodeLookupMessage.CODE: - return new NodeLookupReceiver(server, this.localNode); + return new NodeLookupReceiver(server, this.localNode, this.config); case SimpleMessage.CODE: return new SimpleReceiver(); case StoreContentMessage.CODE: diff --git a/src/kademlia/message/NodeLookupReceiver.java b/src/kademlia/message/NodeLookupReceiver.java index 6930364..874c315 100644 --- a/src/kademlia/message/NodeLookupReceiver.java +++ b/src/kademlia/message/NodeLookupReceiver.java @@ -2,7 +2,7 @@ package kademlia.message; import java.io.IOException; import java.util.List; -import kademlia.core.Configuration; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.node.Node; import kademlia.operation.Receiver; @@ -18,11 +18,13 @@ public class NodeLookupReceiver implements Receiver private final KadServer server; private final Node localNode; + private final KadConfiguration config; - public NodeLookupReceiver(KadServer server, Node local) + public NodeLookupReceiver(KadServer server, Node local, KadConfiguration config) { this.server = server; this.localNode = local; + this.config = config; } /** @@ -44,7 +46,7 @@ public class NodeLookupReceiver implements Receiver this.localNode.getRoutingTable().insert(origin); /* Find nodes closest to the LookupId */ - List nodes = this.localNode.getRoutingTable().findClosest(msg.getLookupId(), Configuration.K); + List nodes = this.localNode.getRoutingTable().findClosest(msg.getLookupId(), this.config.k()); /* Respond to the NodeLookupMessage */ Message reply = new NodeReplyMessage(this.localNode, nodes); diff --git a/src/kademlia/operation/BucketRefreshOperation.java b/src/kademlia/operation/BucketRefreshOperation.java index 7f98ec7..60c6d7f 100644 --- a/src/kademlia/operation/BucketRefreshOperation.java +++ b/src/kademlia/operation/BucketRefreshOperation.java @@ -1,6 +1,7 @@ package kademlia.operation; import java.io.IOException; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.node.Node; import kademlia.node.NodeId; @@ -17,11 +18,13 @@ public class BucketRefreshOperation implements Operation private final KadServer server; private final Node localNode; + private final KadConfiguration config; - public BucketRefreshOperation(KadServer server, Node localNode) + public BucketRefreshOperation(KadServer server, Node localNode, KadConfiguration config) { this.server = server; this.localNode = localNode; + this.config = config; } /** @@ -50,7 +53,7 @@ public class BucketRefreshOperation implements Operation { try { - new NodeLookupOperation(server, localNode, localNode.getNodeId()).execute(); + new NodeLookupOperation(server, localNode, localNode.getNodeId(), BucketRefreshOperation.this.config).execute(); } catch (IOException e) { diff --git a/src/kademlia/operation/ConnectOperation.java b/src/kademlia/operation/ConnectOperation.java index e52a47a..b41e25b 100644 --- a/src/kademlia/operation/ConnectOperation.java +++ b/src/kademlia/operation/ConnectOperation.java @@ -6,7 +6,8 @@ package kademlia.operation; import java.io.IOException; -import kademlia.core.Configuration; +import kademlia.core.DefaultConfiguration; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.exceptions.RoutingException; import kademlia.message.AcknowledgeMessage; @@ -22,6 +23,7 @@ public class ConnectOperation implements Operation, Receiver private final KadServer server; private final Node localNode; private final Node bootstrapNode; + private final KadConfiguration config; private boolean error; private int attempts; @@ -30,12 +32,14 @@ public class ConnectOperation implements Operation, Receiver * @param server The message server used to send/receive messages * @param local The local node * @param bootstrap Node to use to bootstrap the local node onto the network + * @param config */ - public ConnectOperation(KadServer server, Node local, Node bootstrap) + public ConnectOperation(KadServer server, Node local, Node bootstrap, KadConfiguration config) { this.server = server; this.localNode = local; this.bootstrapNode = bootstrap; + this.config = config; } @Override @@ -52,7 +56,7 @@ public class ConnectOperation implements Operation, Receiver server.sendMessage(this.bootstrapNode, m, this); /* Wait for a while */ - wait(Configuration.OPERATION_TIMEOUT); + wait(config.operationTimeout()); if (error) { @@ -61,7 +65,7 @@ public class ConnectOperation implements Operation, Receiver } /* Perform lookup for our own ID to get nodes close to us */ - Operation lookup = new NodeLookupOperation(this.server, this.localNode, this.localNode.getNodeId()); + Operation lookup = new NodeLookupOperation(this.server, this.localNode, this.localNode.getNodeId(), this.config); lookup.execute(); /** diff --git a/src/kademlia/operation/ContentLookupOperation.java b/src/kademlia/operation/ContentLookupOperation.java index dcb8619..78fad0b 100644 --- a/src/kademlia/operation/ContentLookupOperation.java +++ b/src/kademlia/operation/ContentLookupOperation.java @@ -9,8 +9,9 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; -import kademlia.core.Configuration; +import kademlia.core.DefaultConfiguration; import kademlia.core.GetParameter; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.KadContent; import kademlia.exceptions.RoutingException; @@ -44,6 +45,7 @@ public class ContentLookupOperation implements Operation, Receiver private final GetParameter params; private final List contentFound; private final int numResultsReq; + private final KadConfiguration config; private final ContentLookupMessage lookupMessage; @@ -68,8 +70,9 @@ public class ContentLookupOperation implements Operation, Receiver * @param localNode * @param params The parameters to search for the content which we need to find * @param numResultsReq The number of results for this content from different nodes required + * @param config */ - public ContentLookupOperation(KadServer server, Node localNode, GetParameter params, int numResultsReq) + public ContentLookupOperation(KadServer server, Node localNode, GetParameter params, int numResultsReq, KadConfiguration config) { /* Construct our lookup message */ this.lookupMessage = new ContentLookupMessage(localNode, params); @@ -78,6 +81,7 @@ public class ContentLookupOperation implements Operation, Receiver this.localNode = localNode; this.params = params; this.numResultsReq = numResultsReq; + this.config = config; /** * We initialize a TreeMap to store nodes. @@ -107,19 +111,19 @@ public class ContentLookupOperation implements Operation, Receiver { /* If we haven't finished as yet, wait a while */ /** - * @todo Get rid of this wait here! + * @todo Get rid of this wait here! * We should run this until there are no nodes left to ask from the K closest nodes * and only pause for short intervals in between - * + * * @todo Do the same for the NodeLookupOperation */ - wait(Configuration.OPERATION_TIMEOUT); + wait(this.config.operationTimeout()); /* If we still haven't received any responses by then, do a routing timeout */ if (error) { /* Lets not throw any exception */ - + //throw new RoutingException("Content Lookup Operation Timeout."); } } @@ -149,7 +153,7 @@ public class ContentLookupOperation implements Operation, Receiver /** * Asks some of the K closest nodes seen but not yet queried. - * Assures that no more than Configuration.CONCURRENCY messages are in transit at a time + * Assures that no more than DefaultConfiguration.CONCURRENCY messages are in transit at a time * * This method should be called every time a reply is received or a timeout occurs. * @@ -161,7 +165,7 @@ public class ContentLookupOperation implements Operation, Receiver private boolean askNodesorFinish() throws IOException { /* If >= CONCURRENCY nodes are in transit, don't do anything */ - if (Configuration.CONCURRENCY <= this.messagesTransiting.size()) + if (this.config.maxConcurrentMessagesTransiting() <= this.messagesTransiting.size()) { return false; } @@ -183,7 +187,7 @@ public class ContentLookupOperation implements Operation, Receiver * Send messages to nodes in the list; * making sure than no more than CONCURRENCY messsages are in transit */ - for (int i = 0; (this.messagesTransiting.size() < Configuration.CONCURRENCY) && (i < unasked.size()); i++) + for (int i = 0; (this.messagesTransiting.size() < this.config.maxConcurrentMessagesTransiting()) && (i < unasked.size()); i++) { Node n = (Node) unasked.get(i); @@ -207,8 +211,8 @@ public class ContentLookupOperation implements Operation, Receiver */ private List closestNodesNotFailed(Byte status) { - List closestNodes = new ArrayList<>(Configuration.K); - int remainingSpaces = Configuration.K; + List closestNodes = new ArrayList<>(this.config.k()); + int remainingSpaces = this.config.k(); for (Map.Entry e : this.nodes.entrySet()) { @@ -237,7 +241,7 @@ public class ContentLookupOperation implements Operation, Receiver { return; } - + if (incoming instanceof ContentMessage) { /* The reply received is a content message with the required content, take it in */ diff --git a/src/kademlia/operation/ContentRefreshOperation.java b/src/kademlia/operation/ContentRefreshOperation.java index 2302e58..c42ed84 100644 --- a/src/kademlia/operation/ContentRefreshOperation.java +++ b/src/kademlia/operation/ContentRefreshOperation.java @@ -2,7 +2,8 @@ package kademlia.operation; import java.io.IOException; import java.util.List; -import kademlia.core.Configuration; +import kademlia.core.DefaultConfiguration; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.dht.StorageEntry; @@ -23,12 +24,14 @@ public class ContentRefreshOperation implements Operation private final KadServer server; private final Node localNode; private final DHT dht; + private final KadConfiguration config; - public ContentRefreshOperation(KadServer server, Node localNode, DHT dht) + public ContentRefreshOperation(KadServer server, Node localNode, DHT dht, KadConfiguration config) { this.server = server; this.localNode = localNode; this.dht = dht; + this.config = config; } /** @@ -54,7 +57,7 @@ public class ContentRefreshOperation implements Operation * only distribute it if it has been last updated > 1 hour ago */ /* Get the K closest nodes to this entries */ - List closestNodes = this.localNode.getRoutingTable().findClosest(e.getKey(), Configuration.K); + List closestNodes = this.localNode.getRoutingTable().findClosest(e.getKey(), this.config.k()); /* Create the message */ Message msg = new StoreContentMessage(this.localNode, dht.get(e)); diff --git a/src/kademlia/operation/KadRefreshOperation.java b/src/kademlia/operation/KadRefreshOperation.java index 16899cf..d928543 100644 --- a/src/kademlia/operation/KadRefreshOperation.java +++ b/src/kademlia/operation/KadRefreshOperation.java @@ -1,6 +1,7 @@ package kademlia.operation; import java.io.IOException; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.node.Node; @@ -17,21 +18,23 @@ public class KadRefreshOperation implements Operation private final KadServer server; private final Node localNode; private final DHT dht; + private final KadConfiguration config; - public KadRefreshOperation(KadServer server, Node localNode, DHT dht) + public KadRefreshOperation(KadServer server, Node localNode, DHT dht, KadConfiguration config) { this.server = server; this.localNode = localNode; this.dht = dht; + this.config = config; } @Override public void execute() throws IOException { /* Run our BucketRefreshOperation to refresh buckets */ - new BucketRefreshOperation(server, localNode).execute(); + new BucketRefreshOperation(this.server, this.localNode, this.config).execute(); /* After buckets have been refreshed, we refresh content */ - new ContentRefreshOperation(server, localNode, dht).execute(); + new ContentRefreshOperation(this.server, this.localNode, this.dht, this.config).execute(); } } diff --git a/src/kademlia/operation/NodeLookupOperation.java b/src/kademlia/operation/NodeLookupOperation.java index 5dbaab1..06679ac 100644 --- a/src/kademlia/operation/NodeLookupOperation.java +++ b/src/kademlia/operation/NodeLookupOperation.java @@ -7,7 +7,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; -import kademlia.core.Configuration; +import kademlia.core.DefaultConfiguration; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.exceptions.RoutingException; import kademlia.exceptions.UnknownMessageException; @@ -38,6 +39,7 @@ public class NodeLookupOperation implements Operation, Receiver private final KadServer server; private final Node localNode; private final NodeId lookupId; + private final KadConfiguration config; private boolean error; @@ -59,12 +61,14 @@ public class NodeLookupOperation implements Operation, Receiver * @param server KadServer used for communication * @param localNode The local node making the communication * @param lookupId The ID for which to find nodes close to + * @param config */ - public NodeLookupOperation(KadServer server, Node localNode, NodeId lookupId) + public NodeLookupOperation(KadServer server, Node localNode, NodeId lookupId, KadConfiguration config) { this.server = server; this.localNode = localNode; this.lookupId = lookupId; + this.config = config; this.lookupMessage = new NodeLookupMessage(localNode, lookupId); @@ -96,7 +100,7 @@ public class NodeLookupOperation implements Operation, Receiver if (!this.askNodesorFinish()) { /* If we haven't finished as yet, wait a while */ - wait(Configuration.OPERATION_TIMEOUT); + wait(this.config.operationTimeout()); /* If we still haven't received any responses by then, do a routing timeout */ if (error) @@ -135,7 +139,7 @@ public class NodeLookupOperation implements Operation, Receiver /** * Asks some of the K closest nodes seen but not yet queried. - * Assures that no more than Configuration.CONCURRENCY messages are in transit at a time + * Assures that no more than DefaultConfiguration.CONCURRENCY messages are in transit at a time * * This method should be called every time a reply is received or a timeout occurs. * @@ -147,7 +151,7 @@ public class NodeLookupOperation implements Operation, Receiver private boolean askNodesorFinish() throws IOException { /* If >= CONCURRENCY nodes are in transit, don't do anything */ - if (Configuration.CONCURRENCY <= this.messagesTransiting.size()) + if (this.config.maxConcurrentMessagesTransiting() <= this.messagesTransiting.size()) { return false; } @@ -166,7 +170,7 @@ public class NodeLookupOperation implements Operation, Receiver * Send messages to nodes in the list; * making sure than no more than CONCURRENCY messsages are in transit */ - for (int i = 0; (this.messagesTransiting.size() < Configuration.CONCURRENCY) && (i < unasked.size()); i++) + for (int i = 0; (this.messagesTransiting.size() < this.config.maxConcurrentMessagesTransiting()) && (i < unasked.size()); i++) { Node n = (Node) unasked.get(i); @@ -187,8 +191,8 @@ public class NodeLookupOperation implements Operation, Receiver */ private List closestNodes(String status) { - List closestNodes = new ArrayList<>(Configuration.K); - int remainingSpaces = Configuration.K; + List closestNodes = new ArrayList<>(this.config.k()); + int remainingSpaces = this.config.k(); for (Map.Entry e : this.nodes.entrySet()) { @@ -216,8 +220,8 @@ public class NodeLookupOperation implements Operation, Receiver */ private List closestNodesNotFailed(String status) { - List closestNodes = new ArrayList<>(Configuration.K); - int remainingSpaces = Configuration.K; + List closestNodes = new ArrayList<>(this.config.k()); + int remainingSpaces = this.config.k(); for (Map.Entry e : this.nodes.entrySet()) { diff --git a/src/kademlia/operation/StoreOperation.java b/src/kademlia/operation/StoreOperation.java index 39736d6..f914c2f 100644 --- a/src/kademlia/operation/StoreOperation.java +++ b/src/kademlia/operation/StoreOperation.java @@ -2,6 +2,7 @@ package kademlia.operation; import java.io.IOException; import java.util.List; +import kademlia.core.KadConfiguration; import kademlia.core.KadServer; import kademlia.dht.DHT; import kademlia.dht.KadContent; @@ -22,26 +23,29 @@ public class StoreOperation implements Operation private final Node localNode; private final KadContent content; private final DHT localDht; + private final KadConfiguration config; /** * @param server * @param localNode * @param content The content to be stored on the DHT * @param localDht The local DHT + * @param config */ - public StoreOperation(KadServer server, Node localNode, KadContent content, DHT localDht) + public StoreOperation(KadServer server, Node localNode, KadContent content, DHT localDht, KadConfiguration config) { this.server = server; this.localNode = localNode; this.content = content; this.localDht = localDht; + this.config = config; } @Override public synchronized void execute() throws IOException { /* Get the nodes on which we need to store the content */ - NodeLookupOperation ndlo = new NodeLookupOperation(this.server, this.localNode, this.content.getKey()); + NodeLookupOperation ndlo = new NodeLookupOperation(this.server, this.localNode, this.content.getKey(), this.config); ndlo.execute(); List nodes = ndlo.getClosestNodes(); diff --git a/src/kademlia/routing/GetParameter.java b/src/kademlia/routing/GetParameter.java new file mode 100644 index 0000000..90ed9e9 --- /dev/null +++ b/src/kademlia/routing/GetParameter.java @@ -0,0 +1,87 @@ +package kademlia.routing; + +import kademlia.node.NodeId; + +/** + * A GET request can get content based on Key, Owner, Type, etc + * + * This is a class containing the parameters to be passed in a GET request + * + * We use a class since the number of filtering parameters can change later + * + * @author Joshua Kissoon + * @since 20140224 + */ +public class GetParameter +{ + + private NodeId key; + private String ownerId = null; + private String type = null; + + /** + * Construct a GetParameter to search for data by NodeId + * + * @param key + */ + public GetParameter(NodeId key) + { + this.key = key; + } + + /** + * Construct a GetParameter to search for data by NodeId and owner + * + * @param key + * @param owner + */ + public GetParameter(NodeId key, String owner) + { + this(key); + this.ownerId = owner; + } + + /** + * Construct a GetParameter to search for data by NodeId, owner, type + * + * @param key + * @param owner + * @param type + */ + public GetParameter(NodeId key, String owner, String type) + { + this(key, owner); + this.type = type; + } + + public NodeId getKey() + { + return this.key; + } + + public void setOwnerId(String ownerId) + { + this.ownerId = ownerId; + } + + public String getOwnerId() + { + return this.ownerId; + } + + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return this.type; + } + + @Override + public String toString() + { + return "GetParameter - [Key: " + key + "][Owner: " + this.ownerId + "][Type: " + this.type + "]"; + } +} diff --git a/src/kademlia/tests/AutoRefreshOperationTest.java b/src/kademlia/tests/AutoRefreshOperationTest.java index 9d50052..c209f77 100644 --- a/src/kademlia/tests/AutoRefreshOperationTest.java +++ b/src/kademlia/tests/AutoRefreshOperationTest.java @@ -2,8 +2,9 @@ package kademlia.tests; import java.util.Timer; import java.util.TimerTask; -import kademlia.core.Configuration; -import kademlia.core.Kademlia; +import kademlia.core.DefaultConfiguration; +import kademlia.Kademlia; +import kademlia.core.KadConfiguration; import kademlia.node.NodeId; /** @@ -53,6 +54,7 @@ public class AutoRefreshOperationTest System.out.println(kad5); /* Print the node states every few minutes */ + KadConfiguration config = new DefaultConfiguration(); Timer timer = new Timer(true); timer.schedule( new TimerTask() @@ -68,7 +70,7 @@ public class AutoRefreshOperationTest } }, // Delay // Interval - Configuration.RESTORE_INTERVAL, Configuration.RESTORE_INTERVAL + config.restoreInterval(), config.restoreInterval() ); } diff --git a/src/kademlia/tests/AutoRefreshOperationTest2.java b/src/kademlia/tests/AutoRefreshOperationTest2.java index 7d4f7a3..4eedbae 100644 --- a/src/kademlia/tests/AutoRefreshOperationTest2.java +++ b/src/kademlia/tests/AutoRefreshOperationTest2.java @@ -2,8 +2,9 @@ package kademlia.tests; import java.util.Timer; import java.util.TimerTask; -import kademlia.core.Configuration; -import kademlia.core.Kademlia; +import kademlia.core.DefaultConfiguration; +import kademlia.Kademlia; +import kademlia.core.KadConfiguration; import kademlia.node.NodeId; /** @@ -32,8 +33,7 @@ public class AutoRefreshOperationTest2 DHTContentImpl c = new DHTContentImpl(new NodeId("AS84k678947584567465"), kad1.getOwnerId()); c.setData("Setting the data"); kad1.putLocally(c); - - + System.out.println("\n Content ID: " + c.getKey()); System.out.println(kad1.getNode() + " Distance from content: " + kad1.getNode().getNodeId().getDistance(c.getKey())); System.out.println(kad2.getNode() + " Distance from content: " + kad2.getNode().getNodeId().getDistance(c.getKey())); @@ -45,6 +45,7 @@ public class AutoRefreshOperationTest2 System.out.println(kad3); /* Print the node states every few minutes */ + KadConfiguration config = new DefaultConfiguration(); Timer timer = new Timer(true); timer.schedule( new TimerTask() @@ -58,7 +59,7 @@ public class AutoRefreshOperationTest2 } }, // Delay // Interval - Configuration.RESTORE_INTERVAL, Configuration.RESTORE_INTERVAL + config.restoreInterval(), config.restoreInterval() ); } diff --git a/src/kademlia/tests/ContentSendingTest.java b/src/kademlia/tests/ContentSendingTest.java index aa3134c..1eb3761 100644 --- a/src/kademlia/tests/ContentSendingTest.java +++ b/src/kademlia/tests/ContentSendingTest.java @@ -3,7 +3,7 @@ package kademlia.tests; import java.io.IOException; import java.util.List; import kademlia.core.GetParameter; -import kademlia.core.Kademlia; +import kademlia.Kademlia; import kademlia.dht.KadContent; import kademlia.node.NodeId; diff --git a/src/kademlia/tests/NodeConnectionTest.java b/src/kademlia/tests/NodeConnectionTest.java index 5df8bd5..ea6d3de 100644 --- a/src/kademlia/tests/NodeConnectionTest.java +++ b/src/kademlia/tests/NodeConnectionTest.java @@ -1,7 +1,7 @@ package kademlia.tests; import java.io.IOException; -import kademlia.core.Kademlia; +import kademlia.Kademlia; import kademlia.node.NodeId; /** diff --git a/src/kademlia/tests/RefreshOperationTest.java b/src/kademlia/tests/RefreshOperationTest.java index 1a7ea9d..160e4b5 100644 --- a/src/kademlia/tests/RefreshOperationTest.java +++ b/src/kademlia/tests/RefreshOperationTest.java @@ -3,7 +3,7 @@ package kademlia.tests; import java.io.IOException; import java.util.List; import kademlia.core.GetParameter; -import kademlia.core.Kademlia; +import kademlia.Kademlia; import kademlia.dht.KadContent; import kademlia.node.NodeId; diff --git a/src/kademlia/tests/SaveStateTest.java b/src/kademlia/tests/SaveStateTest.java index 5b8f8ee..426f32c 100644 --- a/src/kademlia/tests/SaveStateTest.java +++ b/src/kademlia/tests/SaveStateTest.java @@ -1,6 +1,6 @@ package kademlia.tests; -import kademlia.core.Kademlia; +import kademlia.Kademlia; import kademlia.node.NodeId; /** diff --git a/src/kademlia/tests/SimpleMessageTest.java b/src/kademlia/tests/SimpleMessageTest.java index e3ddabe..d26783c 100644 --- a/src/kademlia/tests/SimpleMessageTest.java +++ b/src/kademlia/tests/SimpleMessageTest.java @@ -1,7 +1,7 @@ package kademlia.tests; import java.io.IOException; -import kademlia.core.Kademlia; +import kademlia.Kademlia; import kademlia.message.SimpleMessage; import kademlia.node.NodeId; import kademlia.message.SimpleReceiver;