Use the nio network listener.

This commit is contained in:
Christian Basler 2016-09-12 11:00:00 +02:00
parent dd9539aa3f
commit af2bfc796b
13 changed files with 382 additions and 156 deletions

View File

@ -11,7 +11,7 @@ if (project.hasProperty("project.configs")
android { android {
compileSdkVersion 24 compileSdkVersion 24
buildToolsVersion "24.0.1" buildToolsVersion "24.0.2"
defaultConfig { defaultConfig {
applicationId "ch.dissem.apps." + appName.toLowerCase() applicationId "ch.dissem.apps." + appName.toLowerCase()
@ -29,12 +29,12 @@ android {
} }
} }
ext.jabitVersion = 'develop-SNAPSHOT' ext.jabitVersion = 'feature-nio-SNAPSHOT'
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:24.1.1' compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:support-v4:24.1.1' compile 'com.android.support:support-v4:24.2.0'
compile 'com.android.support:design:24.1.1' compile 'com.android.support:design:24.2.0'
compile "ch.dissem.jabit:jabit-core:$jabitVersion" compile "ch.dissem.jabit:jabit-core:$jabitVersion"
compile "ch.dissem.jabit:jabit-networking:$jabitVersion" compile "ch.dissem.jabit:jabit-networking:$jabitVersion"

View File

@ -0,0 +1,9 @@
CREATE TABLE Node (
stream BIGINT NOT NULL,
address BINARY(32) NOT NULL,
port INT NOT NULL,
services BIGINT NOT NULL,
time BIGINT NOT NULL,
PRIMARY KEY (stream, address, port)
);
CREATE INDEX idx_time on Node(time);

View File

@ -27,6 +27,7 @@ public class ComposeMessageActivity extends AppCompatActivity {
public static final String EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER"; public static final String EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER";
public static final String EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT"; public static final String EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT";
public static final String EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT"; public static final String EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT";
public static final String EXTRA_CONTENT = "ch.dissem.abit.Message.CONTENT";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {

View File

@ -18,6 +18,7 @@ package ch.dissem.apps.abit;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.text.Selection;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -34,6 +35,7 @@ import ch.dissem.apps.abit.adapter.ContactAdapter;
import ch.dissem.apps.abit.service.Singleton; import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_CONTENT;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT;
@ -45,6 +47,7 @@ public class ComposeMessageFragment extends Fragment {
private BitmessageAddress identity; private BitmessageAddress identity;
private BitmessageAddress recipient; private BitmessageAddress recipient;
private String subject; private String subject;
private String content;
private AutoCompleteTextView recipientInput; private AutoCompleteTextView recipientInput;
private EditText subjectInput; private EditText subjectInput;
private EditText bodyInput; private EditText bodyInput;
@ -71,6 +74,9 @@ public class ComposeMessageFragment extends Fragment {
if (getArguments().containsKey(EXTRA_SUBJECT)) { if (getArguments().containsKey(EXTRA_SUBJECT)) {
subject = getArguments().getString(EXTRA_SUBJECT); subject = getArguments().getString(EXTRA_SUBJECT);
} }
if (getArguments().containsKey(EXTRA_CONTENT)) {
content = getArguments().getString(EXTRA_CONTENT);
}
} else { } else {
throw new RuntimeException("No identity set for ComposeMessageFragment"); throw new RuntimeException("No identity set for ComposeMessageFragment");
} }
@ -106,6 +112,16 @@ public class ComposeMessageFragment extends Fragment {
subjectInput = (EditText) rootView.findViewById(R.id.subject); subjectInput = (EditText) rootView.findViewById(R.id.subject);
subjectInput.setText(subject); subjectInput.setText(subject);
bodyInput = (EditText) rootView.findViewById(R.id.body); bodyInput = (EditText) rootView.findViewById(R.id.body);
bodyInput.setText(content);
if (recipient == null) {
recipientInput.requestFocus();
} else if (subject == null || subject.isEmpty()) {
subjectInput.requestFocus();
} else {
bodyInput.requestFocus();
bodyInput.setSelection(0);
}
return rootView; return rootView;
} }

View File

@ -44,6 +44,7 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import static android.text.util.Linkify.WEB_URLS; import static android.text.util.Linkify.WEB_URLS;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_CONTENT;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT;
@ -169,6 +170,9 @@ public class MessageDetailFragment extends Fragment {
prefix = "RE: "; prefix = "RE: ";
} }
replyIntent.putExtra(EXTRA_SUBJECT, prefix + item.getSubject()); replyIntent.putExtra(EXTRA_SUBJECT, prefix + item.getSubject());
replyIntent.putExtra(EXTRA_CONTENT,
"\n\n------------------------------------------------------\n"
+ item.getText());
startActivity(replyIntent); startActivity(replyIntent);
return true; return true;
case R.id.delete: case R.id.delete:

View File

@ -133,8 +133,7 @@ public class AndroidAddressRepository implements AddressRepository {
where, where,
null, null, null, null null, null, null, null
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
BitmessageAddress address; BitmessageAddress address;
byte[] privateKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PRIVATE_KEY)); byte[] privateKeyBytes = c.getBlob(c.getColumnIndex(COLUMN_PRIVATE_KEY));
@ -161,7 +160,6 @@ public class AndroidAddressRepository implements AddressRepository {
address.setSubscribed(c.getInt(c.getColumnIndex(COLUMN_SUBSCRIBED)) == 1); address.setSubscribed(c.getInt(c.getColumnIndex(COLUMN_SUBSCRIBED)) == 1);
result.add(address); result.add(address);
c.moveToNext();
} }
} catch (IOException e) { } catch (IOException e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
@ -184,8 +182,10 @@ public class AndroidAddressRepository implements AddressRepository {
private boolean exists(BitmessageAddress address) { private boolean exists(BitmessageAddress address) {
SQLiteDatabase db = sql.getReadableDatabase(); SQLiteDatabase db = sql.getReadableDatabase();
try (Cursor cursor = db.rawQuery("SELECT COUNT(*) FROM Address WHERE address='" + address try (Cursor cursor = db.rawQuery(
.getAddress() + "'", null)) { "SELECT COUNT(*) FROM Address WHERE address=?",
new String[]{address.getAddress()}
)) {
cursor.moveToFirst(); cursor.moveToFirst();
return cursor.getInt(0) > 0; return cursor.getInt(0) > 0;
} }
@ -210,8 +210,8 @@ public class AndroidAddressRepository implements AddressRepository {
values.put(COLUMN_CHAN, address.isChan()); values.put(COLUMN_CHAN, address.isChan());
values.put(COLUMN_SUBSCRIBED, address.isSubscribed()); values.put(COLUMN_SUBSCRIBED, address.isSubscribed());
int update = db.update(TABLE_NAME, values, "address = '" + address.getAddress() + int update = db.update(TABLE_NAME, values, "address=?",
"'", null); new String[]{address.getAddress()});
if (update < 0) { if (update < 0) {
LOG.error("Could not update address " + address); LOG.error("Could not update address " + address);
} }

View File

@ -25,7 +25,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -42,6 +41,7 @@ import ch.dissem.bitmessage.utils.Encode;
import static ch.dissem.apps.abit.repository.SqlHelper.join; import static ch.dissem.apps.abit.repository.SqlHelper.join;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static ch.dissem.bitmessage.utils.UnixTime.now; import static ch.dissem.bitmessage.utils.UnixTime.now;
import static java.lang.String.valueOf;
/** /**
* {@link Inventory} implementation using the Android SQL API. * {@link Inventory} implementation using the Android SQL API.
@ -97,12 +97,10 @@ public class AndroidInventory implements Inventory {
"stream = " + stream, "stream = " + stream,
null, null, null, null null, null, null, null
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH)); byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_HASH));
long expires = c.getLong(c.getColumnIndex(COLUMN_EXPIRES)); long expires = c.getLong(c.getColumnIndex(COLUMN_EXPIRES));
result.put(new InventoryVector(blob), expires); result.put(new InventoryVector(blob), expires);
c.moveToNext();
} }
} }
LOG.info("Stream #" + stream + " inventory size: " + result.size()); LOG.info("Stream #" + stream + " inventory size: " + result.size());
@ -136,8 +134,7 @@ public class AndroidInventory implements Inventory {
"hash = X'" + vector + "'", "hash = X'" + vector + "'",
null, null, null, null null, null, null, null
)) { )) {
c.moveToFirst(); if (!c.moveToFirst()) {
if (c.isAfterLast()) {
LOG.info("Object requested that we don't have. IV: " + vector); LOG.info("Object requested that we don't have. IV: " + vector);
return null; return null;
} }
@ -174,13 +171,11 @@ public class AndroidInventory implements Inventory {
where.toString(), where.toString(),
null, null, null, null null, null, null, null
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION)); int objectVersion = c.getInt(c.getColumnIndex(COLUMN_VERSION));
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA)); byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
result.add(Factory.getObjectMessage(objectVersion, new ByteArrayInputStream(blob), result.add(Factory.getObjectMessage(objectVersion, new ByteArrayInputStream(blob),
blob.length)); blob.length));
c.moveToNext();
} }
} }
return result; return result;
@ -211,8 +206,6 @@ public class AndroidInventory implements Inventory {
getCache(object.getStream()).put(iv, object.getExpiresTime()); getCache(object.getStream()).put(iv, object.getExpiresTime());
} catch (SQLiteConstraintException e) { } catch (SQLiteConstraintException e) {
LOG.trace(e.getMessage(), e); LOG.trace(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} }
} }
@ -225,7 +218,7 @@ public class AndroidInventory implements Inventory {
public void cleanup() { public void cleanup() {
long fiveMinutesAgo = now() - 5 * MINUTE; long fiveMinutesAgo = now() - 5 * MINUTE;
SQLiteDatabase db = sql.getWritableDatabase(); SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, "expires < " + fiveMinutesAgo, null); db.delete(TABLE_NAME, "expires < ?", new String[]{valueOf(fiveMinutesAgo)});
for (Map<InventoryVector, Long> c : cache.values()) { for (Map<InventoryVector, Long> c : cache.values()) {
Iterator<Map.Entry<InventoryVector, Long>> iterator = c.entrySet().iterator(); Iterator<Map.Entry<InventoryVector, Long>> iterator = c.entrySet().iterator();

View File

@ -41,6 +41,8 @@ import ch.dissem.bitmessage.ports.AbstractMessageRepository;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import static java.lang.String.valueOf;
/** /**
* {@link MessageRepository} implementation using the Android SQL API. * {@link MessageRepository} implementation using the Android SQL API.
*/ */
@ -100,10 +102,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
null, null, null, null, null, null,
LBL_COLUMN_ORDER LBL_COLUMN_ORDER
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
result.add(getLabel(c)); result.add(getLabel(c));
c.moveToNext();
} }
} }
return result; return result;
@ -149,17 +149,25 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
@Override @Override
public int countUnread(Label label) { public int countUnread(Label label) {
String[] args;
String where; String where;
if (label != null) { if (label != null) {
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=?) AND ";
+ ") AND "; args = new String[]{
label.getId().toString(),
Label.Type.UNREAD.name()
};
} else { } else {
where = ""; where = "";
args = new String[]{
Label.Type.UNREAD.name()
};
} }
SQLiteDatabase db = sql.getReadableDatabase(); SQLiteDatabase db = sql.getReadableDatabase();
return (int) DatabaseUtils.queryNumEntries(db, TABLE_NAME, return (int) DatabaseUtils.queryNumEntries(db, TABLE_NAME,
where + "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" + where + "id IN (SELECT message_id FROM Message_Label WHERE label_id IN (" +
"SELECT id FROM Label WHERE type = '" + Label.Type.UNREAD.name() + "'))" "SELECT id FROM Label WHERE type=?))",
args
); );
} }
@ -191,8 +199,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
null, null, null, null, null, null,
COLUMN_RECEIVED + " DESC" COLUMN_RECEIVED + " DESC"
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
byte[] iv = c.getBlob(c.getColumnIndex(COLUMN_IV)); byte[] iv = c.getBlob(c.getColumnIndex(COLUMN_IV));
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA)); byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex
@ -219,7 +226,6 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
} }
builder.labels(findLabels(id)); builder.labels(findLabels(id));
result.add(builder.build()); result.add(builder.build());
c.moveToNext();
} }
} catch (IOException e) { } catch (IOException e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
@ -257,7 +263,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
} }
// remove existing labels // remove existing labels
db.delete(JOIN_TABLE_NAME, "message_id=" + message.getId(), null); db.delete(JOIN_TABLE_NAME, "message_id=?", new String[]{valueOf(message.getId())});
// save labels // save labels
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
@ -284,10 +290,14 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
values.put(COLUMN_SENDER, message.getFrom().getAddress()); values.put(COLUMN_SENDER, message.getFrom().getAddress());
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress()); values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
values.put(COLUMN_DATA, Encode.bytes(message)); values.put(COLUMN_DATA, Encode.bytes(message));
values.put(COLUMN_ACK_DATA, message.getAckData());
values.put(COLUMN_SENT, message.getSent()); values.put(COLUMN_SENT, message.getSent());
values.put(COLUMN_RECEIVED, message.getReceived()); values.put(COLUMN_RECEIVED, message.getReceived());
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name()); values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
values.put(COLUMN_INITIAL_HASH, message.getInitialHash()); values.put(COLUMN_INITIAL_HASH, message.getInitialHash());
values.put(COLUMN_TTL, message.getTTL());
values.put(COLUMN_RETRIES, message.getRetries());
values.put(COLUMN_NEXT_TRY, message.getNextTry());
long id = db.insertOrThrow(TABLE_NAME, null, values); long id = db.insertOrThrow(TABLE_NAME, null, values);
message.setId(id); message.setId(id);
} }
@ -300,10 +310,14 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
values.put(COLUMN_SENDER, message.getFrom().getAddress()); values.put(COLUMN_SENDER, message.getFrom().getAddress());
values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress()); values.put(COLUMN_RECIPIENT, message.getTo() == null ? null : message.getTo().getAddress());
values.put(COLUMN_DATA, Encode.bytes(message)); values.put(COLUMN_DATA, Encode.bytes(message));
values.put(COLUMN_ACK_DATA, message.getAckData());
values.put(COLUMN_SENT, message.getSent()); values.put(COLUMN_SENT, message.getSent());
values.put(COLUMN_RECEIVED, message.getReceived()); values.put(COLUMN_RECEIVED, message.getReceived());
values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name()); values.put(COLUMN_STATUS, message.getStatus() == null ? null : message.getStatus().name());
values.put(COLUMN_INITIAL_HASH, message.getInitialHash()); values.put(COLUMN_INITIAL_HASH, message.getInitialHash());
values.put(COLUMN_TTL, message.getTTL());
values.put(COLUMN_RETRIES, message.getRetries());
values.put(COLUMN_NEXT_TRY, message.getNextTry());
db.update(TABLE_NAME, values, "id = " + message.getId(), null); db.update(TABLE_NAME, values, "id = " + message.getId(), null);
} }

View File

@ -0,0 +1,187 @@
package ch.dissem.apps.abit.repository;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDoneException;
import android.database.sqlite.SQLiteStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
import ch.dissem.bitmessage.exception.ApplicationException;
import ch.dissem.bitmessage.ports.NodeRegistry;
import ch.dissem.bitmessage.utils.Collections;
import ch.dissem.bitmessage.utils.SqlStrings;
import static ch.dissem.bitmessage.ports.NodeRegistryHelper.loadStableNodes;
import static ch.dissem.bitmessage.utils.Strings.hex;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static ch.dissem.bitmessage.utils.UnixTime.now;
import static java.lang.String.valueOf;
/**
* @author Christian Basler
*/
public class AndroidNodeRegistry implements NodeRegistry {
private static final Logger LOG = LoggerFactory.getLogger(AndroidInventory.class);
private static final String TABLE_NAME = "Node";
private static final String COLUMN_STREAM = "stream";
private static final String COLUMN_ADDRESS = "address";
private static final String COLUMN_PORT = "port";
private static final String COLUMN_SERVICES = "services";
private static final String COLUMN_TIME = "time";
private final ThreadLocal<SQLiteStatement> loadExistingStatement = new ThreadLocal<>();
private final SqlHelper sql;
private Map<Long, Set<NetworkAddress>> stableNodes;
public AndroidNodeRegistry(SqlHelper sql) {
this.sql = sql;
cleanUp();
}
private void cleanUp() {
SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))});
}
private Long loadExistingTime(NetworkAddress node) {
SQLiteStatement statement = loadExistingStatement.get();
if (statement == null) {
statement = sql.getWritableDatabase().compileStatement(
"SELECT " + COLUMN_TIME +
" FROM " + TABLE_NAME +
" WHERE stream=? AND address=? AND port=?"
);
loadExistingStatement.set(statement);
}
statement.bindLong(1, node.getStream());
statement.bindBlob(2, node.getIPv6());
statement.bindLong(3, node.getPort());
try {
return statement.simpleQueryForLong();
} catch (SQLiteDoneException e) {
return null;
}
}
@Override
public List<NetworkAddress> getKnownAddresses(int limit, long... streams) {
String[] projection = {
COLUMN_STREAM,
COLUMN_ADDRESS,
COLUMN_PORT,
COLUMN_SERVICES,
COLUMN_TIME
};
List<NetworkAddress> result = new LinkedList<>();
SQLiteDatabase db = sql.getReadableDatabase();
try (Cursor c = db.query(
TABLE_NAME, projection,
"stream IN (?)",
new String[]{SqlStrings.join(streams).toString()},
null, null,
"time DESC",
valueOf(limit)
)) {
while (c.moveToNext()) {
result.add(
new NetworkAddress.Builder()
.stream(c.getLong(c.getColumnIndex(COLUMN_STREAM)))
.ipv6(c.getBlob(c.getColumnIndex(COLUMN_ADDRESS)))
.port(c.getInt(c.getColumnIndex(COLUMN_PORT)))
.services(c.getLong(c.getColumnIndex(COLUMN_SERVICES)))
.time(c.getLong(c.getColumnIndex(COLUMN_TIME)))
.build()
);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
throw new ApplicationException(e);
}
if (result.isEmpty()) {
synchronized (this) {
if (stableNodes == null) {
stableNodes = loadStableNodes();
}
}
for (long stream : streams) {
Set<NetworkAddress> nodes = stableNodes.get(stream);
if (nodes != null && !nodes.isEmpty()) {
result.add(Collections.selectRandom(nodes));
}
}
}
return result;
}
@Override
public void offerAddresses(List<NetworkAddress> nodes) {
SQLiteDatabase db = sql.getWritableDatabase();
db.beginTransaction();
try {
cleanUp();
for (NetworkAddress node : nodes) {
if (node.getTime() < now(+5 * MINUTE) && node.getTime() > now(-28 * DAY)) {
synchronized (this) {
Long existing = loadExistingTime(node);
if (existing == null) {
insert(node);
} else if (node.getTime() > existing) {
update(node);
}
}
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void insert(NetworkAddress node) {
try {
SQLiteDatabase db = sql.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(COLUMN_STREAM, node.getStream());
values.put(COLUMN_ADDRESS, node.getIPv6());
values.put(COLUMN_PORT, node.getPort());
values.put(COLUMN_SERVICES, node.getServices());
values.put(COLUMN_TIME, node.getTime());
db.insertOrThrow(TABLE_NAME, null, values);
} catch (SQLiteConstraintException e) {
LOG.trace(e.getMessage(), e);
}
}
private void update(NetworkAddress node) {
try {
SQLiteDatabase db = sql.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(COLUMN_SERVICES, node.getServices());
values.put(COLUMN_TIME, node.getTime());
db.update(TABLE_NAME, values,
"stream=" + node.getStream() + " AND address=X'" + hex(node.getIPv6()) + "' AND " +
"port=" + node.getPort(),
null);
} catch (SQLiteConstraintException e) {
LOG.trace(e.getMessage(), e);
}
}
}

View File

@ -25,7 +25,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -37,6 +36,7 @@ import ch.dissem.bitmessage.utils.Encode;
import ch.dissem.bitmessage.utils.Strings; import ch.dissem.bitmessage.utils.Strings;
import static ch.dissem.bitmessage.utils.Singleton.cryptography; import static ch.dissem.bitmessage.utils.Singleton.cryptography;
import static ch.dissem.bitmessage.utils.Strings.hex;
/** /**
* @author Christian Basler * @author Christian Basler
@ -82,11 +82,10 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte
SQLiteDatabase db = sql.getReadableDatabase(); SQLiteDatabase db = sql.getReadableDatabase();
try (Cursor c = db.query( try (Cursor c = db.query(
TABLE_NAME, projection, TABLE_NAME, projection,
"initial_hash = X'" + Strings.hex(initialHash) + "'", "initial_hash=X'" + hex(initialHash) + "'",
null, null, null, null null, null, null, null
)) { )) {
c.moveToFirst(); if (c.moveToFirst()) {
if (!c.isAfterLast()) {
int version = c.getInt(c.getColumnIndex(COLUMN_VERSION)); int version = c.getInt(c.getColumnIndex(COLUMN_VERSION));
byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA)); byte[] blob = c.getBlob(c.getColumnIndex(COLUMN_DATA));
if (c.isNull(c.getColumnIndex(COLUMN_MESSAGE_ID))) { if (c.isNull(c.getColumnIndex(COLUMN_MESSAGE_ID))) {
@ -110,7 +109,7 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte
} }
} }
throw new RuntimeException("Object requested that we don't have. Initial hash: " + throw new RuntimeException("Object requested that we don't have. Initial hash: " +
Strings.hex(initialHash)); hex(initialHash));
} }
@Override @Override
@ -127,11 +126,9 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte
TABLE_NAME, projection, TABLE_NAME, projection,
null, null, null, null, null null, null, null, null, null
)) { )) {
c.moveToFirst(); while (c.moveToNext()) {
while (!c.isAfterLast()) {
byte[] initialHash = c.getBlob(c.getColumnIndex(COLUMN_INITIAL_HASH)); byte[] initialHash = c.getBlob(c.getColumnIndex(COLUMN_INITIAL_HASH));
result.add(initialHash); result.add(initialHash);
c.moveToNext();
} }
} }
return result; return result;
@ -156,8 +153,6 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte
db.insertOrThrow(TABLE_NAME, null, values); db.insertOrThrow(TABLE_NAME, null, values);
} catch (SQLiteConstraintException e) { } catch (SQLiteConstraintException e) {
LOG.trace(e.getMessage(), e); LOG.trace(e.getMessage(), e);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
} }
} }
@ -169,8 +164,10 @@ public class AndroidProofOfWorkRepository implements ProofOfWorkRepository, Inte
@Override @Override
public void removeObject(byte[] initialHash) { public void removeObject(byte[] initialHash) {
SQLiteDatabase db = sql.getWritableDatabase(); SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, db.delete(
"initial_hash = X'" + Strings.hex(initialHash) + "'", TABLE_NAME,
null); "initial_hash=X'" + hex(initialHash) + "'",
null
);
} }
} }

View File

@ -19,6 +19,7 @@ package ch.dissem.apps.abit.repository;
import android.content.Context; import android.content.Context;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import ch.dissem.apps.abit.util.Assets; import ch.dissem.apps.abit.util.Assets;
/** /**
@ -26,7 +27,7 @@ import ch.dissem.apps.abit.util.Assets;
*/ */
public class SqlHelper extends SQLiteOpenHelper { public class SqlHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version. // If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 4; public static final int DATABASE_VERSION = 5;
public static final String DATABASE_NAME = "jabit.db"; public static final String DATABASE_NAME = "jabit.db";
protected final Context ctx; protected final Context ctx;
@ -38,7 +39,7 @@ public class SqlHelper extends SQLiteOpenHelper {
@Override @Override
public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, 2); onUpgrade(db, 0, DATABASE_VERSION);
} }
@Override @Override
@ -56,8 +57,11 @@ public class SqlHelper extends SQLiteOpenHelper {
case 3: case 3:
executeMigration(db, "V3.1__Update_table_POW"); executeMigration(db, "V3.1__Update_table_POW");
executeMigration(db, "V3.2__Update_table_message"); executeMigration(db, "V3.2__Update_table_message");
case 4:
executeMigration(db, "V3.3__Create_table_node");
default: default:
// Nothing to do. Let's assume we won't upgrade from a version that's newer than DATABASE_VERSION. // Nothing to do. Let's assume we won't upgrade from a version that's newer than
// DATABASE_VERSION.
} }
} }

View File

@ -28,16 +28,17 @@ import ch.dissem.apps.abit.pow.ServerPowEngine;
import ch.dissem.apps.abit.repository.AndroidAddressRepository; import ch.dissem.apps.abit.repository.AndroidAddressRepository;
import ch.dissem.apps.abit.repository.AndroidInventory; import ch.dissem.apps.abit.repository.AndroidInventory;
import ch.dissem.apps.abit.repository.AndroidMessageRepository; import ch.dissem.apps.abit.repository.AndroidMessageRepository;
import ch.dissem.apps.abit.repository.AndroidNodeRegistry;
import ch.dissem.apps.abit.repository.AndroidProofOfWorkRepository; import ch.dissem.apps.abit.repository.AndroidProofOfWorkRepository;
import ch.dissem.apps.abit.repository.SqlHelper; import ch.dissem.apps.abit.repository.SqlHelper;
import ch.dissem.apps.abit.util.Constants; import ch.dissem.apps.abit.util.Constants;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.networking.DefaultNetworkHandler; import ch.dissem.bitmessage.networking.nio.NioNetworkHandler;
import ch.dissem.bitmessage.ports.AddressRepository; import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MemoryNodeRegistry;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.ports.ProofOfWorkRepository; import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
import ch.dissem.bitmessage.utils.TTL;
import static ch.dissem.bitmessage.utils.UnixTime.DAY; import static ch.dissem.bitmessage.utils.UnixTime.DAY;
@ -58,6 +59,7 @@ public class Singleton {
final Context ctx = context.getApplicationContext(); final Context ctx = context.getApplicationContext();
SqlHelper sqlHelper = new SqlHelper(ctx); SqlHelper sqlHelper = new SqlHelper(ctx);
powRepo = new AndroidProofOfWorkRepository(sqlHelper); powRepo = new AndroidProofOfWorkRepository(sqlHelper);
TTL.pubkey(2 * DAY);
bitmessageContext = new BitmessageContext.Builder() bitmessageContext = new BitmessageContext.Builder()
.proofOfWorkEngine(new SwitchingProofOfWorkEngine( .proofOfWorkEngine(new SwitchingProofOfWorkEngine(
ctx, Constants.PREFERENCE_SERVER_POW, ctx, Constants.PREFERENCE_SERVER_POW,
@ -65,15 +67,14 @@ public class Singleton {
new ServicePowEngine(ctx) new ServicePowEngine(ctx)
)) ))
.cryptography(new AndroidCryptography()) .cryptography(new AndroidCryptography())
.nodeRegistry(new MemoryNodeRegistry()) .nodeRegistry(new AndroidNodeRegistry(sqlHelper))
.inventory(new AndroidInventory(sqlHelper)) .inventory(new AndroidInventory(sqlHelper))
.addressRepo(new AndroidAddressRepository(sqlHelper)) .addressRepo(new AndroidAddressRepository(sqlHelper))
.messageRepo(new AndroidMessageRepository(sqlHelper, ctx)) .messageRepo(new AndroidMessageRepository(sqlHelper, ctx))
.powRepo(powRepo) .powRepo(powRepo)
.networkHandler(new DefaultNetworkHandler()) .networkHandler(new NioNetworkHandler())
.listener(getMessageListener(ctx)) .listener(getMessageListener(ctx))
.doNotSendPubkeyOnIdentityCreation() .doNotSendPubkeyOnIdentityCreation()
.pubkeyTTL(2 * DAY)
.build(); .build();
} }
} }

View File

@ -9,7 +9,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.1.2' classpath 'com.android.tools.build:gradle:2.1.3'
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files