mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 06:09:55 +01:00
Don't store subscription or transport updates that are older than those already received. Also some small changes to DatabaseComponent impls for readability.
This commit is contained in:
@@ -51,7 +51,8 @@ public interface DatabaseComponent {
|
|||||||
* Generates a bundle of acknowledgements, subscriptions, and batches of
|
* Generates a bundle of acknowledgements, subscriptions, and batches of
|
||||||
* messages for the given contact.
|
* messages for the given contact.
|
||||||
*/
|
*/
|
||||||
void generateBundle(ContactId c, BundleWriter bundleBuilder) throws DbException, IOException, GeneralSecurityException;
|
void generateBundle(ContactId c, BundleWriter bundleBuilder)
|
||||||
|
throws DbException, IOException, GeneralSecurityException;
|
||||||
|
|
||||||
/** Returns the IDs of all contacts. */
|
/** Returns the IDs of all contacts. */
|
||||||
Set<ContactId> getContacts() throws DbException;
|
Set<ContactId> getContacts() throws DbException;
|
||||||
@@ -73,7 +74,8 @@ public interface DatabaseComponent {
|
|||||||
* messages received from the given contact. Some or all of the messages
|
* messages received from the given contact. Some or all of the messages
|
||||||
* in the bundle may be stored.
|
* in the bundle may be stored.
|
||||||
*/
|
*/
|
||||||
void receiveBundle(ContactId c, BundleReader b) throws DbException, IOException, GeneralSecurityException;
|
void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||||
|
IOException, GeneralSecurityException;
|
||||||
|
|
||||||
/** Removes a contact (and all associated state) from the database. */
|
/** Removes a contact (and all associated state) from the database. */
|
||||||
void removeContact(ContactId c) throws DbException;
|
void removeContact(ContactId c) throws DbException;
|
||||||
@@ -81,12 +83,6 @@ public interface DatabaseComponent {
|
|||||||
/** Records the user's rating for the given author. */
|
/** Records the user's rating for the given author. */
|
||||||
void setRating(AuthorId a, Rating r) throws DbException;
|
void setRating(AuthorId a, Rating r) throws DbException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Records the transport details for the given contact, replacing any
|
|
||||||
* existing transport details.
|
|
||||||
*/
|
|
||||||
void setTransports(ContactId c, Map<String, String> transports) throws DbException;
|
|
||||||
|
|
||||||
/** Subscribes to the given group. */
|
/** Subscribes to the given group. */
|
||||||
void subscribe(GroupId g) throws DbException;
|
void subscribe(GroupId g) throws DbException;
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ import java.security.GeneralSecurityException;
|
|||||||
|
|
||||||
public interface MessageParser {
|
public interface MessageParser {
|
||||||
|
|
||||||
Message parseMessage(byte[] raw) throws IOException, GeneralSecurityException;
|
Message parseMessage(byte[] raw) throws IOException,
|
||||||
|
GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts write, transports write.
|
* Locking: contacts write, transports write.
|
||||||
*/
|
*/
|
||||||
ContactId addContact(T txn, Map<String, String> transports) throws DbException;
|
ContactId addContact(T txn, Map<String, String> transports)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false if the given message is already in the database. Otherwise
|
* Returns false if the given message is already in the database. Otherwise
|
||||||
@@ -96,7 +97,8 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts read, messages read, messageStatuses write.
|
* Locking: contacts read, messages read, messageStatuses write.
|
||||||
*/
|
*/
|
||||||
void addOutstandingBatch(T txn, ContactId c, BatchId b, Set<MessageId> sent) throws DbException;
|
void addOutstandingBatch(T txn, ContactId c, BatchId b, Set<MessageId> sent)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to the given group.
|
* Subscribes to the given group.
|
||||||
@@ -169,7 +171,8 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: messages read.
|
* Locking: messages read.
|
||||||
*/
|
*/
|
||||||
Iterable<MessageId> getMessagesByAuthor(T txn, AuthorId a) throws DbException;
|
Iterable<MessageId> getMessagesByAuthor(T txn, AuthorId a)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of children of the message identified by the given
|
* Returns the number of children of the message identified by the given
|
||||||
@@ -216,7 +219,8 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts read, messages read, messageStatuses read.
|
* Locking: contacts read, messages read, messageStatuses read.
|
||||||
*/
|
*/
|
||||||
Iterable<MessageId> getSendableMessages(T txn, ContactId c, long capacity) throws DbException;
|
Iterable<MessageId> getSendableMessages(T txn, ContactId c, long capacity)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the groups to which the user subscribes.
|
* Returns the groups to which the user subscribes.
|
||||||
@@ -225,6 +229,13 @@ interface Database<T> {
|
|||||||
*/
|
*/
|
||||||
Set<GroupId> getSubscriptions(T txn) throws DbException;
|
Set<GroupId> getSubscriptions(T txn) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the groups to which the given contact subscribes.
|
||||||
|
* <p>
|
||||||
|
* Locking: contacts read, subscriptions read.
|
||||||
|
*/
|
||||||
|
Set<GroupId> getSubscriptions(T txn, ContactId c) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the local transport details.
|
* Returns the local transport details.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -308,15 +319,17 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts read, messages read, messageStatuses write.
|
* Locking: contacts read, messages read, messageStatuses write.
|
||||||
*/
|
*/
|
||||||
void setStatus(T txn, ContactId c, MessageId m, Status s) throws DbException;
|
void setStatus(T txn, ContactId c, MessageId m, Status s)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the subscriptions for the given contact, replacing any existing
|
* Sets the subscriptions for the given contact, replacing any existing
|
||||||
* subscriptions.
|
* subscriptions unless the existing subscriptions have a newer timestamp.
|
||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts read, subscriptions write.
|
* Locking: contacts write, subscriptions write.
|
||||||
*/
|
*/
|
||||||
void setSubscriptions(T txn, ContactId c, Set<GroupId> subs) throws DbException;
|
void setSubscriptions(T txn, ContactId c, Set<GroupId> subs, long timestamp)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the local transport details, replacing any existing transport
|
* Sets the local transport details, replacing any existing transport
|
||||||
@@ -324,13 +337,15 @@ interface Database<T> {
|
|||||||
* <p>
|
* <p>
|
||||||
* Locking: transports write.
|
* Locking: transports write.
|
||||||
*/
|
*/
|
||||||
void setTransports(T txn, Map<String, String> transports) throws DbException;
|
void setTransports(T txn, Map<String, String> transports)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the transport details for the given contact, replacing any existing
|
* Sets the transport details for the given contact, replacing any existing
|
||||||
* transport details.
|
* transport details unless the existing details have a newer timestamp.
|
||||||
* <p>
|
* <p>
|
||||||
* Locking: contacts read, transports write.
|
* Locking: contacts write, transports write.
|
||||||
*/
|
*/
|
||||||
void setTransports(T txn, ContactId c, Map<String, String> transports) throws DbException;
|
void setTransports(T txn, ContactId c, Map<String, String> transports,
|
||||||
|
long timestamp) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ public class DatabaseModule extends AbstractModule {
|
|||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(Database.class).to(H2Database.class);
|
bind(Database.class).to(H2Database.class);
|
||||||
bind(DatabaseComponent.class).to(ReadWriteLockDatabaseComponent.class).in(Singleton.class);
|
bind(DatabaseComponent.class).to(
|
||||||
bind(Password.class).annotatedWith(DatabasePassword.class).toInstance(new Password() {
|
ReadWriteLockDatabaseComponent.class).in(Singleton.class);
|
||||||
|
bind(Password.class).annotatedWith(DatabasePassword.class).toInstance(
|
||||||
|
new Password() {
|
||||||
public char[] getPassword() {
|
public char[] getPassword() {
|
||||||
return "fixme fixme".toCharArray();
|
return "fixme fixme".toCharArray();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_LOCAL_SUBSCRIPTIONS =
|
private static final String CREATE_LOCAL_SUBSCRIPTIONS =
|
||||||
"CREATE TABLE localSubscriptions"
|
"CREATE TABLE localSubscriptions"
|
||||||
+ " (groupId XXXX NOT NULL,"
|
+ " (groupId HASH NOT NULL,"
|
||||||
+ " PRIMARY KEY (groupId))";
|
+ " PRIMARY KEY (groupId))";
|
||||||
|
|
||||||
private static final String CREATE_MESSAGES =
|
private static final String CREATE_MESSAGES =
|
||||||
"CREATE TABLE messages"
|
"CREATE TABLE messages"
|
||||||
+ " (messageId XXXX NOT NULL,"
|
+ " (messageId HASH NOT NULL,"
|
||||||
+ " parentId XXXX NOT NULL,"
|
+ " parentId HASH NOT NULL,"
|
||||||
+ " groupId XXXX NOT NULL,"
|
+ " groupId HASH NOT NULL,"
|
||||||
+ " authorId XXXX NOT NULL,"
|
+ " authorId HASH NOT NULL,"
|
||||||
+ " timestamp BIGINT NOT NULL,"
|
+ " timestamp BIGINT NOT NULL,"
|
||||||
+ " size INT NOT NULL,"
|
+ " size INT NOT NULL,"
|
||||||
+ " raw BLOB NOT NULL,"
|
+ " raw BLOB NOT NULL,"
|
||||||
@@ -70,11 +70,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_CONTACTS =
|
private static final String CREATE_CONTACTS =
|
||||||
"CREATE TABLE contacts"
|
"CREATE TABLE contacts"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
|
+ " subscriptionsTimestamp TIMESTAMP NOT NULL,"
|
||||||
|
+ " transportsTimestamp TIMESTAMP NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId))";
|
+ " PRIMARY KEY (contactId))";
|
||||||
|
|
||||||
private static final String CREATE_BATCHES_TO_ACK =
|
private static final String CREATE_BATCHES_TO_ACK =
|
||||||
"CREATE TABLE batchesToAck"
|
"CREATE TABLE batchesToAck"
|
||||||
+ " (batchId XXXX NOT NULL,"
|
+ " (batchId HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (batchId),"
|
+ " PRIMARY KEY (batchId),"
|
||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
@@ -83,16 +85,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
private static final String CREATE_CONTACT_SUBSCRIPTIONS =
|
private static final String CREATE_CONTACT_SUBSCRIPTIONS =
|
||||||
"CREATE TABLE contactSubscriptions"
|
"CREATE TABLE contactSubscriptions"
|
||||||
+ " (contactId INT NOT NULL,"
|
+ " (contactId INT NOT NULL,"
|
||||||
+ " groupId XXXX NOT NULL,"
|
+ " groupId HASH NOT NULL,"
|
||||||
+ " PRIMARY KEY (contactId, groupId),"
|
+ " PRIMARY KEY (contactId, groupId),"
|
||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
+ " ON DELETE CASCADE)";
|
+ " ON DELETE CASCADE)";
|
||||||
|
|
||||||
private static final String CREATE_OUTSTANDING_BATCHES =
|
private static final String CREATE_OUTSTANDING_BATCHES =
|
||||||
"CREATE TABLE outstandingBatches"
|
"CREATE TABLE outstandingBatches"
|
||||||
+ " (batchId XXXX NOT NULL,"
|
+ " (batchId HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " timestamp YYYY NOT NULL,"
|
+ " timestamp TIMESTAMP NOT NULL,"
|
||||||
+ " passover INT NOT NULL,"
|
+ " passover INT NOT NULL,"
|
||||||
+ " PRIMARY KEY (batchId),"
|
+ " PRIMARY KEY (batchId),"
|
||||||
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
|
||||||
@@ -100,9 +102,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_OUTSTANDING_MESSAGES =
|
private static final String CREATE_OUTSTANDING_MESSAGES =
|
||||||
"CREATE TABLE outstandingMessages"
|
"CREATE TABLE outstandingMessages"
|
||||||
+ " (batchId XXXX NOT NULL,"
|
+ " (batchId HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " messageId XXXX NOT NULL,"
|
+ " messageId HASH NOT NULL,"
|
||||||
+ " PRIMARY KEY (batchId, messageId),"
|
+ " PRIMARY KEY (batchId, messageId),"
|
||||||
+ " FOREIGN KEY (batchId) REFERENCES outstandingBatches (batchId)"
|
+ " FOREIGN KEY (batchId) REFERENCES outstandingBatches (batchId)"
|
||||||
+ " ON DELETE CASCADE,"
|
+ " ON DELETE CASCADE,"
|
||||||
@@ -117,13 +119,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
private static final String CREATE_RATINGS =
|
private static final String CREATE_RATINGS =
|
||||||
"CREATE TABLE ratings"
|
"CREATE TABLE ratings"
|
||||||
+ " (authorId XXXX NOT NULL,"
|
+ " (authorId HASH NOT NULL,"
|
||||||
+ " rating SMALLINT NOT NULL,"
|
+ " rating SMALLINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (authorId))";
|
+ " PRIMARY KEY (authorId))";
|
||||||
|
|
||||||
private static final String CREATE_STATUSES =
|
private static final String CREATE_STATUSES =
|
||||||
"CREATE TABLE statuses"
|
"CREATE TABLE statuses"
|
||||||
+ " (messageId XXXX NOT NULL,"
|
+ " (messageId HASH NOT NULL,"
|
||||||
+ " contactId INT NOT NULL,"
|
+ " contactId INT NOT NULL,"
|
||||||
+ " status SMALLINT NOT NULL,"
|
+ " status SMALLINT NOT NULL,"
|
||||||
+ " PRIMARY KEY (messageId, contactId),"
|
+ " PRIMARY KEY (messageId, contactId),"
|
||||||
@@ -157,7 +159,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
Logger.getLogger(JdbcDatabase.class.getName());
|
Logger.getLogger(JdbcDatabase.class.getName());
|
||||||
|
|
||||||
// Different database libraries use different names for certain types
|
// Different database libraries use different names for certain types
|
||||||
private final String hashType, bigIntType;
|
private final String hashType, timestampType;
|
||||||
private final LinkedList<Connection> connections =
|
private final LinkedList<Connection> connections =
|
||||||
new LinkedList<Connection>(); // Locking: self
|
new LinkedList<Connection>(); // Locking: self
|
||||||
|
|
||||||
@@ -166,9 +168,9 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
|
|
||||||
protected abstract Connection createConnection() throws SQLException;
|
protected abstract Connection createConnection() throws SQLException;
|
||||||
|
|
||||||
JdbcDatabase(String hashType, String bigIntType) {
|
JdbcDatabase(String hashType, String timestampType) {
|
||||||
this.hashType = hashType;
|
this.hashType = hashType;
|
||||||
this.bigIntType = bigIntType;
|
this.timestampType = timestampType;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void open(boolean resume, File dir, String driverClass)
|
protected void open(boolean resume, File dir, String driverClass)
|
||||||
@@ -254,7 +256,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String insertTypeNames(String s) {
|
private String insertTypeNames(String s) {
|
||||||
return s.replaceAll("XXXX", hashType).replaceAll("YYYY", bigIntType);
|
s = s.replaceAll("HASH", hashType);
|
||||||
|
return s.replaceAll("TIMESTAMP", timestampType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToClose(Connection c) {
|
private void tryToClose(Connection c) {
|
||||||
@@ -388,9 +391,13 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
rs.close();
|
rs.close();
|
||||||
ps.close();
|
ps.close();
|
||||||
// Create a new contact row
|
// Create a new contact row
|
||||||
sql = "INSERT INTO contacts (contactId) VALUES (?)";
|
sql = "INSERT INTO contacts"
|
||||||
|
+ " (contactId, subscriptionsTimestamp, transportsTimestamp)"
|
||||||
|
+ " 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(3, 0L);
|
||||||
int rowsAffected = ps.executeUpdate();
|
int rowsAffected = ps.executeUpdate();
|
||||||
assert rowsAffected == 1;
|
assert rowsAffected == 1;
|
||||||
ps.close();
|
ps.close();
|
||||||
@@ -955,6 +962,29 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Set<GroupId> getSubscriptions(Connection txn, ContactId c)
|
||||||
|
throws DbException {
|
||||||
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
|
try {
|
||||||
|
String sql = "SELECT groupId FROM contactSubscriptions"
|
||||||
|
+ " WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
Set<GroupId> ids = new HashSet<GroupId>();
|
||||||
|
while(rs.next()) ids.add(new GroupId(rs.getBytes(1)));
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
return ids;
|
||||||
|
} catch(SQLException e) {
|
||||||
|
tryToClose(rs);
|
||||||
|
tryToClose(ps);
|
||||||
|
tryToClose(txn);
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, String> getTransports(Connection txn)
|
public Map<String, String> getTransports(Connection txn)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
@@ -1279,12 +1309,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSubscriptions(Connection txn, ContactId c, Set<GroupId> subs)
|
public void setSubscriptions(Connection txn, ContactId c, Set<GroupId> subs,
|
||||||
throws DbException {
|
long timestamp) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
|
// Return if the timestamp isn't fresh
|
||||||
|
String sql = "SELECT subscriptionsTimestamp FROM contacts"
|
||||||
|
+ " WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean found = rs.next();
|
||||||
|
assert found;
|
||||||
|
long lastTimestamp = rs.getLong(1);
|
||||||
|
boolean more = rs.next();
|
||||||
|
assert !more;
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
if(lastTimestamp >= timestamp) return;
|
||||||
// Delete any existing subscriptions
|
// Delete any existing subscriptions
|
||||||
String sql = "DELETE FROM contactSubscriptions WHERE contactId = ?";
|
sql = "DELETE FROM contactSubscriptions WHERE contactId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
@@ -1304,6 +1349,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
assert rowsAffectedArray[i] == 1;
|
assert rowsAffectedArray[i] == 1;
|
||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
|
// Update the timestamp
|
||||||
|
sql = "UPDATE contacts SET subscriptionsTimestamp = ?"
|
||||||
|
+ " WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setLong(1, timestamp);
|
||||||
|
ps.setInt(2, c.getInt());
|
||||||
|
int rowsAffected = ps.executeUpdate();
|
||||||
|
assert rowsAffected == 1;
|
||||||
|
ps.close();
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
tryToClose(txn);
|
tryToClose(txn);
|
||||||
@@ -1345,11 +1399,26 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setTransports(Connection txn, ContactId c,
|
public void setTransports(Connection txn, ContactId c,
|
||||||
Map<String, String> transports) throws DbException {
|
Map<String, String> transports, long timestamp) throws DbException {
|
||||||
PreparedStatement ps = null;
|
PreparedStatement ps = null;
|
||||||
|
ResultSet rs = null;
|
||||||
try {
|
try {
|
||||||
|
// Return if the timestamp isn't fresh
|
||||||
|
String sql = "SELECT transportsTimestamp FROM contacts"
|
||||||
|
+ " WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setInt(1, c.getInt());
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
boolean found = rs.next();
|
||||||
|
assert found;
|
||||||
|
long lastTimestamp = rs.getLong(1);
|
||||||
|
boolean more = rs.next();
|
||||||
|
assert !more;
|
||||||
|
rs.close();
|
||||||
|
ps.close();
|
||||||
|
if(lastTimestamp >= timestamp) return;
|
||||||
// Delete any existing transports
|
// Delete any existing transports
|
||||||
String sql = "DELETE FROM contactTransports WHERE contactId = ?";
|
sql = "DELETE FROM contactTransports WHERE contactId = ?";
|
||||||
ps = txn.prepareStatement(sql);
|
ps = txn.prepareStatement(sql);
|
||||||
ps.setInt(1, c.getInt());
|
ps.setInt(1, c.getInt());
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
@@ -1372,6 +1441,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
}
|
}
|
||||||
ps.close();
|
ps.close();
|
||||||
}
|
}
|
||||||
|
// Update the timestamp
|
||||||
|
sql = "UPDATE contacts SET transportsTimestamp = ?"
|
||||||
|
+ " WHERE contactId = ?";
|
||||||
|
ps = txn.prepareStatement(sql);
|
||||||
|
ps.setLong(1, timestamp);
|
||||||
|
ps.setInt(2, c.getInt());
|
||||||
|
int rowsAffected = ps.executeUpdate();
|
||||||
|
assert rowsAffected == 1;
|
||||||
|
ps.close();
|
||||||
} catch(SQLException e) {
|
} catch(SQLException e) {
|
||||||
tryToClose(ps);
|
tryToClose(ps);
|
||||||
tryToClose(txn);
|
tryToClose(txn);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package net.sf.briar.db;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.SignatureException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@@ -192,10 +193,19 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||||
Set<BatchId> acks;
|
Set<BatchId> acks = generateAcks(c);
|
||||||
Set<GroupId> subs;
|
Set<GroupId> subs = generateSubscriptions(c);
|
||||||
Map<String, String> transports;
|
Map<String, String> transports = generateTransports(c);
|
||||||
// Add acks
|
// Add the header to the bundle
|
||||||
|
b.addHeader(acks, subs, transports);
|
||||||
|
// Add as many messages as possible to the bundle
|
||||||
|
while(generateBatch(c, b));
|
||||||
|
b.finish();
|
||||||
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<BatchId> generateAcks(ContactId c) throws DbException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -203,10 +213,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
acks = db.removeBatchesToAck(txn, c);
|
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + acks.size() + " acks");
|
LOG.fine("Added " + acks.size() + " acks");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
|
return acks;
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -217,7 +228,9 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
// Add subscriptions
|
}
|
||||||
|
|
||||||
|
private Set<GroupId> generateSubscriptions(ContactId c) throws DbException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -225,10 +238,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
subs = db.getSubscriptions(txn);
|
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
|
return subs;
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -239,7 +253,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
// Add transport details
|
}
|
||||||
|
|
||||||
|
private Map<String, String> generateTransports(ContactId c)
|
||||||
|
throws DbException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -247,10 +264,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
try {
|
try {
|
||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
transports = db.getTransports(txn);
|
Map<String, String> transports = db.getTransports(txn);
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Added " + transports.size() + " transports");
|
LOG.fine("Added " + transports.size() + " transports");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
|
return transports;
|
||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -261,17 +279,10 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
// Add the header to the bundle
|
|
||||||
b.addHeader(acks, subs, transports);
|
|
||||||
// Add as many messages as possible to the bundle
|
|
||||||
while(fillBatch(c, b));
|
|
||||||
b.finish();
|
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
|
||||||
System.gc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
private boolean generateBatch(ContactId c, BundleWriter b)
|
||||||
IOException, GeneralSecurityException {
|
throws DbException, IOException, GeneralSecurityException {
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -309,7 +320,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
} catch(GeneralSecurityException e) {
|
} catch(SignatureException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -435,12 +446,29 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||||
Header h;
|
Header h = b.getHeader();
|
||||||
|
receiveAcks(c, h);
|
||||||
|
receiveSubscriptions(c, h);
|
||||||
|
receiveTransports(c, h);
|
||||||
|
// Store the messages
|
||||||
|
int batches = 0;
|
||||||
|
Batch batch = null;
|
||||||
|
while((batch = b.getNextBatch()) != null) {
|
||||||
|
receiveBatch(c, batch);
|
||||||
|
batches++;
|
||||||
|
}
|
||||||
|
if(LOG.isLoggable(Level.FINE))
|
||||||
|
LOG.fine("Received " + batches + " batches");
|
||||||
|
b.finish();
|
||||||
|
findLostBatches(c);
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveAcks(ContactId c, Header h) throws DbException {
|
||||||
// Mark all messages in acked batches as seen
|
// Mark all messages in acked batches as seen
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
h = b.getHeader();
|
|
||||||
messageLock.readLock().lock();
|
messageLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
messageStatusLock.writeLock().lock();
|
messageStatusLock.writeLock().lock();
|
||||||
@@ -467,8 +495,12 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveSubscriptions(ContactId c, Header h)
|
||||||
|
throws DbException {
|
||||||
// Update the contact's subscriptions
|
// Update the contact's subscriptions
|
||||||
contactLock.readLock().lock();
|
contactLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
subscriptionLock.writeLock().lock();
|
subscriptionLock.writeLock().lock();
|
||||||
@@ -476,7 +508,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<GroupId> subs = h.getSubscriptions();
|
Set<GroupId> subs = h.getSubscriptions();
|
||||||
db.setSubscriptions(txn, c, subs);
|
db.setSubscriptions(txn, c, subs, h.getTimestamp());
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + subs.size() + " subscriptions");
|
LOG.fine("Received " + subs.size() + " subscriptions");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -488,10 +520,13 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
subscriptionLock.writeLock().unlock();
|
subscriptionLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveTransports(ContactId c, Header h) throws DbException {
|
||||||
// Update the contact's transport details
|
// Update the contact's transport details
|
||||||
contactLock.readLock().lock();
|
contactLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
transportLock.writeLock().lock();
|
transportLock.writeLock().lock();
|
||||||
@@ -499,7 +534,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Map<String, String> transports = h.getTransports();
|
Map<String, String> transports = h.getTransports();
|
||||||
db.setTransports(txn, c, transports);
|
db.setTransports(txn, c, transports, h.getTimestamp());
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + transports.size()
|
LOG.fine("Received " + transports.size()
|
||||||
+ " transports");
|
+ " transports");
|
||||||
@@ -512,23 +547,11 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
transportLock.writeLock().unlock();
|
transportLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
contactLock.readLock().unlock();
|
contactLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
// Store the messages
|
|
||||||
int batches = 0;
|
|
||||||
Batch batch = null;
|
|
||||||
while((batch = b.getNextBatch()) != null) {
|
|
||||||
storeBatch(c, batch);
|
|
||||||
batches++;
|
|
||||||
}
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
|
||||||
LOG.fine("Received " + batches + " batches");
|
|
||||||
b.finish();
|
|
||||||
findLostBatches(c);
|
|
||||||
System.gc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeBatch(ContactId c, Batch b) throws DbException {
|
private void receiveBatch(ContactId c, Batch b) throws DbException {
|
||||||
waitForPermissionToWrite();
|
waitForPermissionToWrite();
|
||||||
contactLock.readLock().lock();
|
contactLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
@@ -687,29 +710,6 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransports(ContactId c, Map<String, String> transports)
|
|
||||||
throws DbException {
|
|
||||||
contactLock.readLock().lock();
|
|
||||||
try {
|
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
|
||||||
transportLock.writeLock().lock();
|
|
||||||
try {
|
|
||||||
Txn txn = db.startTransaction();
|
|
||||||
try {
|
|
||||||
db.setTransports(txn, c, transports);
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
} catch(DbException e) {
|
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
transportLock.writeLock().unlock();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
contactLock.readLock().unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subscribe(GroupId g) throws DbException {
|
public void subscribe(GroupId g) throws DbException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g);
|
||||||
subscriptionLock.writeLock().lock();
|
subscriptionLock.writeLock().lock();
|
||||||
|
|||||||
@@ -146,68 +146,78 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
public void generateBundle(ContactId c, BundleWriter b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Generating bundle for " + c);
|
||||||
Set<BatchId> acks;
|
Set<BatchId> acks = generateAcks(c);
|
||||||
Set<GroupId> subs;
|
Set<GroupId> subs = generateSubscriptions(c);
|
||||||
Map<String, String> transports;
|
Map<String, String> transports = generateTransports(c);
|
||||||
// Add acks
|
|
||||||
synchronized(contactLock) {
|
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
|
||||||
synchronized(messageStatusLock) {
|
|
||||||
Txn txn = db.startTransaction();
|
|
||||||
try {
|
|
||||||
acks = db.removeBatchesToAck(txn, c);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
|
||||||
LOG.fine("Added " + acks.size() + " acks");
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
} catch(DbException e) {
|
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add subscriptions
|
|
||||||
synchronized(contactLock) {
|
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
|
||||||
synchronized(subscriptionLock) {
|
|
||||||
Txn txn = db.startTransaction();
|
|
||||||
try {
|
|
||||||
subs = db.getSubscriptions(txn);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
|
||||||
LOG.fine("Added " + subs.size() + " subscriptions");
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
} catch(DbException e) {
|
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add transport details
|
|
||||||
synchronized(contactLock) {
|
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
|
||||||
synchronized(transportLock) {
|
|
||||||
Txn txn = db.startTransaction();
|
|
||||||
try {
|
|
||||||
transports = db.getTransports(txn);
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
|
||||||
LOG.fine("Added " + transports.size() + " transports");
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
} catch(DbException e) {
|
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add the header to the bundle
|
// Add the header to the bundle
|
||||||
b.addHeader(acks, subs, transports);
|
b.addHeader(acks, subs, transports);
|
||||||
// Add as many messages as possible to the bundle
|
// Add as many messages as possible to the bundle
|
||||||
while(fillBatch(c, b));
|
while(generateBatch(c, b));
|
||||||
b.finish();
|
b.finish();
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Bundle generated");
|
||||||
System.gc();
|
System.gc();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fillBatch(ContactId c, BundleWriter b) throws DbException,
|
private Set<BatchId> generateAcks(ContactId c) throws DbException {
|
||||||
IOException, GeneralSecurityException {
|
synchronized(contactLock) {
|
||||||
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
|
synchronized(messageStatusLock) {
|
||||||
|
Txn txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
Set<BatchId> acks = db.removeBatchesToAck(txn, c);
|
||||||
|
if(LOG.isLoggable(Level.FINE))
|
||||||
|
LOG.fine("Added " + acks.size() + " acks");
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
return acks;
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<GroupId> generateSubscriptions(ContactId c) throws DbException {
|
||||||
|
synchronized(contactLock) {
|
||||||
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
|
synchronized(subscriptionLock) {
|
||||||
|
Txn txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
Set<GroupId> subs = db.getSubscriptions(txn);
|
||||||
|
if(LOG.isLoggable(Level.FINE))
|
||||||
|
LOG.fine("Added " + subs.size() + " subscriptions");
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
return subs;
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> generateTransports(ContactId c)
|
||||||
|
throws DbException {
|
||||||
|
synchronized(contactLock) {
|
||||||
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
|
synchronized(transportLock) {
|
||||||
|
Txn txn = db.startTransaction();
|
||||||
|
try {
|
||||||
|
Map<String, String> transports = db.getTransports(txn);
|
||||||
|
if(LOG.isLoggable(Level.FINE))
|
||||||
|
LOG.fine("Added " + transports.size() + " transports");
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
return transports;
|
||||||
|
} catch(DbException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean generateBatch(ContactId c, BundleWriter b)
|
||||||
|
throws DbException, IOException, GeneralSecurityException {
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
synchronized(messageLock) {
|
synchronized(messageLock) {
|
||||||
@@ -242,6 +252,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
} catch(DbException e) {
|
} catch(DbException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
|
} catch(IOException e) {
|
||||||
|
db.abortTransaction(txn);
|
||||||
|
throw e;
|
||||||
} catch(SignatureException e) {
|
} catch(SignatureException e) {
|
||||||
db.abortTransaction(txn);
|
db.abortTransaction(txn);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -327,11 +340,28 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
public void receiveBundle(ContactId c, BundleReader b) throws DbException,
|
||||||
IOException, GeneralSecurityException {
|
IOException, GeneralSecurityException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Received bundle from " + c);
|
||||||
Header h;
|
Header h = b.getHeader();
|
||||||
|
receiveAcks(c, h);
|
||||||
|
receiveSubscriptions(c, h);
|
||||||
|
receiveTransports(c, h);
|
||||||
|
// Store the messages
|
||||||
|
int batches = 0;
|
||||||
|
Batch batch = null;
|
||||||
|
while((batch = b.getNextBatch()) != null) {
|
||||||
|
receiveBatch(c, batch);
|
||||||
|
batches++;
|
||||||
|
}
|
||||||
|
if(LOG.isLoggable(Level.FINE))
|
||||||
|
LOG.fine("Received " + batches + " batches");
|
||||||
|
b.finish();
|
||||||
|
findLostBatches(c);
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveAcks(ContactId c, Header h) throws DbException {
|
||||||
// Mark all messages in acked batches as seen
|
// Mark all messages in acked batches as seen
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
h = b.getHeader();
|
|
||||||
synchronized(messageLock) {
|
synchronized(messageLock) {
|
||||||
synchronized(messageStatusLock) {
|
synchronized(messageStatusLock) {
|
||||||
Set<BatchId> acks = h.getAcks();
|
Set<BatchId> acks = h.getAcks();
|
||||||
@@ -350,6 +380,10 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveSubscriptions(ContactId c, Header h)
|
||||||
|
throws DbException {
|
||||||
// Update the contact's subscriptions
|
// Update the contact's subscriptions
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -357,7 +391,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Set<GroupId> subs = h.getSubscriptions();
|
Set<GroupId> subs = h.getSubscriptions();
|
||||||
db.setSubscriptions(txn, c, subs);
|
db.setSubscriptions(txn, c, subs, h.getTimestamp());
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + subs.size() + " subscriptions");
|
LOG.fine("Received " + subs.size() + " subscriptions");
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -367,6 +401,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void receiveTransports(ContactId c, Header h) throws DbException {
|
||||||
// Update the contact's transport details
|
// Update the contact's transport details
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -374,7 +411,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
Txn txn = db.startTransaction();
|
Txn txn = db.startTransaction();
|
||||||
try {
|
try {
|
||||||
Map<String, String> transports = h.getTransports();
|
Map<String, String> transports = h.getTransports();
|
||||||
db.setTransports(txn, c, transports);
|
db.setTransports(txn, c, transports, h.getTimestamp());
|
||||||
if(LOG.isLoggable(Level.FINE))
|
if(LOG.isLoggable(Level.FINE))
|
||||||
LOG.fine("Received " + transports.size()
|
LOG.fine("Received " + transports.size()
|
||||||
+ " transports");
|
+ " transports");
|
||||||
@@ -385,21 +422,9 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Store the messages
|
|
||||||
int batches = 0;
|
|
||||||
Batch batch = null;
|
|
||||||
while((batch = b.getNextBatch()) != null) {
|
|
||||||
storeBatch(c, batch);
|
|
||||||
batches++;
|
|
||||||
}
|
|
||||||
if(LOG.isLoggable(Level.FINE))
|
|
||||||
LOG.fine("Received " + batches + " batches");
|
|
||||||
b.finish();
|
|
||||||
findLostBatches(c);
|
|
||||||
System.gc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeBatch(ContactId c, Batch b) throws DbException {
|
private void receiveBatch(ContactId c, Batch b) throws DbException {
|
||||||
waitForPermissionToWrite();
|
waitForPermissionToWrite();
|
||||||
synchronized(contactLock) {
|
synchronized(contactLock) {
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
if(!containsContact(c)) throw new NoSuchContactException();
|
||||||
@@ -511,23 +536,6 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTransports(ContactId c, Map<String, String> transports)
|
|
||||||
throws DbException {
|
|
||||||
synchronized(contactLock) {
|
|
||||||
if(!containsContact(c)) throw new NoSuchContactException();
|
|
||||||
synchronized(transportLock) {
|
|
||||||
Txn txn = db.startTransaction();
|
|
||||||
try {
|
|
||||||
db.setTransports(txn, c, transports);
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
} catch(DbException e) {
|
|
||||||
db.abortTransaction(txn);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subscribe(GroupId g) throws DbException {
|
public void subscribe(GroupId g) throws DbException {
|
||||||
if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g);
|
if(LOG.isLoggable(Level.FINE)) LOG.fine("Subscribing to " + g);
|
||||||
synchronized(subscriptionLock) {
|
synchronized(subscriptionLock) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class InvitationModule extends AbstractModule {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bind(InvitationWorkerFactory.class).to(InvitationWorkerFactoryImpl.class);
|
bind(InvitationWorkerFactory.class).to(
|
||||||
|
InvitationWorkerFactoryImpl.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,8 +71,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleCalls() throws DbException {
|
public void testSimpleCalls() throws DbException {
|
||||||
final Map<String, String> transports1 =
|
|
||||||
Collections.singletonMap("foo", "bar baz");
|
|
||||||
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);
|
||||||
@@ -98,10 +96,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
oneOf(database).getTransports(txn, contactId);
|
oneOf(database).getTransports(txn, contactId);
|
||||||
will(returnValue(transports));
|
will(returnValue(transports));
|
||||||
// setTransports(contactId, transports1)
|
|
||||||
oneOf(database).containsContact(txn, contactId);
|
|
||||||
will(returnValue(true));
|
|
||||||
oneOf(database).setTransports(txn, contactId, transports1);
|
|
||||||
// subscribe(groupId)
|
// subscribe(groupId)
|
||||||
oneOf(database).addSubscription(txn, groupId);
|
oneOf(database).addSubscription(txn, groupId);
|
||||||
// getSubscriptions()
|
// getSubscriptions()
|
||||||
@@ -122,7 +116,6 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
assertEquals(contactId, db.addContact(transports));
|
assertEquals(contactId, db.addContact(transports));
|
||||||
assertEquals(contacts, db.getContacts());
|
assertEquals(contacts, db.getContacts());
|
||||||
assertEquals(transports, db.getTransports(contactId));
|
assertEquals(transports, db.getTransports(contactId));
|
||||||
db.setTransports(contactId, transports1);
|
|
||||||
db.subscribe(groupId);
|
db.subscribe(groupId);
|
||||||
assertEquals(subs, db.getSubscriptions());
|
assertEquals(subs, db.getSubscriptions());
|
||||||
db.unsubscribe(groupId);
|
db.unsubscribe(groupId);
|
||||||
@@ -509,7 +502,10 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
final Database<Object> database = context.mock(Database.class);
|
final Database<Object> database = context.mock(Database.class);
|
||||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||||
final BundleReader bundleReader = context.mock(BundleReader.class);
|
final BundleReader bundleReader = context.mock(BundleReader.class);
|
||||||
|
final Header header = context.mock(Header.class);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(bundleReader).getHeader();
|
||||||
|
will(returnValue(header));
|
||||||
// Check that the contact is still in the DB
|
// Check that the contact is still in the DB
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -552,11 +548,16 @@ public abstract class DatabaseComponentTest extends TestCase {
|
|||||||
// Subscriptions
|
// Subscriptions
|
||||||
oneOf(header).getSubscriptions();
|
oneOf(header).getSubscriptions();
|
||||||
will(returnValue(subs));
|
will(returnValue(subs));
|
||||||
oneOf(database).setSubscriptions(txn, contactId, subs);
|
oneOf(header).getTimestamp();
|
||||||
|
will(returnValue(timestamp));
|
||||||
|
oneOf(database).setSubscriptions(txn, contactId, subs, timestamp);
|
||||||
// Transports
|
// Transports
|
||||||
oneOf(header).getTransports();
|
oneOf(header).getTransports();
|
||||||
will(returnValue(transports));
|
will(returnValue(transports));
|
||||||
oneOf(database).setTransports(txn, contactId, transports);
|
oneOf(header).getTimestamp();
|
||||||
|
will(returnValue(timestamp));
|
||||||
|
oneOf(database).setTransports(txn, contactId, transports,
|
||||||
|
timestamp);
|
||||||
// Batches
|
// Batches
|
||||||
oneOf(bundleReader).getNextBatch();
|
oneOf(bundleReader).getNextBatch();
|
||||||
will(returnValue(batch));
|
will(returnValue(batch));
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertEquals(contactId, db.addContact(txn, null));
|
assertEquals(contactId, db.addContact(txn, null));
|
||||||
db.addSubscription(txn, groupId);
|
db.addSubscription(txn, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
db.addMessage(txn, message);
|
db.addMessage(txn, message);
|
||||||
db.setStatus(txn, contactId, messageId, Status.NEW);
|
db.setStatus(txn, contactId, messageId, Status.NEW);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -234,7 +234,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertEquals(contactId, db.addContact(txn, null));
|
assertEquals(contactId, db.addContact(txn, null));
|
||||||
db.addSubscription(txn, groupId);
|
db.addSubscription(txn, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
db.addMessage(txn, message);
|
db.addMessage(txn, message);
|
||||||
db.setSendability(txn, messageId, 1);
|
db.setSendability(txn, messageId, 1);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -293,7 +293,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
|
|
||||||
// The contact subscribing should make the message sendable
|
// The contact subscribing should make the message sendable
|
||||||
txn = db.startTransaction();
|
txn = db.startTransaction();
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||||
assertTrue(it.hasNext());
|
assertTrue(it.hasNext());
|
||||||
assertEquals(messageId, it.next());
|
assertEquals(messageId, it.next());
|
||||||
@@ -301,7 +301,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
|
|
||||||
// The contact unsubscribing should make the message unsendable
|
// The contact unsubscribing should make the message unsendable
|
||||||
txn = db.startTransaction();
|
txn = db.startTransaction();
|
||||||
db.setSubscriptions(txn, contactId, Collections.<GroupId>emptySet());
|
db.setSubscriptions(txn, contactId, Collections.<GroupId>emptySet(), 2);
|
||||||
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
|
||||||
assertFalse(it.hasNext());
|
assertFalse(it.hasNext());
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
@@ -317,7 +317,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertEquals(contactId, db.addContact(txn, null));
|
assertEquals(contactId, db.addContact(txn, null));
|
||||||
db.addSubscription(txn, groupId);
|
db.addSubscription(txn, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
db.addMessage(txn, message);
|
db.addMessage(txn, message);
|
||||||
db.setSendability(txn, messageId, 1);
|
db.setSendability(txn, messageId, 1);
|
||||||
db.setStatus(txn, contactId, messageId, Status.NEW);
|
db.setStatus(txn, contactId, messageId, Status.NEW);
|
||||||
@@ -377,7 +377,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertEquals(contactId, db.addContact(txn, null));
|
assertEquals(contactId, db.addContact(txn, null));
|
||||||
db.addSubscription(txn, groupId);
|
db.addSubscription(txn, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
db.addMessage(txn, message);
|
db.addMessage(txn, message);
|
||||||
db.setSendability(txn, messageId, 1);
|
db.setSendability(txn, messageId, 1);
|
||||||
db.setStatus(txn, contactId, messageId, Status.NEW);
|
db.setStatus(txn, contactId, messageId, Status.NEW);
|
||||||
@@ -417,7 +417,7 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertEquals(contactId, db.addContact(txn, null));
|
assertEquals(contactId, db.addContact(txn, null));
|
||||||
db.addSubscription(txn, groupId);
|
db.addSubscription(txn, groupId);
|
||||||
db.setSubscriptions(txn, contactId, Collections.singleton(groupId));
|
db.setSubscriptions(txn, contactId, Collections.singleton(groupId), 1);
|
||||||
db.addMessage(txn, message);
|
db.addMessage(txn, message);
|
||||||
db.setSendability(txn, messageId, 1);
|
db.setSendability(txn, messageId, 1);
|
||||||
db.setStatus(txn, contactId, messageId, Status.NEW);
|
db.setStatus(txn, contactId, messageId, Status.NEW);
|
||||||
@@ -740,10 +740,10 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
transports = new TreeMap<String, String>();
|
transports = new TreeMap<String, String>();
|
||||||
transports.put("foo", "bar baz");
|
transports.put("foo", "bar baz");
|
||||||
transports.put("bar", "baz quux");
|
transports.put("bar", "baz quux");
|
||||||
db.setTransports(txn, contactId, transports);
|
db.setTransports(txn, contactId, transports, 1);
|
||||||
assertEquals(transports, db.getTransports(txn, contactId));
|
assertEquals(transports, db.getTransports(txn, contactId));
|
||||||
// Remove the transport details
|
// Remove the transport details
|
||||||
db.setTransports(txn, contactId, null);
|
db.setTransports(txn, contactId, null, 2);
|
||||||
assertEquals(Collections.emptyMap(), db.getTransports(txn, contactId));
|
assertEquals(Collections.emptyMap(), db.getTransports(txn, contactId));
|
||||||
// Set the local transport details
|
// Set the local transport details
|
||||||
db.setTransports(txn, transports);
|
db.setTransports(txn, transports);
|
||||||
@@ -756,6 +756,85 @@ public class H2DatabaseTest extends TestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransportsNotUpdatedIfTimestampIsOld() throws DbException {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
|
||||||
|
// Add a contact with some transport details
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
Map<String, String> transports = Collections.singletonMap("foo", "bar");
|
||||||
|
assertEquals(contactId, db.addContact(txn, transports));
|
||||||
|
assertEquals(transports, db.getTransports(txn, contactId));
|
||||||
|
// Replace the transport details using a timestamp of 2
|
||||||
|
Map<String, String> transports1 = new TreeMap<String, String>();
|
||||||
|
transports1.put("foo", "bar baz");
|
||||||
|
transports1.put("bar", "baz quux");
|
||||||
|
db.setTransports(txn, contactId, transports1, 2);
|
||||||
|
assertEquals(transports1, db.getTransports(txn, contactId));
|
||||||
|
// Try to replace the transport details using a timestamp of 1
|
||||||
|
Map<String, String> transports2 = new TreeMap<String, String>();
|
||||||
|
transports2.put("bar", "baz");
|
||||||
|
transports2.put("quux", "fnord");
|
||||||
|
db.setTransports(txn, contactId, transports2, 1);
|
||||||
|
// The old transports should still be there
|
||||||
|
assertEquals(transports1, db.getTransports(txn, contactId));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateSubscriptions() throws DbException {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
|
||||||
|
// Add a contact
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
Map<String, String> transports = Collections.emptyMap();
|
||||||
|
assertEquals(contactId, db.addContact(txn, transports));
|
||||||
|
// Add some subscriptions
|
||||||
|
Set<GroupId> subs = new HashSet<GroupId>();
|
||||||
|
subs.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
subs.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
db.setSubscriptions(txn, contactId, subs, 1);
|
||||||
|
assertEquals(subs, db.getSubscriptions(txn, contactId));
|
||||||
|
// Update the subscriptions
|
||||||
|
Set<GroupId> subs1 = new HashSet<GroupId>();
|
||||||
|
subs1.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
subs1.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
db.setSubscriptions(txn, contactId, subs1, 2);
|
||||||
|
assertEquals(subs1, db.getSubscriptions(txn, contactId));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionsNotUpdatedIfTimestampIsOld()
|
||||||
|
throws DbException {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
|
||||||
|
// Add a contact
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
Map<String, String> transports = Collections.emptyMap();
|
||||||
|
assertEquals(contactId, db.addContact(txn, transports));
|
||||||
|
// Add some subscriptions
|
||||||
|
Set<GroupId> subs = new HashSet<GroupId>();
|
||||||
|
subs.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
subs.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
db.setSubscriptions(txn, contactId, subs, 2);
|
||||||
|
assertEquals(subs, db.getSubscriptions(txn, contactId));
|
||||||
|
// Try to update the subscriptions using a timestamp of 1
|
||||||
|
Set<GroupId> subs1 = new HashSet<GroupId>();
|
||||||
|
subs1.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
subs1.add(new GroupId(TestUtils.getRandomId()));
|
||||||
|
db.setSubscriptions(txn, contactId, subs1, 1);
|
||||||
|
// The old subscriptions should still be there
|
||||||
|
assertEquals(subs, db.getSubscriptions(txn, contactId));
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
private Database<Connection> open(boolean resume) throws DbException {
|
private Database<Connection> open(boolean resume) throws DbException {
|
||||||
final char[] passwordArray = passwordString.toCharArray();
|
final char[] passwordArray = passwordString.toCharArray();
|
||||||
Mockery context = new Mockery();
|
Mockery context = new Mockery();
|
||||||
|
|||||||
Reference in New Issue
Block a user