Record when the transports and subscriptions visible to each contact

were last modified.

In future this will be used to determine when to send updates.
This commit is contained in:
akwizgran
2011-10-18 17:32:32 +01:00
parent d7a417f36d
commit c8b2cc38de
4 changed files with 173 additions and 166 deletions

View File

@@ -116,12 +116,11 @@ interface Database<T> {
boolean addPrivateMessage(T txn, Message m, ContactId c) throws DbException; boolean addPrivateMessage(T txn, Message m, ContactId c) throws DbException;
/** /**
* Subscribes to the given group and returns true if the subscription did * Subscribes to the given group.
* not previously exist.
* <p> * <p>
* Locking: subscriptions write. * Locking: subscriptions write.
*/ */
boolean addSubscription(T txn, Group g) throws DbException; void addSubscription(T txn, Group g) throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact.
@@ -407,14 +406,15 @@ interface Database<T> {
void removeMessage(T txn, MessageId m) throws DbException; void removeMessage(T txn, MessageId m) throws DbException;
/** /**
* Unsubscribes from the given group and returns true if a subscription * Unsubscribes from the given group and returns the IDs of any contacts
* previously existed. Any messages belonging to the group * affected by the change. Any messages belonging to the group are deleted
* are deleted from the database. * from the database.
* <p> * <p>
* Locking: contacts read, messages write, messageStatuses write, * Locking: contacts read, messages write, messageStatuses write,
* subscriptions write. * subscriptions write.
*/ */
boolean removeSubscription(T txn, GroupId g) throws DbException; Collection<ContactId> removeSubscription(T txn, GroupId g)
throws DbException;
/** /**
* Sets the configuration for the given transport, replacing any existing * Sets the configuration for the given transport, replacing any existing
@@ -436,11 +436,12 @@ interface Database<T> {
/** /**
* Sets the local transport properties for the given transport, replacing * Sets the local transport properties for the given transport, replacing
* any existing properties for that transport. * any existing properties for that transport. Returns true if the
* properties have changed.
* <p> * <p>
* Locking: transports write. * Locking: transports write.
*/ */
void setLocalProperties(T txn, TransportId t, TransportProperties p) boolean setLocalProperties(T txn, TransportId t, TransportProperties p)
throws DbException; throws DbException;
/** /**
@@ -517,10 +518,11 @@ interface Database<T> {
/** /**
* Makes the given group visible to the given set of contacts and invisible * Makes the given group visible to the given set of contacts and invisible
* to any other contacts. * to any other contacts. Returns the IDs of any contacts affected by the
* change.
* <p> * <p>
* Locking: contacts read, subscriptions write. * Locking: contacts read, subscriptions write.
*/ */
void setVisibility(T txn, GroupId g, Collection<ContactId> visible) Collection<ContactId> setVisibility(T txn, GroupId g,
throws DbException; Collection<ContactId> visible) throws DbException;
} }

View File

@@ -11,7 +11,6 @@ import java.util.ArrayList;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -1114,16 +1113,11 @@ DatabaseCleaner.Callback {
public void setConfig(TransportId t, TransportConfig config) public void setConfig(TransportId t, TransportConfig config)
throws DbException { throws DbException {
boolean changed = false;
transportLock.writeLock().lock(); transportLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
TransportConfig old = db.getConfig(txn, t); db.setConfig(txn, t, config);
if(!config.equals(old)) {
db.setConfig(txn, t, config);
changed = true;
}
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1132,8 +1126,6 @@ DatabaseCleaner.Callback {
} finally { } finally {
transportLock.writeLock().unlock(); transportLock.writeLock().unlock();
} }
// Call the listeners outside the lock
if(changed) callListeners(new TransportsUpdatedEvent());
} }
public void setConnectionWindow(ContactId c, TransportId t, public void setConnectionWindow(ContactId c, TransportId t,
@@ -1160,16 +1152,12 @@ DatabaseCleaner.Callback {
public void setLocalProperties(TransportId t, public void setLocalProperties(TransportId t,
TransportProperties properties) throws DbException { TransportProperties properties) throws DbException {
boolean changed = false; boolean changed;
transportLock.writeLock().lock(); transportLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
TransportProperties old = db.getLocalTransports(txn).get(t); changed = db.setLocalProperties(txn, t, properties);
if(!properties.equals(old)) {
db.setLocalProperties(txn, t, properties);
changed = true;
}
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1278,19 +1266,16 @@ DatabaseCleaner.Callback {
public void setVisibility(GroupId g, Collection<ContactId> visible) public void setVisibility(GroupId g, Collection<ContactId> visible)
throws DbException { throws DbException {
Collection<ContactId> then, now; Collection<ContactId> affected;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
subscriptionLock.writeLock().lock(); subscriptionLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
// Get the contacts to which the group used to be visible
then = new HashSet<ContactId>(db.getVisibility(txn, g));
// Don't try to make the group visible to ex-contacts // Don't try to make the group visible to ex-contacts
now = new HashSet<ContactId>(visible); visible.retainAll((db.getContacts(txn)));
now.retainAll(new HashSet<ContactId>(db.getContacts(txn))); affected = db.setVisibility(txn, g, visible);
db.setVisibility(txn, g, now);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1302,22 +1287,19 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Work out which contacts were affected by the change
Collection<ContactId> affected = new ArrayList<ContactId>();
for(ContactId c : then) if(!now.contains(c)) affected.add(c);
for(ContactId c : now) if(!then.contains(c)) affected.add(c);
// Call the listeners outside the lock // Call the listeners outside the lock
callListeners(new SubscriptionsUpdatedEvent(affected)); if(!affected.isEmpty())
callListeners(new SubscriptionsUpdatedEvent(affected));
} }
public void subscribe(Group g) throws DbException { public void subscribe(Group g) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g); if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g);
boolean added;
subscriptionLock.writeLock().lock(); subscriptionLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
added = db.addSubscription(txn, g); if(!db.containsSubscription(txn, g.getId()))
db.addSubscription(txn, g);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1326,14 +1308,12 @@ DatabaseCleaner.Callback {
} finally { } finally {
subscriptionLock.writeLock().unlock(); subscriptionLock.writeLock().unlock();
} }
// Call the listeners outside the lock
if(added) callListeners(new SubscriptionsUpdatedEvent());
} }
public void unsubscribe(GroupId g) throws DbException { public void unsubscribe(GroupId g) throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g); if(LOG.isLoggable(Level.FINE)) LOG.fine("Unsubscribing from " + g);
boolean removed; boolean removed = false;
Collection<ContactId> affected; Collection<ContactId> affected = null;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
@@ -1344,8 +1324,10 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
affected = db.getVisibility(txn, g); if(db.containsSubscription(txn, g)) {
removed = db.removeSubscription(txn, g); affected = db.removeSubscription(txn, g);
removed = true;
}
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1364,7 +1346,8 @@ DatabaseCleaner.Callback {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock // Call the listeners outside the lock
if(removed) callListeners(new SubscriptionsUpdatedEvent(affected)); if(removed && !affected.isEmpty())
callListeners(new SubscriptionsUpdatedEvent(affected));
} }
public void checkFreeSpaceAndClean() throws DbException { public void checkFreeSpaceAndClean() throws DbException {

View File

@@ -12,7 +12,9 @@ import java.sql.Statement;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -207,6 +209,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " sent BIGINT NOT NULL," + " sent BIGINT NOT NULL,"
+ " received BIGINT NOT NULL," + " received BIGINT NOT NULL,"
+ " modified BIGINT NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -216,6 +219,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " sent BIGINT NOT NULL," + " sent BIGINT NOT NULL,"
+ " received BIGINT NOT NULL," + " received BIGINT NOT NULL,"
+ " modified BIGINT NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -507,23 +511,25 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close(); ps.close();
// Initialise the subscription timestamps // Initialise the subscription timestamps
sql = "INSERT INTO subscriptionTimestamps" sql = "INSERT INTO subscriptionTimestamps"
+ " (contactId, sent, received)" + " (contactId, sent, received, modified)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setLong(2, 0L); ps.setLong(2, 0L);
ps.setLong(3, 0L); ps.setLong(3, 0L);
ps.setLong(4, 1L);
affected = ps.executeUpdate(); affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
// Initialise the transport timestamps // Initialise the transport timestamps
sql = "INSERT INTO transportTimestamps" sql = "INSERT INTO transportTimestamps"
+ " (contactId, sent, received)" + " (contactId, sent, received, modified)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setLong(2, 0L); ps.setLong(2, 0L);
ps.setLong(3, 0L); ps.setLong(3, 0L);
ps.setLong(4, 1L);
affected = ps.executeUpdate(); affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -652,20 +658,10 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public boolean addSubscription(Connection txn, Group g) throws DbException { public void addSubscription(Connection txn, Group g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
String sql = "SELECT NULL FROM subscriptions WHERE groupId = ?"; String sql = "INSERT INTO subscriptions"
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
rs = ps.executeQuery();
boolean found = rs.next();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
if(found) return false;
sql = "INSERT INTO subscriptions"
+ " (groupId, groupName, groupKey, start)" + " (groupId, groupName, groupKey, start)"
+ " VALUES (?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -676,7 +672,6 @@ abstract class JdbcDatabase implements Database<Connection> {
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
return true;
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
@@ -1765,18 +1760,30 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public boolean removeSubscription(Connection txn, GroupId g) public Collection<ContactId> removeSubscription(Connection txn, GroupId g)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
String sql = "DELETE FROM subscriptions WHERE groupId = ?"; // Retrieve the contacts to which the subscription is visible
String sql = "SELECT contactId FROM visibilities WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
Collection<ContactId> visible = new ArrayList<ContactId>();
while(rs.next()) visible.add(new ContactId(rs.getInt(1)));
rs.close();
ps.close();
// Delete the subscription
sql = "DELETE FROM subscriptions WHERE groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if(affected > 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
return affected > 0; return visible;
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(rs);
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
} }
@@ -1784,31 +1791,26 @@ abstract class JdbcDatabase implements Database<Connection> {
public void setConfig(Connection txn, TransportId t, TransportConfig config) public void setConfig(Connection txn, TransportId t, TransportConfig config)
throws DbException { throws DbException {
setTransportDetails(txn, t, config, "transportConfig");
}
private void setTransportDetails(Connection txn, TransportId t,
Map<String, String> details, String table) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Delete any existing details for the given transport // Delete any existing config for the given transport
String sql = "DELETE FROM " + table + " WHERE transportId = ?"; String sql = "DELETE FROM transportConfig WHERE transportId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setInt(1, t.getInt());
ps.executeUpdate(); ps.executeUpdate();
ps.close(); ps.close();
// Store the new details // Store the new config
sql = "INSERT INTO " + table + " (transportId, key, value)" sql = "INSERT INTO transportConfig (transportId, key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setInt(1, t.getInt());
for(Entry<String, String> e : details.entrySet()) { for(Entry<String, String> e : config.entrySet()) {
ps.setString(2, e.getKey()); ps.setString(2, e.getKey());
ps.setString(3, e.getValue()); ps.setString(3, e.getValue());
ps.addBatch(); ps.addBatch();
} }
int[] batchAffected = ps.executeBatch(); int[] batchAffected = ps.executeBatch();
if(batchAffected.length != details.size()) if(batchAffected.length != config.size())
throw new DbStateException(); throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) { for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException(); if(batchAffected[i] != 1) throw new DbStateException();
@@ -1868,9 +1870,58 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void setLocalProperties(Connection txn, TransportId t, public boolean setLocalProperties(Connection txn, TransportId t,
TransportProperties properties) throws DbException { TransportProperties properties) throws DbException {
setTransportDetails(txn, t, properties, "transports"); PreparedStatement ps = null;
ResultSet rs = null;
try {
// Retrieve any existing properties for the given transport
String sql = "SELECT key, value FROM transports"
+ " WHERE transportId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt());
rs = ps.executeQuery();
TransportProperties old = new TransportProperties();
while(rs.next()) old.put(rs.getString(1), rs.getString(2));
rs.close();
ps.close();
// If the properties haven't changed, return
if(old.equals(properties)) return false;
// Delete any existing properties for the given transport
sql = "DELETE FROM transports WHERE transportId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt());
ps.executeUpdate();
ps.close();
// Store the new properties
sql = "INSERT INTO transports (transportId, key, value)"
+ " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt());
for(Entry<String, String> e : properties.entrySet()) {
ps.setString(2, e.getKey());
ps.setString(3, e.getValue());
ps.addBatch();
}
int[] batchAffected = ps.executeBatch();
if(batchAffected.length != properties.size())
throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException();
}
ps.close();
// Update the transport timestamps of all contacts
sql = "UPDATE transportTimestamps set modified = ?";
ps = txn.prepareStatement(sql);
ps.setLong(1, System.currentTimeMillis());
ps.executeUpdate();
ps.close();
return true;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
} }
public Rating setRating(Connection txn, AuthorId a, Rating r) public Rating setRating(Connection txn, AuthorId a, Rating r)
@@ -2179,17 +2230,29 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void setVisibility(Connection txn, GroupId g, public Collection<ContactId> setVisibility(Connection txn, GroupId g,
Collection<ContactId> visible) throws DbException { Collection<ContactId> visible) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null;
try { try {
// Retrieve any existing visibilities
String sql = "SELECT contactId FROM visibilities WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
Collection<ContactId> then = new HashSet<ContactId>();
while(rs.next()) then.add(new ContactId(rs.getInt(1)));
rs.close();
ps.close();
// If the visibilities haven't changed, return
Collection<ContactId> now = new HashSet<ContactId>(visible);
if(then.equals(now)) return Collections.emptyList();
// Delete any existing visibilities // Delete any existing visibilities
String sql = "DELETE FROM visibilities WHERE groupId = ?"; sql = "DELETE FROM visibilities where groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
ps.executeUpdate(); ps.executeUpdate();
ps.close(); ps.close();
if(visible.isEmpty()) return;
// Store the new visibilities // Store the new visibilities
sql = "INSERT INTO visibilities (groupId, contactId)" sql = "INSERT INTO visibilities (groupId, contactId)"
+ " VALUES (?, ?)"; + " VALUES (?, ?)";
@@ -2205,7 +2268,29 @@ abstract class JdbcDatabase implements Database<Connection> {
for(int i = 0; i < batchAffected.length; i++) { for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException(); if(batchAffected[i] != 1) throw new DbStateException();
} }
ps.close();
// Update the subscription timestamps of any affected contacts
Collection<ContactId> affected = new ArrayList<ContactId>();
for(ContactId c : then) if(!now.contains(c)) affected.add(c);
for(ContactId c : now) if(!then.contains(c)) affected.add(c);
sql = "UPDATE subscriptionTimestamps SET modified = ?"
+ " WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setLong(1, System.currentTimeMillis());
for(ContactId c : affected) {
ps.setInt(2, c.getInt());
ps.addBatch();
}
batchAffected = ps.executeBatch();
if(batchAffected.length != affected.size())
throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] > 1) throw new DbStateException();
}
ps.close();
return affected;
} catch(SQLException e) { } catch(SQLException e) {
tryToClose(rs);
tryToClose(ps); tryToClose(ps);
throw new DbException(e); throw new DbException(e);
} }

View File

@@ -10,7 +10,6 @@ import junit.framework.TestCase;
import net.sf.briar.TestUtils; import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
@@ -20,7 +19,6 @@ import net.sf.briar.api.db.event.ContactAddedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessagesAddedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.db.event.TransportsUpdatedEvent;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
@@ -134,27 +132,26 @@ public abstract class DatabaseComponentTest extends TestCase {
oneOf(database).getRemoteProperties(txn, transportId); oneOf(database).getRemoteProperties(txn, transportId);
will(returnValue(remoteProperties)); will(returnValue(remoteProperties));
// subscribe(group) // subscribe(group)
oneOf(database).addSubscription(txn, group); oneOf(group).getId();
will(returnValue(true)); will(returnValue(groupId));
oneOf(listener).eventOccurred(with(any( oneOf(database).containsSubscription(txn, groupId);
SubscriptionsUpdatedEvent.class)));
// subscribe(group) again
oneOf(database).addSubscription(txn, group);
will(returnValue(false)); will(returnValue(false));
oneOf(database).addSubscription(txn, group);
// subscribe(group) again
oneOf(group).getId();
will(returnValue(groupId));
oneOf(database).containsSubscription(txn, groupId);
will(returnValue(true));
// getSubscriptions() // getSubscriptions()
oneOf(database).getSubscriptions(txn); oneOf(database).getSubscriptions(txn);
will(returnValue(Collections.singletonList(groupId))); will(returnValue(Collections.singletonList(groupId)));
// unsubscribe(groupId) // unsubscribe(groupId)
oneOf(database).getVisibility(txn, groupId); oneOf(database).containsSubscription(txn, groupId);
will(returnValue(Collections.<ContactId>emptySet()));
oneOf(database).removeSubscription(txn, groupId);
will(returnValue(true)); will(returnValue(true));
oneOf(listener).eventOccurred(with(any(
SubscriptionsUpdatedEvent.class)));
// unsubscribe(groupId) again
oneOf(database).getVisibility(txn, groupId);
will(returnValue(Collections.<ContactId>emptySet()));
oneOf(database).removeSubscription(txn, groupId); oneOf(database).removeSubscription(txn, groupId);
will(returnValue(Collections.<ContactId>emptyList()));
// unsubscribe(groupId) again
oneOf(database).containsSubscription(txn, groupId);
will(returnValue(false)); will(returnValue(false));
// setConnectionWindow(contactId, 123, connectionWindow) // setConnectionWindow(contactId, 123, connectionWindow)
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
@@ -1278,8 +1275,6 @@ public abstract class DatabaseComponentTest extends TestCase {
throws Exception { throws Exception {
final TransportProperties properties = final TransportProperties properties =
new TransportProperties(Collections.singletonMap("bar", "baz")); new TransportProperties(Collections.singletonMap("bar", "baz"));
final TransportProperties properties1 =
new TransportProperties(Collections.singletonMap("baz", "bam"));
Mockery context = new Mockery(); Mockery context = new Mockery();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class); final Database<Object> database = context.mock(Database.class);
@@ -1288,11 +1283,8 @@ public abstract class DatabaseComponentTest extends TestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).getLocalTransports(txn); oneOf(database).setLocalProperties(txn, transportId, properties);
will(returnValue(Collections.singletonMap(transportId, will(returnValue(true));
properties)));
oneOf(database).setLocalProperties(txn, transportId,
properties1);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
oneOf(listener).eventOccurred(with(any( oneOf(listener).eventOccurred(with(any(
TransportsUpdatedEvent.class))); TransportsUpdatedEvent.class)));
@@ -1300,7 +1292,7 @@ public abstract class DatabaseComponentTest extends TestCase {
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner);
db.addListener(listener); db.addListener(listener);
db.setLocalProperties(transportId, properties1); db.setLocalProperties(transportId, properties);
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@@ -1318,9 +1310,8 @@ public abstract class DatabaseComponentTest extends TestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).getLocalTransports(txn); oneOf(database).setLocalProperties(txn, transportId, properties);
will(returnValue(Collections.singletonMap(transportId, will(returnValue(false));
properties)));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner);
@@ -1331,60 +1322,6 @@ public abstract class DatabaseComponentTest extends TestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test
public void testTransportConfigChangedCallsListeners() throws Exception {
final TransportConfig config =
new TransportConfig(Collections.singletonMap("bar", "baz"));
final TransportConfig config1 =
new TransportConfig(Collections.singletonMap("baz", "bam"));
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).getConfig(txn, transportId);
will(returnValue(config));
oneOf(database).setConfig(txn, transportId, config1);
oneOf(database).commitTransaction(txn);
oneOf(listener).eventOccurred(with(any(
TransportsUpdatedEvent.class)));
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner);
db.addListener(listener);
db.setConfig(transportId, config1);
context.assertIsSatisfied();
}
@Test
public void testTransportConfigUnchangedDoesNotCallListeners()
throws Exception {
final TransportConfig config =
new TransportConfig(Collections.singletonMap("bar", "baz"));
Mockery context = new Mockery();
@SuppressWarnings("unchecked")
final Database<Object> database = context.mock(Database.class);
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).getConfig(txn, transportId);
will(returnValue(config));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner);
db.addListener(listener);
db.setConfig(transportId, config);
context.assertIsSatisfied();
}
@Test @Test
public void testSetSeen() throws Exception { public void testSetSeen() throws Exception {
Mockery context = new Mockery(); Mockery context = new Mockery();