mirror of
https://github.com/ChronosX88/Influence-Bootstrap-Node.git
synced 2024-11-22 15:22:18 +00:00
Added StorageBerkeleyDB and rewritten all in Kotlin
This commit is contained in:
parent
8d1f531160
commit
eb1f703d4f
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
/out/
|
|
||||||
/build/
|
|
||||||
/.idea/
|
|
||||||
/.gradle/
|
|
1
.idea/.name
Normal file
1
.idea/.name
Normal file
@ -0,0 +1 @@
|
|||||||
|
influence-bootstrap-node
|
10
.idea/codeStyles/Project.xml
Normal file
10
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
8
.idea/compiler.xml
Normal file
8
.idea/compiler.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel>
|
||||||
|
<module name="io.github.chronosx88.influence-bootstrap-node.test" target="1.8" />
|
||||||
|
</bytecodeTargetLevel>
|
||||||
|
</component>
|
||||||
|
</project>
|
7
.idea/libraries/je_5_0_104.xml
Normal file
7
.idea/libraries/je_5_0_104.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="je-5.0.104">
|
||||||
|
<CLASSES />
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -1,3 +0,0 @@
|
|||||||
Manifest-Version: 1.0
|
|
||||||
Main-Class: io.github.chronosx88.dhtBootstrap.Main
|
|
||||||
|
|
24
build.gradle
24
build.gradle
@ -1,8 +1,20 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id 'java'
|
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
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
@ -14,14 +26,22 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8'
|
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8'
|
||||||
implementation 'org.slf4j:slf4j-log4j12:+'
|
implementation 'org.slf4j:slf4j-log4j12:+'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
}
|
||||||
jar {
|
jar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
'Main-Class': 'io.github.chronosx88.dhtBootstrap.Main'
|
'Main-Class': 'io.github.chronosx88.dhtBootstrap.MainKt'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
|
||||||
}
|
}
|
1
gradle.properties
Normal file
1
gradle.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
kotlin.code.style=official
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,4 +1,4 @@
|
|||||||
#Thu Feb 28 14:04:57 MSK 2019
|
#Sun Apr 07 16:50:17 MSK 2019
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
rootProject.name = 'Influence-Bootstrap-Node'
|
rootProject.name = 'Influence-Bootstrap-Node'
|
||||||
|
rootProject.name = 'influence-bootstrap-node'
|
||||||
|
|
||||||
|
BIN
src/libs/je-5.0.104.jar
Normal file
BIN
src/libs/je-5.0.104.jar
Normal file
Binary file not shown.
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Data>, 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<ByteBuffer>) {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
@ -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<KeyPair>
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/main/kotlin/io/github/chronosx88/dhtBootstrap/Main.kt
Normal file
69
src/main/kotlin/io/github/chronosx88/dhtBootstrap/Main.kt
Normal file
@ -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()
|
||||||
|
}
|
@ -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<T> : EntryBinding<T> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -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<Number640, Data>
|
||||||
|
// Maintenance
|
||||||
|
private val timeoutMap: StoredSortedMap<Number640, Long>
|
||||||
|
private val timeoutMapRev: StoredSortedMap<Long, Set<Number640>>
|
||||||
|
// Protection
|
||||||
|
private val protectedDomainMap: StoredSortedMap<Number320, PublicKey>
|
||||||
|
private val protectedEntryMap: StoredSortedMap<Number480, PublicKey>
|
||||||
|
// Responsibility
|
||||||
|
private val responsibilityMap: StoredSortedMap<Number160, Number160>
|
||||||
|
private val responsibilityMapRev: StoredSortedMap<Number160, Set<Number160>>
|
||||||
|
|
||||||
|
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<Number640>(), DataSerializer(signatureFactory), true)
|
||||||
|
timeoutMap = StoredSortedMap(timeoutMapDB, Serializer<Number640>(), Serializer<Long>(), true)
|
||||||
|
timeoutMapRev = StoredSortedMap(timeoutMapRevDB, Serializer<Long>(), Serializer<Set<Number640>>(), true)
|
||||||
|
protectedDomainMap = StoredSortedMap(protectedDomainMapDB, Serializer<Number320>(), Serializer<PublicKey>(), true)
|
||||||
|
protectedEntryMap = StoredSortedMap(protectedEntryMapDB, Serializer<Number480>(), Serializer<PublicKey>(), true)
|
||||||
|
responsibilityMap = StoredSortedMap(responsibilityMapDB, Serializer<Number160>(), Serializer<Number160>(), true)
|
||||||
|
responsibilityMapRev = StoredSortedMap(responsibilityMapRevDB, Serializer<Number160>(), Serializer<Set<Number160>>(), 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<Number160> {
|
||||||
|
return responsibilityMapRev[peerID] as MutableSet<Number160>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Number640, Data> {
|
||||||
|
val tmp = dataMap.subMap(from, true, to, true)
|
||||||
|
val retVal = TreeMap<Number640, Data>()
|
||||||
|
for(entry : Map.Entry<Number640, Data> 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<Number640> = timeoutMapRev[expiration] as MutableSet<Number640>
|
||||||
|
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<Number640>
|
||||||
|
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<Number160>? = 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<Number640, Data> {
|
||||||
|
val retVal = TreeMap<Number640, Data>()
|
||||||
|
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<Number640, Data> {
|
||||||
|
val tmp = dataMap.subMap(from, true, to, true)
|
||||||
|
val descendingMap = TreeMap<Number640, Data>(tmp).descendingMap()
|
||||||
|
val retVal = TreeMap<Number640, Data>()
|
||||||
|
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<Number640> {
|
||||||
|
val tmp = timeoutMapRev.subMap(0L, to)
|
||||||
|
val toRemove = ArrayList<Number640>()
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user