Merge branch 'message-status-cleanup' into 'master'

Database efficiency improvements

Some tweaks to the DB schema to improve efficiency:

* Only keep status rows for messages that are visible - this saves space and avoids the need to join the groupVisibilities table when selecting messages to offer or send
* Use adjacent columns for the composite primary key on the settings table

This MR depends on !101.

See merge request !102
This commit is contained in:
Torsten Grote
2016-02-17 19:49:48 +00:00
15 changed files with 280 additions and 211 deletions

View File

@@ -125,7 +125,7 @@ public class ContactListFragment extends BaseEventFragment {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
List<ContactListItem> contacts = List<ContactListItem> contacts =
new ArrayList<ContactListItem>(); new ArrayList<ContactListItem>();
for (Contact c : contactManager.getContacts()) { for (Contact c : contactManager.getActiveContacts()) {
try { try {
ContactId id = c.getId(); ContactId id = c.getId();
GroupId groupId = GroupId groupId =

View File

@@ -138,7 +138,7 @@ SelectContactsDialog.Listener {
public void run() { public void run() {
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
contacts = contactManager.getContacts(); contacts = contactManager.getActiveContacts();
selected = forumSharingManager.getSharedWith(groupId); selected = forumSharingManager.getSharedWith(groupId);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))

View File

@@ -8,11 +8,14 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final Author author; private final Author author;
private final AuthorId localAuthorId; private final AuthorId localAuthorId;
private final boolean active;
public Contact(ContactId id, Author author, AuthorId localAuthorId) { public Contact(ContactId id, Author author, AuthorId localAuthorId,
boolean active) {
this.id = id; this.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId; this.localAuthorId = localAuthorId;
this.active = active;
} }
public ContactId getId() { public ContactId getId() {
@@ -27,6 +30,10 @@ public class Contact {
return localAuthorId; return localAuthorId;
} }
public boolean isActive() {
return active;
}
@Override @Override
public int hashCode() { public int hashCode() {
return id.hashCode(); return id.hashCode();

View File

@@ -19,17 +19,21 @@ public interface ContactManager {
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Author remote, AuthorId local) throws DbException; ContactId addContact(Author remote, AuthorId local, boolean active)
throws DbException;
/** Returns the contact with the given ID. */ /** Returns the contact with the given ID. */
Contact getContact(ContactId c) throws DbException; Contact getContact(ContactId c) throws DbException;
/** Returns all contacts. */ /** Returns all active contacts. */
Collection<Contact> getContacts() throws DbException; Collection<Contact> getActiveContacts() throws DbException;
/** Removes a contact and all associated state. */ /** Removes a contact and all associated state. */
void removeContact(ContactId c) throws DbException; void removeContact(ContactId c) throws DbException;
/** Marks a contact as active or inactive. */
void setContactActive(ContactId c, boolean active) throws DbException;
interface AddContactHook { interface AddContactHook {
void addingContact(Transaction txn, Contact c) throws DbException; void addingContact(Transaction txn, Contact c) throws DbException;
} }

View File

@@ -59,8 +59,8 @@ public interface DatabaseComponent {
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local) ContactId addContact(Transaction txn, Author remote, AuthorId local,
throws DbException; boolean active) throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -317,6 +317,12 @@ public interface DatabaseComponent {
*/ */
void removeTransport(Transaction txn, TransportId t) throws DbException; void removeTransport(Transaction txn, TransportId t) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/** /**
* Marks the given message as shared or unshared. * Marks the given message as shared or unshared.
*/ */

View File

@@ -0,0 +1,23 @@
package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId;
/** An event that is broadcast when a contact is marked active or inactive. */
public class ContactStatusChangedEvent extends Event {
private final ContactId contactId;
private final boolean active;
public ContactStatusChangedEvent(ContactId contactId, boolean active) {
this.contactId = contactId;
this.active = active;
}
public ContactId getContactId() {
return contactId;
}
public boolean isActive() {
return active;
}
}

View File

@@ -13,7 +13,9 @@ import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -41,12 +43,12 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} }
@Override @Override
public ContactId addContact(Author remote, AuthorId local) public ContactId addContact(Author remote, AuthorId local, boolean active)
throws DbException { throws DbException {
ContactId c; ContactId c;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction();
try { try {
c = db.addContact(txn, remote, local); c = db.addContact(txn, remote, local, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (AddContactHook hook : addHooks) for (AddContactHook hook : addHooks)
hook.addingContact(txn, contact); hook.addingContact(txn, contact);
@@ -71,7 +73,7 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} }
@Override @Override
public Collection<Contact> getContacts() throws DbException { public Collection<Contact> getActiveContacts() throws DbException {
Collection<Contact> contacts; Collection<Contact> contacts;
Transaction txn = db.startTransaction(); Transaction txn = db.startTransaction();
try { try {
@@ -80,7 +82,9 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
} }
return contacts; List<Contact> active = new ArrayList<Contact>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c);
return Collections.unmodifiableList(active);
} }
@Override @Override
@@ -94,6 +98,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
} }
} }
@Override
public void setContactActive(ContactId c, boolean active)
throws DbException {
Transaction txn = db.startTransaction();
try {
db.setContactActive(txn, c, active);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
}
private void removeContact(Transaction txn, ContactId c) private void removeContact(Transaction txn, ContactId c)
throws DbException { throws DbException {
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);

View File

@@ -64,7 +64,7 @@ interface Database<T> {
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact. * and returns an ID for the contact.
*/ */
ContactId addContact(T txn, Author remote, AuthorId local) ContactId addContact(T txn, Author remote, AuthorId local, boolean active)
throws DbException; throws DbException;
/** /**
@@ -188,11 +188,6 @@ interface Database<T> {
*/ */
Contact getContact(T txn, ContactId c) throws DbException; Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns the IDs of all contacts.
*/
Collection<ContactId> getContactIds(T txn) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
*/ */
@@ -240,6 +235,11 @@ interface Database<T> {
*/ */
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/**
* Returns the IDs of all messages in the given group.
*/
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
/** /**
* Returns the metadata for all messages in the given group. * Returns the metadata for all messages in the given group.
*/ */
@@ -424,6 +424,12 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c, void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException; Collection<MessageId> requested) throws DbException;
/**
* Removes the status of the given message with respect to the given
* contact.
*/
void removeStatus(T txn, ContactId c, MessageId m) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -440,6 +446,12 @@ interface Database<T> {
*/ */
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException; void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(T txn, ContactId c, boolean active)
throws DbException;
/** /**
* Marks the given message as shared or unshared. * Marks the given message as shared or unshared.
*/ */

View File

@@ -16,6 +16,7 @@ import org.briarproject.api.db.NoSuchTransportException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.ContactAddedEvent; import org.briarproject.api.event.ContactAddedEvent;
import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupAddedEvent;
@@ -56,7 +57,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -70,12 +70,6 @@ import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
import static org.briarproject.api.sync.ValidationManager.Validity.VALID; import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES; import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
/**
* An implementation of DatabaseComponent using reentrant read-write locks.
* Depending on the JVM's lock implementation, this implementation may allow
* writers to starve. LockFairnessTest can be used to test whether this
* implementation is safe on a given JVM.
*/
class DatabaseComponentImpl<T> implements DatabaseComponent { class DatabaseComponentImpl<T> implements DatabaseComponent {
private static final Logger LOG = private static final Logger LOG =
@@ -143,13 +137,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
public ContactId addContact(Transaction transaction, Author remote, public ContactId addContact(Transaction transaction, Author remote,
AuthorId local) throws DbException { AuthorId local, boolean active) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, local)) if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException(); throw new NoSuchLocalAuthorException();
if (db.containsContact(txn, remote.getId(), local)) if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(); throw new ContactExistsException();
ContactId c = db.addContact(txn, remote, local); ContactId c = db.addContact(txn, remote, local, active);
transaction.attach(new ContactAddedEvent(c)); transaction.attach(new ContactAddedEvent(c));
return c; return c;
} }
@@ -177,7 +171,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsGroup(txn, m.getGroupId())) if (!db.containsGroup(txn, m.getGroupId()))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
if (!db.containsMessage(txn, m.getId())) { if (!db.containsMessage(txn, m.getId())) {
addMessage(txn, m, VALID, shared, null); addMessage(txn, m, VALID, shared);
transaction.attach(new MessageAddedEvent(m, null)); transaction.attach(new MessageAddedEvent(m, null));
transaction.attach(new MessageValidatedEvent(m, c, true, true)); transaction.attach(new MessageValidatedEvent(m, c, true, true));
if (shared) transaction.attach(new MessageSharedEvent(m)); if (shared) transaction.attach(new MessageSharedEvent(m));
@@ -185,26 +179,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta); db.mergeMessageMetadata(txn, m.getId(), meta);
} }
/** private void addMessage(T txn, Message m, Validity validity, boolean shared)
* Stores a message and initialises its status with respect to each contact. throws DbException {
*
* @param sender null for a locally generated message.
*/
private void addMessage(T txn, Message m, Validity validity, boolean shared,
ContactId sender) throws DbException {
db.addMessage(txn, m, validity, shared); db.addMessage(txn, m, validity, shared);
GroupId g = m.getGroupId(); for (ContactId c : db.getVisibility(txn, m.getGroupId())) {
Collection<ContactId> visibility = db.getVisibility(txn, g); boolean offered = db.removeOfferedMessage(txn, c, m.getId());
visibility = new HashSet<ContactId>(visibility); db.addStatus(txn, c, m.getId(), offered, offered);
for (ContactId c : db.getContactIds(txn)) {
if (visibility.contains(c)) {
boolean offered = db.removeOfferedMessage(txn, c, m.getId());
boolean seen = offered || c.equals(sender);
db.addStatus(txn, c, m.getId(), offered, seen);
} else {
if (c.equals(sender)) throw new IllegalStateException();
db.addStatus(txn, c, m.getId(), false, false);
}
} }
} }
@@ -516,7 +496,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchContactException(); throw new NoSuchContactException();
if (db.containsVisibleGroup(txn, c, m.getGroupId())) { if (db.containsVisibleGroup(txn, c, m.getGroupId())) {
if (!db.containsMessage(txn, m.getId())) { if (!db.containsMessage(txn, m.getId())) {
addMessage(txn, m, UNKNOWN, false, c); addMessage(txn, m, UNKNOWN, false);
transaction.attach(new MessageAddedEvent(m, c)); transaction.attach(new MessageAddedEvent(m, c));
} }
db.raiseAckFlag(txn, c, m.getId()); db.raiseAckFlag(txn, c, m.getId());
@@ -529,8 +509,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
int count = db.countOfferedMessages(txn, c);
boolean ack = false, request = false; boolean ack = false, request = false;
int count = db.countOfferedMessages(txn, c);
for (MessageId m : o.getMessageIds()) { for (MessageId m : o.getMessageIds()) {
if (db.containsVisibleMessage(txn, c, m)) { if (db.containsVisibleMessage(txn, c, m)) {
db.raiseSeenFlag(txn, c, m); db.raiseSeenFlag(txn, c, m);
@@ -601,6 +581,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new TransportRemovedEvent(t)); transaction.attach(new TransportRemovedEvent(t));
} }
public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setContactActive(txn, c, active);
transaction.attach(new ContactStatusChangedEvent(c, active));
}
public void setMessageShared(Transaction transaction, Message m, public void setMessageShared(Transaction transaction, Message m,
boolean shared) throws DbException { boolean shared) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
@@ -638,8 +627,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsGroup(txn, g)) if (!db.containsGroup(txn, g))
throw new NoSuchGroupException(); throw new NoSuchGroupException();
boolean wasVisible = db.containsVisibleGroup(txn, c, g); boolean wasVisible = db.containsVisibleGroup(txn, c, g);
if (visible && !wasVisible) db.addVisibility(txn, c, g); if (visible && !wasVisible) {
else if (!visible && wasVisible) db.removeVisibility(txn, c, g); db.addVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g)) {
boolean seen = db.removeOfferedMessage(txn, c, m);
db.addStatus(txn, c, m, seen, seen);
}
} else if (!visible && wasVisible) {
db.removeVisibility(txn, c, g);
for (MessageId m : db.getMessageIds(txn, g))
db.removeStatus(txn, c, m);
}
if (visible != wasVisible) { if (visible != wasVisible) {
List<ContactId> affected = Collections.singletonList(c); List<ContactId> affected = Collections.singletonList(c);
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));

View File

@@ -63,15 +63,15 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
*/ */
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 21; private static final int SCHEMA_VERSION = 22;
private static final int MIN_SCHEMA_VERSION = 21; private static final int MIN_SCHEMA_VERSION = 22;
private static final String CREATE_SETTINGS = private static final String CREATE_SETTINGS =
"CREATE TABLE settings" "CREATE TABLE settings"
+ " (key VARCHAR NOT NULL," + " (namespace VARCHAR NOT NULL,"
+ " key VARCHAR NOT NULL,"
+ " value VARCHAR NOT NULL," + " value VARCHAR NOT NULL,"
+ " namespace VARCHAR NOT NULL," + " PRIMARY KEY (namespace, key))";
+ " PRIMARY KEY (key, namespace))";
private static final String CREATE_LOCAL_AUTHORS = private static final String CREATE_LOCAL_AUTHORS =
"CREATE TABLE localAuthors" "CREATE TABLE localAuthors"
@@ -89,6 +89,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " name VARCHAR NOT NULL," + " name VARCHAR NOT NULL,"
+ " publicKey BINARY NOT NULL," + " publicKey BINARY NOT NULL,"
+ " localAuthorId HASH NOT NULL," + " localAuthorId HASH NOT NULL,"
+ " active BOOLEAN NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (localAuthorId)" + " FOREIGN KEY (localAuthorId)"
+ " REFERENCES localAuthors (authorId)" + " REFERENCES localAuthors (authorId)"
@@ -441,20 +442,21 @@ abstract class JdbcDatabase implements Database<Connection> {
return new DeviceId(StringUtils.fromHexString(s.get(DEVICE_ID_KEY))); return new DeviceId(StringUtils.fromHexString(s.get(DEVICE_ID_KEY)));
} }
public ContactId addContact(Connection txn, Author remote, AuthorId local) public ContactId addContact(Connection txn, Author remote, AuthorId local,
throws DbException { boolean active) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Create a contact row // Create a contact row
String sql = "INSERT INTO contacts" String sql = "INSERT INTO contacts"
+ " (authorId, name, publicKey, localAuthorId)" + " (authorId, name, publicKey, localAuthorId, active)"
+ " VALUES (?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getId().getBytes()); ps.setBytes(1, remote.getId().getBytes());
ps.setString(2, remote.getName()); ps.setString(2, remote.getName());
ps.setBytes(3, remote.getPublicKey()); ps.setBytes(3, remote.getPublicKey());
ps.setBytes(4, local.getBytes()); ps.setBytes(4, local.getBytes());
ps.setBoolean(5, active);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -468,31 +470,6 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rs.next()) throw new DbStateException(); if (rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.close(); ps.close();
// Create a status row for each message
sql = "SELECT messageID FROM messages";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
Collection<byte[]> ids = new ArrayList<byte[]>();
while (rs.next()) ids.add(rs.getBytes(1));
rs.close();
ps.close();
if (!ids.isEmpty()) {
sql = "INSERT INTO statuses (messageId, contactId, ack,"
+ " seen, requested, expiry, txCount)"
+ " VALUES (?, ?, FALSE, FALSE, FALSE, 0, 0)";
ps = txn.prepareStatement(sql);
ps.setInt(2, c.getInt());
for (byte[] id : ids) {
ps.setBytes(1, id);
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();
if (batchAffected.length != ids.size())
throw new DbStateException();
for (int rows : batchAffected)
if (rows != 1) throw new DbStateException();
ps.close();
}
return c; return c;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs);
@@ -951,7 +928,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT authorId, name, publicKey, localAuthorId" String sql = "SELECT authorId, name, publicKey,"
+ " localAuthorId, active"
+ " FROM contacts" + " FROM contacts"
+ " WHERE contactId = ?"; + " WHERE contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -962,30 +940,11 @@ abstract class JdbcDatabase implements Database<Connection> {
String name = rs.getString(2); String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3); byte[] publicKey = rs.getBytes(3);
AuthorId localAuthorId = new AuthorId(rs.getBytes(4)); AuthorId localAuthorId = new AuthorId(rs.getBytes(4));
boolean active = rs.getBoolean(5);
rs.close(); rs.close();
ps.close(); ps.close();
Author author = new Author(authorId, name, publicKey); Author author = new Author(authorId, name, publicKey);
return new Contact(c, author, localAuthorId); return new Contact(c, author, localAuthorId, active);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<ContactId> getContactIds(Connection txn)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId FROM contacts";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
List<ContactId> ids = new ArrayList<ContactId>();
while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs);
tryToClose(ps); tryToClose(ps);
@@ -999,7 +958,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, authorId, name, publicKey," String sql = "SELECT contactId, authorId, name, publicKey,"
+ " localAuthorId" + " localAuthorId, active"
+ " FROM contacts"; + " FROM contacts";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
@@ -1011,7 +970,9 @@ abstract class JdbcDatabase implements Database<Connection> {
byte[] publicKey = rs.getBytes(4); byte[] publicKey = rs.getBytes(4);
Author author = new Author(authorId, name, publicKey); Author author = new Author(authorId, name, publicKey);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5)); AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
contacts.add(new Contact(contactId, author, localAuthorId)); boolean active = rs.getBoolean(6);
contacts.add(new Contact(contactId, author, localAuthorId,
active));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1151,6 +1112,27 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Collection<MessageId> getMessageIds(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Map<MessageId, Metadata> getMessageMetadata(Connection txn, public Map<MessageId, Metadata> getMessageMetadata(Connection txn,
GroupId g) throws DbException { GroupId g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1309,15 +1291,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT m.messageId FROM messages AS m" String sql = "SELECT m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s" + " JOIN statuses AS s"
+ " ON m.messageId = s.messageId" + " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId" + " WHERE contactId = ?"
+ " WHERE gv.contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE AND requested = FALSE" + " AND seen = FALSE AND requested = FALSE"
+ " AND s.expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp DESC LIMIT ?"; + " ORDER BY timestamp DESC LIMIT ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -1368,15 +1347,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT length, m.messageId FROM messages AS m" String sql = "SELECT length, m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s" + " JOIN statuses AS s"
+ " ON m.messageId = s.messageId" + " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId" + " WHERE contactId = ?"
+ " WHERE gv.contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE" + " AND seen = FALSE"
+ " AND s.expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp DESC"; + " ORDER BY timestamp DESC";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -1454,15 +1430,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT length, m.messageId FROM messages AS m" String sql = "SELECT length, m.messageId FROM messages AS m"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " JOIN statuses AS s" + " JOIN statuses AS s"
+ " ON m.messageId = s.messageId" + " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId" + " WHERE contactId = ?"
+ " WHERE gv.contactId = ?"
+ " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL"
+ " AND seen = FALSE AND requested = TRUE" + " AND seen = FALSE AND requested = TRUE"
+ " AND s.expiry < ?" + " AND expiry < ?"
+ " ORDER BY timestamp DESC"; + " ORDER BY timestamp DESC";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -1777,12 +1750,12 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
// Update any settings that already exist // Update any settings that already exist
String sql = "UPDATE settings SET value = ?" String sql = "UPDATE settings SET value = ?"
+ " WHERE key = ? AND namespace = ?"; + " WHERE namespace = ? AND key = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
for (Entry<String, String> e : s.entrySet()) { for (Entry<String, String> e : s.entrySet()) {
ps.setString(1, e.getValue()); ps.setString(1, e.getValue());
ps.setString(2, e.getKey()); ps.setString(2, namespace);
ps.setString(3, namespace); ps.setString(3, e.getKey());
ps.addBatch(); ps.addBatch();
} }
int[] batchAffected = ps.executeBatch(); int[] batchAffected = ps.executeBatch();
@@ -1792,15 +1765,15 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows > 1) throw new DbStateException(); if (rows > 1) throw new DbStateException();
} }
// Insert any settings that don't already exist // Insert any settings that don't already exist
sql = "INSERT INTO settings (key, value, namespace)" sql = "INSERT INTO settings (namespace, key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
int updateIndex = 0, inserted = 0; int updateIndex = 0, inserted = 0;
for (Entry<String, String> e : s.entrySet()) { for (Entry<String, String> e : s.entrySet()) {
if (batchAffected[updateIndex] == 0) { if (batchAffected[updateIndex] == 0) {
ps.setString(1, e.getKey()); ps.setString(1, namespace);
ps.setString(2, e.getValue()); ps.setString(2, e.getKey());
ps.setString(3, namespace); ps.setString(3, e.getValue());
ps.addBatch(); ps.addBatch();
inserted++; inserted++;
} }
@@ -1976,6 +1949,24 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void removeStatus(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM statuses"
+ " WHERE contactId = ? AND messageId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, m.getBytes());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void removeTransport(Connection txn, TransportId t) public void removeTransport(Connection txn, TransportId t)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2028,6 +2019,23 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void setContactActive(Connection txn, ContactId c, boolean active)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET active = ? WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBoolean(1, active);
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void setMessageShared(Connection txn, MessageId m, boolean shared) public void setMessageShared(Connection txn, MessageId m, boolean shared)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -2037,7 +2045,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBoolean(1, shared); ps.setBoolean(1, shared);
ps.setBytes(2, m.getBytes()); ps.setBytes(2, m.getBytes());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);
@@ -2054,7 +2062,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, valid ? VALID.getValue() : INVALID.getValue()); ps.setInt(1, valid ? VALID.getValue() : INVALID.getValue());
ps.setBytes(2, m.getBytes()); ps.setBytes(2, m.getBytes());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException(); if (affected < 0 || affected > 1) throw new DbStateException();
ps.close(); ps.close();
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(ps); tryToClose(ps);

View File

@@ -219,7 +219,7 @@ abstract class Connector extends Thread {
long timestamp, boolean alice) throws DbException { long timestamp, boolean alice) throws DbException {
// Add the contact to the database // Add the contact to the database
contactId = contactManager.addContact(remoteAuthor, contactId = contactManager.addContact(remoteAuthor,
localAuthor.getId()); localAuthor.getId(), true);
// Derive transport keys // Derive transport keys
keyManager.addContact(contactId, master, timestamp, alice); keyManager.addContact(contactId, master, timestamp, alice);
return contactId; return contactId;

View File

@@ -107,7 +107,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
transportId = new TransportId("id"); transportId = new TransportId("id");
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
contactId = new ContactId(234); contactId = new ContactId(234);
contact = new Contact(contactId, author, localAuthorId); contact = new Contact(contactId, author, localAuthorId, true);
} }
private DatabaseComponent createDatabaseComponent(Database<Object> database, private DatabaseComponent createDatabaseComponent(Database<Object> database,
@@ -143,7 +143,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsContact(txn, authorId, localAuthorId); oneOf(database).containsContact(txn, authorId, localAuthorId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, localAuthorId); oneOf(database).addContact(txn, author, localAuthorId, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// getContacts() // getContacts()
@@ -193,7 +193,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
try { try {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, assertEquals(contactId,
db.addContact(transaction, author, localAuthorId)); db.addContact(transaction, author, localAuthorId, true));
assertEquals(Collections.singletonList(contact), assertEquals(Collections.singletonList(contact),
db.getContacts(transaction)); db.getContacts(transaction));
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
@@ -261,8 +261,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
oneOf(database).mergeMessageMetadata(txn, messageId, metadata); oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
oneOf(database).getVisibility(txn, groupId); oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
oneOf(database).getContactIds(txn);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false); oneOf(database).addStatus(txn, contactId, messageId, false, false);
@@ -296,11 +294,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(17).of(database).startTransaction(); exactly(18).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(18).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(17).of(database).abortTransaction(txn); exactly(18).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown); shutdown);
@@ -458,6 +456,16 @@ public class DatabaseComponentImplTest extends BriarTestCase {
db.endTransaction(transaction); db.endTransaction(transaction);
} }
transaction = db.startTransaction();
try {
db.setContactActive(transaction, contactId, true);
fail();
} catch (NoSuchContactException expected) {
// Expected
} finally {
db.endTransaction(transaction);
}
transaction = db.startTransaction(); transaction = db.startTransaction();
try { try {
db.setReorderingWindow(transaction, contactId, transportId, 0, 0, db.setReorderingWindow(transaction, contactId, transportId, 0, 0,
@@ -503,7 +511,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
Transaction transaction = db.startTransaction(); Transaction transaction = db.startTransaction();
try { try {
db.addContact(transaction, author, localAuthorId); db.addContact(transaction, author, localAuthorId, true);
fail(); fail();
} catch (NoSuchLocalAuthorException expected) { } catch (NoSuchLocalAuthorException expected) {
// Expected // Expected
@@ -757,7 +765,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).containsContact(txn, authorId, localAuthorId); oneOf(database).containsContact(txn, authorId, localAuthorId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, localAuthorId); oneOf(database).addContact(txn, author, localAuthorId, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// endTransaction() // endTransaction()
@@ -778,7 +786,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
try { try {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, assertEquals(contactId,
db.addContact(transaction, author, localAuthorId)); db.addContact(transaction, author, localAuthorId, true));
transaction.setComplete(); transaction.setComplete();
} finally { } finally {
db.endTransaction(transaction); db.endTransaction(transaction);
@@ -1074,11 +1082,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
oneOf(database).addMessage(txn, message, UNKNOWN, false); oneOf(database).addMessage(txn, message, UNKNOWN, false);
oneOf(database).getVisibility(txn, groupId); oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
oneOf(database).getContactIds(txn);
will(returnValue(Collections.singletonList(contactId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId); oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, true); oneOf(database).addStatus(txn, contactId, messageId, false, false);
oneOf(database).raiseAckFlag(txn, contactId, messageId); oneOf(database).raiseAckFlag(txn, contactId, messageId);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// The message was received and added // The message was received and added
@@ -1270,6 +1276,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
oneOf(database).containsVisibleGroup(txn, contactId, groupId); oneOf(database).containsVisibleGroup(txn, contactId, groupId);
will(returnValue(false)); // Not yet visible will(returnValue(false)); // Not yet visible
oneOf(database).addVisibility(txn, contactId, groupId); oneOf(database).addVisibility(txn, contactId, groupId);
oneOf(database).getMessageIds(txn, groupId);
will(returnValue(Collections.singletonList(messageId)));
oneOf(database).removeOfferedMessage(txn, contactId, messageId);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, false, false);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
GroupVisibilityUpdatedEvent.class))); GroupVisibilityUpdatedEvent.class)));

View File

@@ -108,7 +108,8 @@ public class H2DatabaseTest extends BriarTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsGroup(txn, groupId)); assertFalse(db.containsGroup(txn, groupId));
db.addGroup(txn, group); db.addGroup(txn, group);
@@ -170,7 +171,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true); db.addMessage(txn, message, VALID, true);
@@ -207,7 +209,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and an unvalidated message // Add a contact, a group and an unvalidated message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, UNKNOWN, true); db.addMessage(txn, message, UNKNOWN, true);
@@ -245,7 +248,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and an unshared message // Add a contact, a group and an unshared message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, false); db.addMessage(txn, message, VALID, false);
@@ -283,7 +287,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true); db.addMessage(txn, message, VALID, true);
@@ -302,44 +307,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testSendableMessagesMustBeVisible() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true);
db.addStatus(txn, contactId, messageId, false, false);
// The group is not visible to the contact, so the message
// should not be sendable
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
// Making the group visible should make the message sendable
db.addVisibility(txn, contactId, groupId);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertEquals(Collections.singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100);
assertEquals(Collections.singletonList(messageId), ids);
// Making the group invisible should make the message unsendable
db.removeVisibility(txn, contactId, groupId);
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100);
assertTrue(ids.isEmpty());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testMessagesToAck() throws Exception { public void testMessagesToAck() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -347,7 +314,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and a group // Add a contact and a group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
@@ -383,7 +351,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true); db.addMessage(txn, message, VALID, true);
@@ -546,7 +515,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and a group // Add a contact and a group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
@@ -565,7 +535,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact // Add a contact
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
// The group is not in the database // The group is not in the database
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -582,7 +553,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, VALID, true); db.addMessage(txn, message, VALID, true);
db.addStatus(txn, contactId, messageId, false, false); db.addStatus(txn, contactId, messageId, false, false);
@@ -601,7 +573,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and a group // Add a contact and a group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
@@ -636,7 +609,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and the groups // Add a contact and the groups
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
for (Group g : groups) db.addGroup(txn, g); for (Group g : groups) db.addGroup(txn, g);
// Make the groups visible to the contact // Make the groups visible to the contact
@@ -668,7 +642,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
db.addTransportKeys(txn, contactId, keys); db.addTransportKeys(txn, contactId, keys);
@@ -729,7 +704,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys)); db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys));
@@ -764,7 +740,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys)); db.updateTransportKeys(txn, Collections.singletonMap(contactId, keys));
@@ -800,7 +777,8 @@ public class H2DatabaseTest extends BriarTestCase {
assertEquals(Collections.emptyList(), contacts); assertEquals(Collections.emptyList(), contacts);
// Add a contact associated with the local author // Add a contact associated with the local author
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
contacts = db.getContacts(txn, localAuthorId); contacts = db.getContacts(txn, localAuthorId);
assertEquals(Collections.singletonList(contactId), contacts); assertEquals(Collections.singletonList(contactId), contacts);
@@ -821,7 +799,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact - initially there should be no offered messages // Add a contact - initially there should be no offered messages
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
assertEquals(0, db.countOfferedMessages(txn, contactId)); assertEquals(0, db.countOfferedMessages(txn, contactId));
// Add some offered messages and count them // Add some offered messages and count them
@@ -959,7 +938,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact // Add a contact
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
// Add a group and make it visible to the contact // Add a group and make it visible to the contact
db.addGroup(txn, group); db.addGroup(txn, group);
@@ -1035,7 +1015,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact and a group // Add a contact and a group
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
@@ -1068,8 +1049,8 @@ public class H2DatabaseTest extends BriarTestCase {
db.addLocalAuthor(txn, localAuthor1); db.addLocalAuthor(txn, localAuthor1);
// Add the same contact for each local pseudonym // Add the same contact for each local pseudonym
ContactId contactId = db.addContact(txn, author, localAuthorId); ContactId contactId = db.addContact(txn, author, localAuthorId, true);
ContactId contactId1 = db.addContact(txn, author, localAuthorId1); ContactId contactId1 = db.addContact(txn, author, localAuthorId1, true);
// The contacts should be distinct // The contacts should be distinct
assertNotEquals(contactId, contactId1); assertNotEquals(contactId, contactId1);
@@ -1088,7 +1069,8 @@ public class H2DatabaseTest extends BriarTestCase {
// Add a contact, a group and a message // Add a contact, a group and a message
db.addLocalAuthor(txn, localAuthor); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId)); assertEquals(contactId, db.addContact(txn, author, localAuthorId,
true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addMessage(txn, message, VALID, true); db.addMessage(txn, message, VALID, true);

View File

@@ -38,7 +38,7 @@ public class PluginManagerImplTest extends BriarTestCase {
Mockery context = new Mockery() {{ Mockery context = new Mockery() {{
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
final Executor ioExecutor = Executors.newCachedThreadPool(); final Executor ioExecutor = Executors.newSingleThreadExecutor();
final EventBus eventBus = context.mock(EventBus.class); final EventBus eventBus = context.mock(EventBus.class);
final SimplexPluginConfig simplexPluginConfig = final SimplexPluginConfig simplexPluginConfig =
context.mock(SimplexPluginConfig.class); context.mock(SimplexPluginConfig.class);

View File

@@ -134,7 +134,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Add Bob as a contact // Add Bob as a contact
Author bobAuthor = new Author(bobId, "Bob", Author bobAuthor = new Author(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH]); new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(bobAuthor, aliceId); ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
true);
// Derive and store the transport keys // Derive and store the transport keys
keyManager.addContact(contactId, master, timestamp, true); keyManager.addContact(contactId, master, timestamp, true);
@@ -204,7 +205,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
// Add Alice as a contact // Add Alice as a contact
Author aliceAuthor = new Author(aliceId, "Alice", Author aliceAuthor = new Author(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH]); new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(aliceAuthor, bobId); ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
true);
// Derive and store the transport keys // Derive and store the transport keys
keyManager.addContact(contactId, master, timestamp, false); keyManager.addContact(contactId, master, timestamp, false);