mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 21:29:54 +01:00
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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user