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() + } +}