diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index fddcffd..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-/out/
-/build/
-/.idea/
-/.gradle/
\ No newline at end of file
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..2c11fb3
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+influence-bootstrap-node
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..1bec35e
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..e033963
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/je_5_0_104.xml b/.idea/libraries/je_5_0_104.xml
new file mode 100644
index 0000000..0309a85
--- /dev/null
+++ b/.idea/libraries/je_5_0_104.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
deleted file mode 100644
index 95eb89b..0000000
--- a/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-Main-Class: io.github.chronosx88.dhtBootstrap.Main
-
diff --git a/build.gradle b/build.gradle
index 5826935..eeffc02 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,8 +1,20 @@
plugins {
id 'java'
+ id 'org.jetbrains.kotlin.jvm' version '1.3.21'
}
+group 'io.github.chronosx88'
+version '0.1'
-version '1.0-SNAPSHOT'
+task fatJar(type: Jar) {
+ manifest {
+ attributes(
+ 'Main-Class': 'io.github.chronosx88.dhtBootstrap.MainKt'
+ )
+ }
+ baseName = project.name + '-all'
+ from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+ with jar
+}
sourceCompatibility = 1.8
@@ -14,14 +26,22 @@ repositories {
}
dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8'
implementation 'org.slf4j:slf4j-log4j12:+'
}
+compileKotlin {
+ kotlinOptions.jvmTarget = "1.8"
+}
+compileTestKotlin {
+ kotlinOptions.jvmTarget = "1.8"
+}
jar {
manifest {
attributes(
- 'Main-Class': 'io.github.chronosx88.dhtBootstrap.Main'
+ 'Main-Class': 'io.github.chronosx88.dhtBootstrap.MainKt'
)
}
-}
+ from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..29e08e8
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 3a03f4e..412452a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Thu Feb 28 14:04:57 MSK 2019
+#Sun Apr 07 16:50:17 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/settings.gradle b/settings.gradle
index 6e550ba..928958f 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,3 @@
rootProject.name = 'Influence-Bootstrap-Node'
+rootProject.name = 'influence-bootstrap-node'
diff --git a/src/libs/je-5.0.104.jar b/src/libs/je-5.0.104.jar
new file mode 100644
index 0000000..dcfe9a3
Binary files /dev/null and b/src/libs/je-5.0.104.jar differ
diff --git a/src/main/java/io/github/chronosx88/dhtBootstrap/Main.java b/src/main/java/io/github/chronosx88/dhtBootstrap/Main.java
deleted file mode 100644
index e75732f..0000000
--- a/src/main/java/io/github/chronosx88/dhtBootstrap/Main.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package io.github.chronosx88.dhtBootstrap;
-
-import net.tomp2p.connection.RSASignatureFactory;
-import net.tomp2p.dht.PeerBuilderDHT;
-import net.tomp2p.dht.PeerDHT;
-import net.tomp2p.nat.PeerBuilderNAT;
-import net.tomp2p.p2p.PeerBuilder;
-import net.tomp2p.peers.Number160;
-import net.tomp2p.relay.RelayType;
-import net.tomp2p.relay.tcp.TCPRelayServerConfig;
-import net.tomp2p.storage.StorageDisk;
-
-import java.io.*;
-import java.util.Properties;
-import java.util.UUID;
-
-public class Main {
- private static PeerDHT peerDHT;
- private static Number160 peerID;
- private static Properties props;
- private static final String DATA_DIR_PATH = System.getProperty("user.home") + "/.local/share/Influence-Bootstrap/";
-
- public static void main(String[] args) {
- props = new Properties();
- org.apache.log4j.BasicConfigurator.configure();
- File dataDir = new File(DATA_DIR_PATH);
- File config = new File(DATA_DIR_PATH + "config.properties");
- try {
- if(!dataDir.exists() && !config.exists()) {
- dataDir.mkdir();
- config.createNewFile();
- props.setProperty("isFirstRun", "false");
- props.setProperty("peerID", UUID.randomUUID().toString());
- props.store(new FileWriter(config), "");
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- try{
- props.load(new FileInputStream(config));
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- peerID = Number160.createHash(props.getProperty("peerID"));
-
- try {
- peerDHT =
- new PeerBuilderDHT(new PeerBuilder(peerID).ports(7243).start())
- .storage(
- new StorageDisk(
- peerID,
- new File(DATA_DIR_PATH),
- new RSASignatureFactory()
- )
- ).start();
- new PeerBuilderNAT(peerDHT.peer())
- .addRelayServerConfiguration(RelayType.OPENTCP, new TCPRelayServerConfig())
- .start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/src/main/kotlin/io/github/chronosx88/dhtBootstrap/DataSerializer.kt b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/DataSerializer.kt
new file mode 100644
index 0000000..2f7777f
--- /dev/null
+++ b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/DataSerializer.kt
@@ -0,0 +1,103 @@
+package io.github.chronosx88.dhtBootstrap
+
+import com.sleepycat.bind.EntryBinding
+import com.sleepycat.je.DatabaseEntry
+
+import net.tomp2p.connection.SignatureFactory
+import net.tomp2p.storage.AlternativeCompositeByteBuf
+import net.tomp2p.storage.Data
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.io.OutputStream
+import java.io.Serializable
+import java.nio.ByteBuffer
+import java.security.InvalidKeyException
+import java.security.SignatureException
+
+import io.netty.buffer.Unpooled
+import org.slf4j.LoggerFactory
+
+class DataSerializer(private val signatureFactory: SignatureFactory) : EntryBinding, Serializable {
+ private val LOG_TAG = "DataSerializer"
+ private val LOG = LoggerFactory.getLogger(DataSerializer::class.java)
+
+ override fun entryToObject(databaseEntry: DatabaseEntry): Data? {
+ if (databaseEntry.data == null) {
+ return null
+ }
+ val inputStream = ByteArrayInputStream(databaseEntry.data)
+ var buf = Unpooled.buffer()
+ var data: Data? = null
+ while (data == null) {
+ buf.writeByte(inputStream.read())
+ data = Data.decodeHeader(buf, signatureFactory)
+ }
+ val len = data.length()
+ val me = ByteArray(len)
+ try {
+ inputStream.read(me)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ buf = Unpooled.wrappedBuffer(me)
+ var retVal = data.decodeBuffer(buf)
+ if (!retVal) {
+ LOG.error("# ERROR: Data could not be deserialized!")
+ }
+ retVal = data.decodeDone(buf, signatureFactory)
+ if (!retVal) {
+ LOG.error("# ERROR: Signature could not be read!")
+ }
+ return data
+ }
+
+ override fun objectToEntry(data: Data, databaseEntry: DatabaseEntry) {
+ val forSigningKP = keyPairManager.getKeyPair("mainSigningKeyPair")
+ data.sign(forSigningKP)
+ val out = ByteArrayOutputStream()
+ val acb = AlternativeCompositeByteBuf.compBuffer(AlternativeCompositeByteBuf.UNPOOLED_HEAP)
+ try {
+ // header first
+ data.encodeHeader(acb, signatureFactory)
+ writeData(out, acb.nioBuffers())
+ acb.skipBytes(acb.writerIndex())
+ // next data - no need to copy to another buffer, just take the data
+ // from memory
+ writeData(out, data.toByteBuffers())
+ // rest
+ data.encodeDone(acb, signatureFactory)
+ writeData(out, acb.nioBuffers())
+ } catch (e: SignatureException) {
+ e.printStackTrace()
+ } catch (e: InvalidKeyException) {
+ e.printStackTrace()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ databaseEntry.data = out.toByteArray()
+ }
+
+ @Throws(IOException::class)
+ private fun writeData(out: OutputStream, nioBuffers: Array) {
+ val length = nioBuffers.size
+ for (i in 0 until length) {
+ val remaining = nioBuffers[i].remaining()
+ if (nioBuffers[i].hasArray()) {
+ out.write(nioBuffers[i].array(), nioBuffers[i].arrayOffset(), remaining)
+ } else {
+ val me = ByteArray(remaining)
+ nioBuffers[i].get(me)
+ out.write(me)
+ }
+ }
+ }
+
+ companion object {
+ private const val serialVersionUID = 1428836065493792295L
+ private val keyPairManager = KeyPairManager()
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/io/github/chronosx88/dhtBootstrap/KeyPairManager.kt b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/KeyPairManager.kt
new file mode 100644
index 0000000..984c7ad
--- /dev/null
+++ b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/KeyPairManager.kt
@@ -0,0 +1,85 @@
+package io.github.chronosx88.dhtBootstrap
+
+import java.io.IOException
+import java.io.FileOutputStream
+import java.io.File
+import java.security.KeyPair
+import java.security.NoSuchAlgorithmException
+import java.security.KeyPairGenerator
+import java.io.FileInputStream
+
+
+class KeyPairManager {
+ private val keyPairDir: File
+ private val serializer: Serializer
+
+ init {
+ this.keyPairDir = File(DATA_DIR_PATH, "keyPairs")
+ if (!this.keyPairDir.exists()) {
+ this.keyPairDir.mkdir()
+ }
+ this.serializer = Serializer()
+ }
+
+ fun openMainKeyPair(): KeyPair? {
+ return getKeyPair("mainKeyPair")
+ }
+
+ fun getKeyPair(keyPairName: String): KeyPair? {
+ var keyPairName = keyPairName
+ keyPairName = "$keyPairName.kp"
+ val keyPairFile = File(keyPairDir, keyPairName)
+ return if (!keyPairFile.exists()) {
+ createKeyPairFile(keyPairFile)
+ } else openKeyPairFile(keyPairFile)
+ }
+
+ @Synchronized
+ private fun openKeyPairFile(keyPairFile: File): KeyPair? {
+ var keyPair: KeyPair? = null
+ try {
+ val inputStream = FileInputStream(keyPairFile)
+ val serializedKeyPair = ByteArray(keyPairFile.length().toInt())
+ inputStream.read(serializedKeyPair)
+ inputStream.close()
+ keyPair = serializer.deserialize(serializedKeyPair)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ return keyPair
+ }
+
+ @Synchronized
+ private fun createKeyPairFile(keyPairFile: File): KeyPair? {
+ var keyPair: KeyPair? = null
+ try {
+ keyPairFile.createNewFile()
+ keyPair = KeyPairGenerator.getInstance("DSA").generateKeyPair()
+ val outputStream = FileOutputStream(keyPairFile)
+ outputStream.write(serializer.serialize(keyPair))
+ outputStream.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } catch (e: NoSuchAlgorithmException) {
+ e.printStackTrace()
+ }
+
+ return keyPair
+ }
+
+ @Synchronized
+ fun saveKeyPair(keyPairID: String, keyPair: KeyPair) {
+ val keyPairFile = File(keyPairDir, "$keyPairID.kp")
+ if (!keyPairFile.exists()) {
+ try {
+ val outputStream = FileOutputStream(keyPairFile)
+ outputStream.write(serializer.serialize(keyPair))
+ outputStream.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+}
diff --git a/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Main.kt b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Main.kt
new file mode 100644
index 0000000..dbd8708
--- /dev/null
+++ b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Main.kt
@@ -0,0 +1,69 @@
+package io.github.chronosx88.dhtBootstrap
+
+import net.tomp2p.connection.DSASignatureFactory
+import net.tomp2p.dht.PeerBuilderDHT
+import net.tomp2p.dht.PeerDHT
+import net.tomp2p.nat.PeerBuilderNAT
+import net.tomp2p.p2p.PeerBuilder
+import net.tomp2p.peers.Number160
+import net.tomp2p.relay.RelayType
+import net.tomp2p.relay.tcp.TCPRelayServerConfig
+import net.tomp2p.replication.AutoReplication
+
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileWriter
+import java.io.IOException
+import java.util.Properties
+import java.util.UUID
+
+
+var peerDHT: PeerDHT? = null
+var peerID: Number160? = null
+var props: Properties? = null
+val DATA_DIR_PATH = System.getProperty("user.home") + "/.local/share/Influence-Bootstrap/"
+
+fun main() {
+ props = Properties()
+ org.apache.log4j.BasicConfigurator.configure()
+ val dataDir = File(DATA_DIR_PATH)
+ val config = File(DATA_DIR_PATH + "config.properties")
+ try {
+ if (!dataDir.exists() && !config.exists()) {
+ dataDir.mkdir()
+ config.createNewFile()
+ props!!.setProperty("isFirstRun", "false")
+ props!!.setProperty("peerID", UUID.randomUUID().toString())
+ props!!.store(FileWriter(config), "")
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ try {
+ props!!.load(FileInputStream(config))
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ peerID = Number160.createHash(props!!.getProperty("peerID"))
+
+ try {
+ peerDHT = PeerBuilderDHT(PeerBuilder(peerID).ports(7243).start())
+ .storage(
+ StorageBerkeleyDB(
+ peerID!!,
+ File(DATA_DIR_PATH),
+ DSASignatureFactory()
+ )
+ ).start()
+ PeerBuilderNAT(peerDHT!!.peer())
+ .addRelayServerConfiguration(RelayType.OPENTCP, TCPRelayServerConfig())
+ .start()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ //val replication = AutoReplication(peerDHT!!.peer())
+ //replication.start()
+}
diff --git a/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Serializer.kt b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Serializer.kt
new file mode 100644
index 0000000..822e8b2
--- /dev/null
+++ b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/Serializer.kt
@@ -0,0 +1,50 @@
+package io.github.chronosx88.dhtBootstrap
+
+import com.sleepycat.bind.EntryBinding
+import com.sleepycat.je.DatabaseEntry
+
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.IOException
+import java.io.ObjectInputStream
+import java.io.ObjectOutputStream
+
+class Serializer : EntryBinding {
+ fun serialize(obj: T): ByteArray {
+ val byteArray = ByteArrayOutputStream()
+ try {
+ val objectOutputStream = ObjectOutputStream(byteArray)
+ objectOutputStream.writeObject(obj)
+ objectOutputStream.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ return byteArray.toByteArray()
+ }
+
+ fun deserialize(serializedObject: ByteArray?): T? {
+ if (serializedObject == null)
+ return null
+ val inputStream = ByteArrayInputStream(serializedObject)
+ var obj: Any? = null
+ try {
+ val objectInputStream = ObjectInputStream(inputStream)
+ obj = objectInputStream.readObject()
+ } catch (e: ClassNotFoundException) {
+ e.printStackTrace()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ return obj as T?
+ }
+
+ override fun entryToObject(databaseEntry: DatabaseEntry): T? {
+ return deserialize(databaseEntry.data)
+ }
+
+ override fun objectToEntry(obj: T, databaseEntry: DatabaseEntry) {
+ databaseEntry.data = serialize(obj)
+ }
+}
diff --git a/src/main/kotlin/io/github/chronosx88/dhtBootstrap/StorageBerkeleyDB.kt b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/StorageBerkeleyDB.kt
new file mode 100644
index 0000000..ff0e8a2
--- /dev/null
+++ b/src/main/kotlin/io/github/chronosx88/dhtBootstrap/StorageBerkeleyDB.kt
@@ -0,0 +1,262 @@
+package io.github.chronosx88.dhtBootstrap
+
+import com.sleepycat.collections.StoredSortedMap
+import com.sleepycat.je.Database
+import com.sleepycat.je.DatabaseConfig
+import com.sleepycat.je.Environment
+import com.sleepycat.je.EnvironmentConfig
+import net.tomp2p.connection.SignatureFactory
+import net.tomp2p.dht.Storage
+import net.tomp2p.peers.Number160
+import net.tomp2p.peers.Number320
+import net.tomp2p.peers.Number480
+import net.tomp2p.peers.Number640
+import net.tomp2p.storage.Data
+import java.io.File
+import java.security.PublicKey
+import java.util.*
+import java.util.concurrent.ConcurrentHashMap
+
+
+class StorageBerkeleyDB(peerId: Number160, path : File, signatureFactory: SignatureFactory) : Storage {
+ // Core
+ private val dataMap: StoredSortedMap
+ // Maintenance
+ private val timeoutMap: StoredSortedMap
+ private val timeoutMapRev: StoredSortedMap>
+ // Protection
+ private val protectedDomainMap: StoredSortedMap
+ private val protectedEntryMap: StoredSortedMap
+ // Responsibility
+ private val responsibilityMap: StoredSortedMap
+ private val responsibilityMapRev: StoredSortedMap>
+
+ private val dataMapDB: Database
+ private val timeoutMapDB: Database
+ private val timeoutMapRevDB: Database
+ private val protectedDomainMapDB: Database
+ private val protectedEntryMapDB: Database
+ private val responsibilityMapDB: Database
+ private val responsibilityMapRevDB: Database
+
+
+ private val storageCheckIntervalMillis: Int
+ private val dbEnvironment: Environment
+
+ init {
+ val envConfig = EnvironmentConfig()
+ envConfig.allowCreate = true
+ dbEnvironment = Environment(path, envConfig)
+ val dbConfig = DatabaseConfig()
+ dbConfig.allowCreate = true
+
+ dataMapDB = dbEnvironment.openDatabase(null, "dataMap_$peerId", dbConfig)
+ timeoutMapDB = dbEnvironment.openDatabase(null, "timeoutMap_$peerId", dbConfig)
+ timeoutMapRevDB = dbEnvironment.openDatabase(null, "timeoutMapRev_$peerId", dbConfig)
+ protectedDomainMapDB = dbEnvironment.openDatabase(null, "protectedDomainMap_$peerId", dbConfig)
+ protectedEntryMapDB = dbEnvironment.openDatabase(null, "protectedEntryMap_$peerId", dbConfig)
+ responsibilityMapDB = dbEnvironment.openDatabase(null, "responsibilityMap_$peerId", dbConfig)
+ responsibilityMapRevDB = dbEnvironment.openDatabase(null, "responsibilityMapRev_$peerId", dbConfig)
+
+ storageCheckIntervalMillis = 60 * 1000
+
+ dataMap = StoredSortedMap(dataMapDB, Serializer(), DataSerializer(signatureFactory), true)
+ timeoutMap = StoredSortedMap(timeoutMapDB, Serializer(), Serializer(), true)
+ timeoutMapRev = StoredSortedMap(timeoutMapRevDB, Serializer(), Serializer>(), true)
+ protectedDomainMap = StoredSortedMap(protectedDomainMapDB, Serializer(), Serializer(), true)
+ protectedEntryMap = StoredSortedMap(protectedEntryMapDB, Serializer(), Serializer(), true)
+ responsibilityMap = StoredSortedMap(responsibilityMapDB, Serializer(), Serializer(), true)
+ responsibilityMapRev = StoredSortedMap(responsibilityMapRevDB, Serializer(), Serializer>(), true)
+ }
+
+ override fun contains(key: Number640?): Boolean {
+ return dataMap.containsKey(key)
+ }
+
+ override fun contains(from: Number640?, to: Number640?): Int {
+ return dataMap.subMap(from, true, to, true).size
+ }
+
+ override fun findContentForResponsiblePeerID(peerID: Number160?): MutableSet {
+ return responsibilityMapRev[peerID] as MutableSet
+ }
+
+ override fun findPeerIDsForResponsibleContent(locationKey: Number160?): Number160? {
+ return responsibilityMap[locationKey]
+ }
+
+ override fun put(key: Number640?, value: Data?): Data? {
+ return dataMap.put(key, value)
+ }
+
+ override fun get(key: Number640?): Data? {
+ return dataMap[key]
+ }
+
+ override fun remove(key: Number640?, returnData: Boolean): Data? {
+ return dataMap.remove(key)
+ }
+
+ override fun remove(from: Number640?, to: Number640?): NavigableMap {
+ val tmp = dataMap.subMap(from, true, to, true)
+ val retVal = TreeMap()
+ for(entry : Map.Entry in tmp.entries) {
+ retVal[entry.key] = entry.value
+ }
+ tmp.clear()
+ return retVal
+ }
+
+ override fun addTimeout(key: Number640, expiration: Long) {
+ val oldExpiration = timeoutMap.put(key, expiration)
+ putIfAbsent2(expiration, key)
+ if (oldExpiration == null) {
+ return
+ }
+ removeRevTimeout(key, oldExpiration)
+ }
+
+ private fun putIfAbsent2(expiration: Long, key: Number640) {
+ var timeouts = timeoutMapRev[expiration]
+ //var timeouts : MutableSet = timeoutMapRev[expiration] as MutableSet
+ if (timeouts == null) {
+ timeouts = Collections.newSetFromMap(ConcurrentHashMap())
+ }
+ (timeouts as MutableSet).add(key)
+ timeoutMapRev[expiration] = timeouts
+ }
+
+ private fun removeRevTimeout(key: Number640, expiration: Long?) {
+ val tmp = timeoutMapRev[expiration] as MutableSet
+ if (tmp != null) {
+ tmp.remove(key)
+ if (tmp.isEmpty()) {
+ timeoutMapRev.remove(expiration)
+ } else {
+ timeoutMapRev[expiration!!] = tmp
+ }
+ }
+ }
+
+ override fun updateResponsibilities(locationKey: Number160, peerId: Number160?): Boolean {
+ val oldPeerID = responsibilityMap.put(locationKey, peerId)
+ val hasChanged: Boolean
+ if (oldPeerID != null) {
+ if (oldPeerID == peerId) {
+ hasChanged = false
+ } else {
+ removeRevResponsibility(oldPeerID, locationKey)
+ hasChanged = true
+ }
+ } else {
+ hasChanged = true
+ }
+ var contentIDs: MutableSet? = responsibilityMapRev[peerId] as MutableSet
+ if (contentIDs == null) {
+ contentIDs = HashSet()
+ }
+ contentIDs.add(locationKey)
+ responsibilityMapRev[peerId] = contentIDs
+ return hasChanged
+ }
+
+ private fun removeRevResponsibility(peerId: Number160, locationKey: Number160) {
+ val contentIDs = responsibilityMapRev[peerId] as MutableSet
+ if (contentIDs != null) {
+ contentIDs.remove(locationKey)
+ if (contentIDs.isEmpty()) {
+ responsibilityMapRev.remove(peerId)
+ } else {
+ responsibilityMapRev[peerId] = contentIDs
+ }
+ }
+ }
+
+ override fun protectDomain(key: Number320?, publicKey: PublicKey?): Boolean {
+ protectedDomainMap[key] = publicKey
+ return true
+ }
+
+ override fun storageCheckIntervalMillis(): Int {
+ return storageCheckIntervalMillis
+ }
+
+ override fun isDomainProtectedByOthers(key: Number320?, publicKey: PublicKey?): Boolean {
+ val other = protectedDomainMap[key] ?: return false
+ return other != publicKey
+ }
+
+ override fun removeTimeout(key: Number640) {
+ val expiration = timeoutMap.remove(key) ?: return
+ removeRevTimeout(key, expiration)
+ }
+
+ override fun removeResponsibility(locationKey: Number160) {
+ val peerId = responsibilityMap.remove(locationKey)
+ if (peerId != null) {
+ removeRevResponsibility(peerId, locationKey)
+ }
+ }
+
+ override fun protectEntry(key: Number480?, publicKey: PublicKey?): Boolean {
+ protectedEntryMap[key] = publicKey
+ return true
+ }
+
+ override fun map(): NavigableMap {
+ val retVal = TreeMap()
+ for ((key, value) in dataMap) {
+ retVal[key] = value
+ }
+
+ return retVal
+ }
+
+ override fun isEntryProtectedByOthers(key: Number480?, publicKey: PublicKey?): Boolean {
+ val other = protectedEntryMap[key] ?: return false
+ return other != publicKey
+ }
+
+ override fun subMap(from: Number640?, to: Number640?, limit: Int, ascending: Boolean): NavigableMap {
+ val tmp = dataMap.subMap(from, true, to, true)
+ val descendingMap = TreeMap(tmp).descendingMap()
+ val retVal = TreeMap()
+ if (limit < 0) {
+ for ((key, value) in if (ascending) tmp else descendingMap) {
+ retVal[key] = value
+ }
+ } else {
+ val limit1 = Math.min(limit, tmp.size)
+ val iterator = if (ascending)
+ tmp.entries.iterator()
+ else
+ descendingMap.entries.iterator()
+ var i = 0
+ while (iterator.hasNext() && i < limit1) {
+ val entry = iterator.next()
+ retVal[entry.key] = entry.value
+ i++
+ }
+ }
+ return retVal
+ }
+
+ override fun subMapTimeout(to: Long): MutableCollection {
+ val tmp = timeoutMapRev.subMap(0L, to)
+ val toRemove = ArrayList()
+ for (set in tmp.values) {
+ toRemove.addAll(set)
+ }
+ return toRemove
+ }
+
+ override fun close() {
+ dataMapDB.close()
+ timeoutMapDB.close()
+ timeoutMapRevDB.close()
+ protectedDomainMapDB.close()
+ protectedEntryMapDB.close()
+ responsibilityMapDB.close()
+ responsibilityMapRevDB.close()
+ }
+}