Added BerkeleyDB as storage backend, added support of Kotlin lang, fixed some bugs

This commit is contained in:
ChronosX88 2019-04-07 14:04:50 +04:00
parent 8a0e370936
commit e7862b5b1c
15 changed files with 413 additions and 310 deletions

View File

@ -3,9 +3,6 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<compositeConfiguration>
<compositeBuild compositeDefinitionSource="SCRIPT" />
</compositeConfiguration>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">

View File

@ -1,4 +1,6 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 28
@ -52,4 +54,8 @@ dependencies {
implementation group: 'org.springframework.security', name: 'spring-security-crypto', version: '3.1.0.RELEASE'
implementation 'de.hdodenhof:circleimageview:3.0.0'
implementation group: 'org.objenesis', name: 'objenesis', version: '2.6'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
repositories {
mavenCentral()
}

Binary file not shown.

View File

@ -1,192 +0,0 @@
package io.github.chronosx88.influence.helpers;
import android.util.Log;
import net.tomp2p.connection.SignatureFactory;
import net.tomp2p.peers.Number160;
import net.tomp2p.storage.AlternativeCompositeByteBuf;
import net.tomp2p.storage.Data;
import org.mapdb.Serializer;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.SignatureException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class DataSerializerEx implements Serializer<Data>, Serializable {
private static final long serialVersionUID = 1428836065493792295L;
//TODO: test the performance impact
private static final int MAX_SIZE = 10 * 1024;
private File path;
private SignatureFactory signatureFactory;
final private static KeyPairManager keyPairManager = new KeyPairManager();
public DataSerializerEx() { }
public DataSerializerEx(File path, SignatureFactory signatureFactory) {
this.path = path;
this.signatureFactory = signatureFactory;
}
@Override
public void serialize(DataOutput out, Data value) throws IOException {
if (value.length() > MAX_SIZE) {
// header, 1 means stored on disk in a file
out.writeByte(1);
serializeFile(out, value);
} else {
// header, 0 means stored on disk with MapDB
out.writeByte(0);
serializeMapDB(out, value);
}
}
private void serializeMapDB(DataOutput out, Data value) throws IOException {
KeyPair keyPair = keyPairManager.getKeyPair("mainKeyForSign");
value.sign(keyPair);
AlternativeCompositeByteBuf acb = AlternativeCompositeByteBuf.compBuffer(AlternativeCompositeByteBuf.UNPOOLED_HEAP);
// store data to disk
// header first
value.encodeHeader(acb, signatureFactory);
write(out, acb.nioBuffers());
acb.skipBytes(acb.writerIndex());
// next data - no need to copy to another buffer, just take the data
// from memory
write(out, value.toByteBuffers());
// rest
try {
value.encodeDone(acb, signatureFactory);
write(out, acb.nioBuffers());
} catch (InvalidKeyException e) {
throw new IOException(e);
} catch (SignatureException e) {
throw new IOException(e);
}
}
private void serializeFile(DataOutput out, Data value) throws IOException, FileNotFoundException {
Number160 hash = value.hash();
// store file name
out.write(hash.toByteArray());
// store as external file, create path
RandomAccessFile file = null;
FileChannel rwChannel = null;
try {
file = new RandomAccessFile(new File(path, hash.toString()), "rw");
rwChannel = file.getChannel();
AlternativeCompositeByteBuf acb = AlternativeCompositeByteBuf.compBuffer(AlternativeCompositeByteBuf.UNPOOLED_HEAP);
// store data to disk
// header first
value.encodeHeader(acb, signatureFactory);
rwChannel.write(acb.nioBuffers());
// next data - no need to copy to another buffer, just take the
// data from memory
rwChannel.write(value.toByteBuffers());
// rest
try {
value.encodeDone(acb, signatureFactory);
rwChannel.write(acb.nioBuffers());
} catch (InvalidKeyException e) {
throw new IOException(e);
} catch (SignatureException e) {
throw new IOException(e);
}
} finally {
if (rwChannel != null) {
rwChannel.close();
}
if (file != null) {
file.close();
}
}
}
private void write(DataOutput out, ByteBuffer[] nioBuffers) throws IOException {
final int length = nioBuffers.length;
for(int i=0;i < length; i++) {
int remaining = nioBuffers[i].remaining();
if(nioBuffers[i].hasArray()) {
out.write(nioBuffers[i].array(), nioBuffers[i].arrayOffset(), remaining);
} else {
byte[] me = new byte[remaining];
nioBuffers[i].get(me);
out.write(me);
}
}
}
@Override
public Data deserialize(DataInput in, int available) throws IOException {
int header = in.readByte();
if(header == 1) {
return deserializeFile(in);
} else if(header == 0) {
return deserializeMapDB(in);
} else {
//throw new IOException("unexpected header: " + header);
return null;
}
}
private Data deserializeMapDB(DataInput in) throws IOException {
ByteBuf buf = Unpooled.buffer();
Data data = null;
while(data == null) {
buf.writeByte(in.readByte());
data = Data.decodeHeader(buf, signatureFactory);
}
int len = data.length();
byte me[] = new byte[len];
in.readFully(me);
buf = Unpooled.wrappedBuffer(me);
boolean retVal = data.decodeBuffer(buf);
if(!retVal) {
throw new IOException("data could not be read");
}
retVal = data.decodeDone(buf, signatureFactory);
if(!retVal) {
//throw new IOException("signature could not be read");
Log.e("DataSerializerEx", "# Signature could not be read!");
}
return data;
}
private Data deserializeFile(DataInput in) throws IOException, FileNotFoundException {
byte[] me = new byte[Number160.BYTE_ARRAY_SIZE];
in.readFully(me);
Number160 hash = new Number160(me);
RandomAccessFile file = new RandomAccessFile(new File(path, hash.toString()), "r");
FileChannel inChannel = file.getChannel();
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
buffer.load();
ByteBuf buf = Unpooled.wrappedBuffer(buffer);
Data data = Data.decodeHeader(buf, signatureFactory);
data.decodeBuffer(buf);
data.decodeDone(buf, signatureFactory);
file.close();
return data;
}
@Override
public int fixedSize() {
return -1;
}
}

View File

@ -0,0 +1,104 @@
package io.github.chronosx88.influence.helpers
import android.util.Log
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
class DataSerializerEx(private val signatureFactory: SignatureFactory) : EntryBinding<Data>, Serializable {
private val LOG_TAG = "DataSerializerEx"
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.e(LOG_TAG, "# ERROR: Data could not be deserialized!")
}
retVal = data.decodeDone(buf, signatureFactory)
if (!retVal) {
Log.e(LOG_TAG, "# 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()
}
}

View File

@ -10,12 +10,14 @@ import java.security.NoSuchAlgorithmException;
public class KeyPairManager {
private File keyPairDir;
private Serializer<KeyPair> serializer;
public KeyPairManager() {
this.keyPairDir = new File(AppHelper.getContext().getFilesDir().getAbsoluteFile(), "keyPairs");
if(!this.keyPairDir.exists()) {
this.keyPairDir.mkdir();
}
this.serializer = new Serializer<>();
}
public KeyPair openMainKeyPair() {
@ -39,7 +41,7 @@ public class KeyPairManager {
byte[] serializedKeyPair = new byte[(int) keyPairFile.length()];
inputStream.read(serializedKeyPair);
inputStream.close();
keyPair = (KeyPair) Serializer.deserialize(serializedKeyPair);
keyPair = serializer.deserialize(serializedKeyPair);
} catch (IOException e) {
e.printStackTrace();
}
@ -52,7 +54,7 @@ public class KeyPairManager {
keyPairFile.createNewFile();
keyPair = KeyPairGenerator.getInstance("DSA").generateKeyPair();
FileOutputStream outputStream = new FileOutputStream(keyPairFile);
outputStream.write(Serializer.serialize(keyPair));
outputStream.write(serializer.serialize(keyPair));
outputStream.close();
} catch (IOException | NoSuchAlgorithmException e) {
e.printStackTrace();
@ -65,7 +67,7 @@ public class KeyPairManager {
if(!keyPairFile.exists()) {
try {
FileOutputStream outputStream = new FileOutputStream(keyPairFile);
outputStream.write(Serializer.serialize(keyPair));
outputStream.write(serializer.serialize(keyPair));
outputStream.close();
} catch (IOException e) {
e.printStackTrace();

View File

@ -54,7 +54,7 @@ public class P2PUtils {
.all()
.start()
.awaitUninterruptibly();
if(!futureGet.isEmpty()) {
if(futureGet != null && !futureGet.isEmpty()) {
return futureGet.dataMap();
}
return null;

View File

@ -1,19 +0,0 @@
package io.github.chronosx88.influence.helpers;
import org.springframework.security.crypto.codec.Base64;
import java.io.Serializable;
public class PrepareData {
public static <T> String prepareToStore(T object) {
if(object instanceof Serializable) {
byte[] serializedObject = Serializer.serialize(object);
return new String(Base64.encode(serializedObject));
}
return null;
}
public static Object prepareFromStore(String object) {
return Serializer.deserialize(Base64.decode(object.getBytes()));
}
}

View File

@ -1,17 +1,20 @@
package io.github.chronosx88.influence.helpers;
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;
public class Serializer {
public static byte[] serialize(Object object) {
public class Serializer<T> implements EntryBinding<T> {
public byte[] serialize(T object) {
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArray);
objectOutputStream.writeObject(object);;
objectOutputStream.writeObject(object);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
@ -19,19 +22,7 @@ public class Serializer {
return byteArray.toByteArray();
}
public static Object deserialize(String serializedObject) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedObject.getBytes());
Object object = null;
try {
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
object = objectInputStream.readObject();
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
return object;
}
public static Object deserialize(byte[] serializedObject) {
public T deserialize(byte[] serializedObject) {
if(serializedObject == null)
return null;
ByteArrayInputStream inputStream = new ByteArrayInputStream(serializedObject);
@ -42,6 +33,16 @@ public class Serializer {
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
return object;
return (T) object;
}
@Override
public T entryToObject(DatabaseEntry databaseEntry) {
return deserialize(databaseEntry.getData());
}
@Override
public void objectToEntry(T object, DatabaseEntry databaseEntry) {
databaseEntry.setData(serialize(object));
}
}

View File

@ -0,0 +1,262 @@
package io.github.chronosx88.influence.helpers
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>(), DataSerializerEx(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()
}
}

View File

@ -1,5 +1,5 @@
package io.github.chronosx88.influence.helpers;
/*
import android.util.Log;
import net.tomp2p.connection.DSASignatureFactory;
@ -391,3 +391,4 @@ public class StorageMVStore implements Storage {
return data;
}
}
*/

View File

@ -1,5 +1,5 @@
package io.github.chronosx88.influence.helpers;
/*
import net.tomp2p.connection.SignatureFactory;
import net.tomp2p.dht.Storage;
import net.tomp2p.peers.Number160;
@ -96,27 +96,6 @@ public class StorageMapDB implements Storage {
@Override
public NavigableMap<Number640, Data> remove(Number640 from, Number640 to) {
NavigableMap<Number640, Data> tmp = dataMap.subMap(from, true, to, true);
// new TreeMap<Number640, Data>(tmp); is not possible as this may lead to no such element exception:
//
// java.util.NoSuchElementException: null
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
// at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
// at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
//
// the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
// then you will call next() that returns null and an exception is thrown.
final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
for(final Map.Entry<Number640, Data> entry:tmp.entrySet()) {
retVal.put(entry.getKey(), entry.getValue());
@ -132,28 +111,6 @@ public class StorageMapDB implements Storage {
NavigableMap<Number640, Data> tmp = dataMap.subMap(from, true, to, true);
final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
if (limit < 0) {
// new TreeMap<Number640, Data>(tmp); is not possible as this may lead to no such element exception:
//
// java.util.NoSuchElementException: null
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
// at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
// at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
//
// the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
// then you will call next() that returns null and an exception is thrown.
for(final Map.Entry<Number640, Data> entry:(ascending ? tmp : tmp.descendingMap()).entrySet()) {
retVal.put(entry.getKey(), entry.getValue());
}
@ -171,27 +128,6 @@ public class StorageMapDB implements Storage {
@Override
public NavigableMap<Number640, Data> map() {
// new TreeMap<Number640, Data>(dataMap); is not possible as this may lead to no such element exception:
//
// java.util.NoSuchElementException: null
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapIter.advance(ConcurrentSkipListMap.java:3030) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3100) ~[na:1.7.0_60]
// at java.util.concurrent.ConcurrentSkipListMap$SubMap$SubMapEntryIterator.next(ConcurrentSkipListMap.java:3096) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2394) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2418) ~[na:1.7.0_60]
// at java.util.TreeMap.buildFromSorted(TreeMap.java:2344) ~[na:1.7.0_60]
// at java.util.TreeMap.<init>(TreeMap.java:195) ~[na:1.7.0_60]
// at net.tomp2p.dht.StorageMemory.subMap(StorageMemory.java:119) ~[classes/:na]
//
// the reason is that the size in TreeMap.buildFromSorted is stored beforehand, then iteratated. If the size changes,
// then you will call next() that returns null and an exception is thrown.
final NavigableMap<Number640, Data> retVal = new TreeMap<Number640, Data>();
for(final Map.Entry<Number640, Data> entry:dataMap.entrySet()) {
retVal.put(entry.getKey(), entry.getValue());
@ -354,3 +290,4 @@ public class StorageMapDB implements Storage {
return storageCheckIntervalMillis;
}
}
*/

View File

@ -148,8 +148,8 @@ public class ChatLogic implements IChatLogicContract {
if(messages.size() > 10) {
String messageID = UUID.randomUUID().toString();
try {
chatEntity.chunkCursor += 1;
P2PUtils.put(chatID + "_messages" + chatEntity.chunkCursor, messageID, new Data(gson.toJson(new NextChunkReference(messageID, AppHelper.getPeerID(), AppHelper.getPeerID(), System.currentTimeMillis(), chatEntity.chunkCursor))));
P2PUtils.put(chatEntity.chatID + "_messages" + chunkID, messageID, new Data(gson.toJson(new NextChunkReference(messageID, AppHelper.getPeerID(), AppHelper.getPeerID(), System.currentTimeMillis(), chatEntity.chunkCursor+1))));
P2PUtils.put(chatEntity.chatID + "_newMessage", null, new Data(messageID));
LocalDBWrapper.updateChatEntity(chatEntity);
} catch (IOException e) {
e.printStackTrace();

View File

@ -10,7 +10,6 @@ import com.google.gson.JsonObject;
import net.tomp2p.connection.DSASignatureFactory;
import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.dht.StorageMemory;
import net.tomp2p.futures.FutureBootstrap;
import net.tomp2p.futures.FutureDiscover;
import net.tomp2p.nat.FutureRelayNAT;
@ -43,7 +42,7 @@ import io.github.chronosx88.influence.helpers.JVMShutdownHook;
import io.github.chronosx88.influence.helpers.KeyPairManager;
import io.github.chronosx88.influence.helpers.NetworkHandler;
import io.github.chronosx88.influence.helpers.P2PUtils;
import io.github.chronosx88.influence.helpers.StorageMapDB;
import io.github.chronosx88.influence.helpers.StorageBerkeleyDB;
import io.github.chronosx88.influence.helpers.actions.UIActions;
import io.github.chronosx88.influence.models.PublicUserProfile;
@ -60,6 +59,7 @@ public class MainLogic implements IMainLogicContract {
private AutoReplication replication;
private KeyPairManager keyPairManager;
private Thread checkNewChatsThread = null;
private StorageBerkeleyDB storage;
public MainLogic() {
this.context = AppHelper.getContext();
@ -83,15 +83,16 @@ public class MainLogic implements IMainLogicContract {
new Thread(() -> {
try {
StorageMapDB storageMapDB = new StorageMapDB(peerID, context.getFilesDir(), new DSASignatureFactory());
StorageBerkeleyDB storageBerkeleyDB = new StorageBerkeleyDB(peerID, context.getFilesDir(), new DSASignatureFactory());
this.storage = storageBerkeleyDB;
peerDHT = new PeerBuilderDHT(
new PeerBuilder(peerID)
.ports(7243)
.start()
)
.storage(storageMapDB)
.storage(storageBerkeleyDB)
.start();
Runtime.getRuntime().addShutdownHook(new JVMShutdownHook(storageMapDB));
Runtime.getRuntime().addShutdownHook(new JVMShutdownHook(storageBerkeleyDB));
try {
String bootstrapIP = this.preferences.getString("bootstrapAddress", null);
if(bootstrapIP == null) {
@ -227,7 +228,8 @@ public class MainLogic implements IMainLogicContract {
replication.shutdown().start();
}
peerDHT.peer().announceShutdown().start().awaitUninterruptibly();
peerDHT.peer().shutdown();
peerDHT.peer().shutdown().awaitUninterruptibly();
storage.close();
}).start();
}

View File

@ -1,6 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.21'
repositories {
google()
jcenter()
@ -10,6 +11,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files