Implemented custom storage for peer (standard impl isn't working on Android, so custom impl was build on top of MVStore), added JBoss Serialization lib. Also added basic peer initialization (non architectured yet)

This commit is contained in:
ChronosX88 2019-03-11 21:05:28 +04:00
parent 1ccc891503
commit cd0300eea0
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
13 changed files with 399 additions and 30 deletions

View File

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

View File

@ -5,7 +5,7 @@
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables"> <option name="myNullables">
<value> <value>
<list size="7"> <list size="10">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" /> <item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@ -13,18 +13,24 @@
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> <item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" /> <item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" /> <item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
</list> </list>
</value> </value>
</option> </option>
<option name="myNotNulls"> <option name="myNotNulls">
<value> <value>
<list size="6"> <list size="9">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" /> <item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" /> <item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
</list> </list>
</value> </value>
</option> </option>

View File

@ -19,6 +19,13 @@ android {
packagingOptions { packagingOptions {
exclude 'META-INF/INDEX.LIST' exclude 'META-INF/INDEX.LIST'
exclude 'META-INF/io.netty.versions.properties' exclude 'META-INF/io.netty.versions.properties'
exclude 'LICENSE-EPL-1.0.txt'
exclude 'LICENSE-EDL-1.0.txt'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
} }
} }
@ -29,5 +36,13 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version" annotationProcessor "androidx.room:room-compiler:$room_version"
implementation 'net.tomp2p:tomp2p-all:5.0-Beta8' implementation('net.tomp2p:tomp2p-all:5.0-Beta8') {
exclude group: 'org.mapdb', module: 'mapdb'
}
implementation 'io.paperdb:paperdb:2.6'
implementation 'org.slf4j:slf4j-log4j12:+'
implementation 'com.github.nolia:Noodle:0.5'
implementation group: 'com.h2database', name: 'h2-mvstore', version: '1.4.197'
//implementation 'org.jboss:jdk-misc:2.Final'
//implementation group: 'jboss', name: 'jboss-serialization', version: '4.2.2.GA'
} }

Binary file not shown.

BIN
app/libs/trove.jar Normal file

Binary file not shown.

View File

@ -10,7 +10,7 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:name=".App"> android:name=".AppHelper">
<activity android:name=".MainActivity"> <activity android:name=".MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View File

@ -7,7 +7,7 @@ import android.content.Context;
* Extended Application class which designed for getting Context from anywhere in the application. * Extended Application class which designed for getting Context from anywhere in the application.
*/ */
public class App extends Application { public class AppHelper extends Application {
private static Application instance; private static Application instance;
@Override @Override

View File

@ -4,34 +4,20 @@ import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import net.tomp2p.connection.RSASignatureFactory;
import net.tomp2p.dht.PeerBuilderDHT; import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT; import net.tomp2p.dht.PeerDHT;
import net.tomp2p.dht.Storage;
import net.tomp2p.dht.StorageLayer;
import net.tomp2p.dht.StorageMemory;
import net.tomp2p.futures.FutureBootstrap; import net.tomp2p.futures.FutureBootstrap;
import net.tomp2p.futures.FutureDiscover; import net.tomp2p.futures.FutureDiscover;
import net.tomp2p.p2p.PeerBuilder; import net.tomp2p.p2p.PeerBuilder;
import net.tomp2p.peers.Number160; 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 net.tomp2p.storage.StorageDisk;
import org.mapdb.DBMaker;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.security.PublicKey;
import java.util.Collection;
import java.util.NavigableMap;
import java.util.UUID; import java.util.UUID;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import io.github.chronosx88.influence.helpers.StorageMVStore;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@ -42,18 +28,20 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
org.apache.log4j.BasicConfigurator.configure();
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
preferences = getPreferences(MODE_PRIVATE); preferences = getSharedPreferences("main_config", MODE_PRIVATE);
if(checkFirstRun()) { if(checkFirstRun()) {
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString("peerID", UUID.randomUUID().toString()); String uuid = UUID.randomUUID().toString();
editor.putString("peerID", uuid);
editor.apply(); editor.apply();
} else {
peerID = Number160.createHash(preferences.getString("peerID", "0"));
} }
peerID = Number160.createHash(preferences.getString("peerID", "0"));
try { try {
peerDHT = new PeerBuilderDHT( peerDHT = new PeerBuilderDHT(
new PeerBuilder(peerID) new PeerBuilder(peerID)
@ -61,17 +49,17 @@ public class MainActivity extends AppCompatActivity {
.behindFirewall(true) .behindFirewall(true)
.start() .start()
) )
.storage(new StorageDisk(peerID, getFilesDir(), new RSASignatureFactory())) .storage(new StorageMVStore(peerID, getFilesDir()))
.start(); .start();
InetAddress address = Inet4Address.getByName("192.168.0.82"); InetAddress address = Inet4Address.getByName("*IP*");
FutureDiscover futureDiscover = peerDHT.peer().discover().inetAddress( address ).ports( 7243 ).start(); FutureDiscover futureDiscover = peerDHT.peer().discover().inetAddress( address ).ports( 7243 ).start();
futureDiscover.awaitUninterruptibly(); futureDiscover.awaitUninterruptibly();
FutureBootstrap futureBootstrap = peerDHT.peer().bootstrap().inetAddress( address ).ports( 7243 ).start(); FutureBootstrap futureBootstrap = peerDHT.peer().bootstrap().inetAddress( address ).ports( 7243 ).start();
futureBootstrap.awaitUninterruptibly(); futureBootstrap.awaitUninterruptibly();
Log.d("", futureBootstrap.failedReason());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
public boolean checkFirstRun() { public boolean checkFirstRun() {
@ -85,9 +73,8 @@ public class MainActivity extends AppCompatActivity {
} }
@Override @Override
protected void onStop() { protected void onDestroy() {
super.onStop(); super.onDestroy();
Log.wtf("MainActivity", "onStop");
peerDHT.shutdown(); peerDHT.shutdown();
} }
} }

View File

@ -0,0 +1,15 @@
package io.github.chronosx88.influence.helpers;
public class JVMShutdownHook extends Thread {
StorageMVStore storage;
public JVMShutdownHook(StorageMVStore storage) {
this.storage = storage;
}
@Override
public void run() {
super.run();
storage.close();
}
}

View File

@ -0,0 +1,45 @@
package io.github.chronosx88.influence.helpers;
import org.jboss.serial.io.JBossObjectInputStream;
import org.jboss.serial.io.JBossObjectOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class Serializer {
public static <T> String serializeObject(T t) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
JBossObjectOutputStream out = new JBossObjectOutputStream(outputStream);
out.writeObject(t);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
return outputStream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public static <T> T deserializeObject(String str) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8));
T obj = null;
try {
JBossObjectInputStream in = new JBossObjectInputStream(inputStream);
obj = (T) in.readObject();
in.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
}

View File

@ -0,0 +1,295 @@
package io.github.chronosx88.influence.helpers;
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 org.h2.mvstore.MVMap;
import org.h2.mvstore.MVStore;
import java.io.File;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
public class StorageMVStore implements Storage {
private MVStore db;
// Core
final private MVMap<String, String> dataMap; // <Number 640, Data>
// Maintenance
final private MVMap<String, String> timeoutMap; // <Number640, Long>
final private MVMap<String, String> timeoutMapRev; // <Long, Set<Number640>>
// Protection
final private MVMap<String, String> protectedDomainMap; // <Number320, PublicKey>
final private MVMap<String, String> protectedEntryMap; // <Number480, PublicKey>
// Responsibility
final private MVMap<String, String> responsibilityMap; // <Number160, Number160>
final private MVMap<String, String> responsibilityMapRev; // <Number160, Set<Number160>>
final private int storageCheckIntervalMillis;
public StorageMVStore(Number160 peerID, File path) {
db = new MVStore.Builder()
.fileName(path.getAbsolutePath() + "/coreDB.db")
.open();
dataMap = db.openMap("dataMap_" + peerID.toString());
timeoutMap = db.openMap("timeoutMap_" + peerID.toString());
timeoutMapRev = db.openMap("timeoutMapRev_" + peerID.toString());
protectedDomainMap = db.openMap("protectedDomainMap_" + peerID.toString());
protectedEntryMap = db.openMap("protectedEntryMap_" + peerID.toString());
responsibilityMap = db.openMap("responsibilityMap_" + peerID.toString());
responsibilityMapRev = db.openMap("responsibilityMapRev_ " + peerID.toString());
storageCheckIntervalMillis = 60 * 1000;
Runtime.getRuntime().addShutdownHook(new JVMShutdownHook(this));
}
@Override
public Data put(Number640 key, Data value) {
Data oldData = Serializer.deserializeObject(dataMap.put(Serializer.serializeObject(key), Serializer.serializeObject(value)));
db.commit();
return oldData;
}
@Override
public Data get(Number640 key) {
return Serializer.deserializeObject(dataMap.get(Serializer.serializeObject(key)));
}
@Override
public boolean contains(Number640 key) {
return dataMap.containsKey(Serializer.serializeObject(key));
}
@Override
public int contains(Number640 from, Number640 to) {
TreeMap<Number640, Data> tmp = new TreeMap<>();
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
tmp.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
}
return tmp.subMap(from, true, to, true).size();
}
@Override
public Data remove(Number640 key, boolean returnData) {
Data retVal = Serializer.deserializeObject(dataMap.remove(Serializer.serializeObject(key)));
db.commit();
return retVal;
}
@Override
public NavigableMap<Number640, Data> remove(Number640 from, Number640 to) {
TreeMap<Number640, Data> tmp = new TreeMap<>();
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
tmp.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
}
NavigableMap<Number640, Data> tmpSubMap = tmp.subMap(from, true, to, true);
for (Map.Entry<Number640, Data> entry : tmpSubMap.entrySet()) {
dataMap.remove(Serializer.serializeObject(entry.getKey()));
}
db.commit();
return tmpSubMap;
}
@Override
public NavigableMap<Number640, Data> subMap(Number640 from, Number640 to, int limit, boolean ascending) {
TreeMap<Number640, Data> tmpDataMap = new TreeMap<>();
for (Map.Entry<String, String> entry: dataMap.entrySet()) {
tmpDataMap.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
}
NavigableMap<Number640, Data> tmp = tmpDataMap.subMap(from, true, to, true);
final NavigableMap<Number640, Data> retVal = new TreeMap<>();
if (limit < 0) {
for(final Map.Entry<Number640, Data> entry:(ascending ? tmp : tmp.descendingMap()).entrySet()) {
retVal.put(entry.getKey(), entry.getValue());
}
} else {
limit = Math.min(limit, tmp.size());
Iterator<Map.Entry<Number640, Data>> iterator = ascending ? tmp.entrySet().iterator() : tmp
.descendingMap().entrySet().iterator();
for (int i = 0; iterator.hasNext() && i < limit; i++) {
Map.Entry<Number640, Data> entry = iterator.next();
retVal.put(entry.getKey(), entry.getValue());
}
}
return retVal;
}
@Override
public NavigableMap<Number640, Data> map() {
final NavigableMap<Number640, Data> retVal = new TreeMap<>();
for(final Map.Entry<String, String> entry:dataMap.entrySet()) {
retVal.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
}
return retVal;
}
@Override
public void close() {
db.close();
}
@Override
public void addTimeout(Number640 key, long expiration) {
Long oldExpiration = Serializer.deserializeObject(timeoutMap.put(Serializer.serializeObject(key), Serializer.serializeObject(expiration)));
putIfAbsent2(expiration, key);
if (oldExpiration == null) {
return;
}
removeRevTimeout(key, oldExpiration);
db.commit();
}
private void putIfAbsent2(long expiration, Number640 key) {
Set<Number640> timeouts = Serializer.deserializeObject(timeoutMapRev.get(Serializer.serializeObject(expiration)));
if(timeouts == null) {
timeouts = Collections.newSetFromMap(new ConcurrentHashMap<Number640, Boolean>());
}
timeouts.add(key);
timeoutMapRev.put(Serializer.serializeObject(expiration), Serializer.serializeObject(timeouts));
}
private void removeRevTimeout(Number640 key, Long expiration) {
Set<Number640> tmp = Serializer.deserializeObject(timeoutMapRev.get(Serializer.serializeObject(expiration)));
if (tmp != null) {
tmp.remove(key);
if (tmp.isEmpty()) {
timeoutMapRev.remove(expiration);
} else {
timeoutMapRev.put(Serializer.serializeObject(expiration), Serializer.serializeObject(tmp));
}
}
}
@Override
public void removeTimeout(Number640 key) {
Long expiration = Serializer.deserializeObject(timeoutMap.remove(Serializer.serializeObject(key)));
if (expiration == null) {
return;
}
removeRevTimeout(key, expiration);
db.commit();
}
@Override
public Collection<Number640> subMapTimeout(long to) {
TreeMap<Long, Set<Number640>> tmpTimeoutMapRev = new TreeMap<>();
for (Map.Entry<String, String> entry: timeoutMapRev.entrySet()) {
tmpTimeoutMapRev.put(Serializer.deserializeObject(entry.getKey()), Serializer.deserializeObject(entry.getValue()));
}
SortedMap<Long, Set<Number640>> tmp = tmpTimeoutMapRev.subMap(0L, to);
Collection<Number640> toRemove = new ArrayList<>();
for (Set<Number640> set : tmp.values()) {
toRemove.addAll(set);
}
return toRemove;
}
@Override
public int storageCheckIntervalMillis() {
return this.storageCheckIntervalMillis;
}
@Override
public boolean protectDomain(Number320 key, PublicKey publicKey) {
protectedDomainMap.put(Serializer.serializeObject(key), Serializer.serializeObject(publicKey));
return true;
}
@Override
public boolean isDomainProtectedByOthers(Number320 key, PublicKey publicKey) {
PublicKey other = Serializer.deserializeObject(protectedDomainMap.get(Serializer.serializeObject(key)));
if (other == null) {
return false;
}
return !other.equals(publicKey);
}
@Override
public boolean protectEntry(Number480 key, PublicKey publicKey) {
protectedEntryMap.put(Serializer.serializeObject(key), Serializer.serializeObject(publicKey));
return true;
}
@Override
public boolean isEntryProtectedByOthers(Number480 key, PublicKey publicKey) {
PublicKey other = Serializer.deserializeObject(protectedEntryMap.get(Serializer.serializeObject(key)));
if (other == null) {
return false;
}
return !other.equals(publicKey);
}
@Override
public Number160 findPeerIDsForResponsibleContent(Number160 locationKey) {
return Serializer.deserializeObject(responsibilityMap.get(Serializer.serializeObject(locationKey)));
}
@Override
public Collection<Number160> findContentForResponsiblePeerID(Number160 peerID) {
return Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerID)));
}
@Override
public boolean updateResponsibilities(Number160 locationKey, Number160 peerId) {
final Number160 oldPeerID = Serializer.deserializeObject(responsibilityMap.put(Serializer.serializeObject(locationKey), Serializer.serializeObject(peerId)));
final boolean hasChanged;
if(oldPeerID != null) {
if(oldPeerID.equals(peerId)) {
hasChanged = false;
} else {
removeRevResponsibility(oldPeerID, locationKey);
hasChanged = true;
}
} else {
hasChanged = true;
}
Set<Number160> contentIDs = Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerId)));
if(contentIDs == null) {
contentIDs = new HashSet<Number160>();
}
contentIDs.add(locationKey);
responsibilityMapRev.put(Serializer.serializeObject(peerId), Serializer.serializeObject(contentIDs));
db.commit();
return hasChanged;
}
@Override
public void removeResponsibility(Number160 locationKey) {
}
private void removeRevResponsibility(Number160 peerId, Number160 locationKey) {
Set<Number160> contentIDs = Serializer.deserializeObject(responsibilityMapRev.get(Serializer.serializeObject(peerId)));
if (contentIDs != null) {
contentIDs.remove(locationKey);
if (contentIDs.isEmpty()) {
responsibilityMapRev.remove(peerId);
} else {
responsibilityMapRev.put(Serializer.serializeObject(peerId), Serializer.serializeObject(contentIDs));
}
}
}
}

View File

@ -23,6 +23,8 @@ allprojects {
maven { maven {
url "http://tomp2p.net/dev/mvn/" url "http://tomp2p.net/dev/mvn/"
} }
mavenCentral()
maven { url 'https://jitpack.io' }
} }
} }

View File

@ -14,5 +14,6 @@ org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableD8=true