diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index f01b2c7799b6f015b177f0e57f42473055547bd2..ca5c732a9beb089047fcb8084e2cf0621961a0df 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -104,12 +104,13 @@ interface Database<T> { void addMessageToAck(T txn, ContactId c, MessageId m) throws DbException; /** - * Records a collection of sent messages as needing to be acknowledged. + * Records the given messages as needing to be acknowledged by the given + * expiry time. * <p> * Locking: contact read, message write. */ - void addOutstandingMessages(T txn, ContactId c, Collection<MessageId> sent) - throws DbException; + void addOutstandingMessages(T txn, ContactId c, Collection<MessageId> sent, + long expiry) throws DbException; /** * Stores the given message, or returns false if the message is already in @@ -128,6 +129,15 @@ interface Database<T> { void addSecrets(T txn, Collection<TemporarySecret> secrets) throws DbException; + /** + * Initialises the status (seen or unseen) of the given message with + * respect to the given contact. + * <p> + * Locking: contact read, message write. + */ + void addStatus(T txn, ContactId c, MessageId m, boolean seen) + throws DbException; + /** * Subscribes to the given group. * <p> @@ -614,14 +624,6 @@ interface Database<T> { boolean setStarredFlag(T txn, MessageId m, boolean starred) throws DbException; - /** - * Sets the status of the given message with respect to the given contact. - * <p> - * Locking: contact read, message write. - */ - void setStatus(T txn, ContactId c, MessageId m, Status s) - throws DbException; - /** * If the database contains the given message and it belongs to a group * that is visible to the given contact, marks the message as seen by the diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index 37c0c40809b942109dc80698dc7fe14f159a8732..4b8ff546e84ecd37ff4c24477457ef64272858f0 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -7,8 +7,6 @@ import static net.sf.briar.db.DatabaseConstants.CRITICAL_FREE_SPACE; import static net.sf.briar.db.DatabaseConstants.MAX_BYTES_BETWEEN_SPACE_CHECKS; import static net.sf.briar.db.DatabaseConstants.MAX_MS_BETWEEN_SPACE_CHECKS; import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE; -import static net.sf.briar.db.Status.NEW; -import static net.sf.briar.db.Status.SEEN; import java.io.IOException; import java.util.ArrayList; @@ -277,11 +275,11 @@ DatabaseCleaner.Callback { boolean stored = db.addGroupMessage(txn, m); // Mark the message as seen by the sender MessageId id = m.getId(); - if(sender != null) db.setStatus(txn, sender, id, SEEN); + if(sender != null) db.addStatus(txn, sender, id, true); if(stored) { // Mark the message as unseen by other contacts for(ContactId c : db.getContacts(txn)) { - if(!c.equals(sender)) db.setStatus(txn, c, id, NEW); + if(!c.equals(sender)) db.addStatus(txn, c, id, false); } // Calculate and store the message's sendability int sendability = calculateSendability(txn, m); @@ -441,8 +439,8 @@ DatabaseCleaner.Callback { if(m.getAuthor() != null) throw new IllegalArgumentException(); if(!db.addPrivateMessage(txn, m, c)) return false; MessageId id = m.getId(); - if(incoming) db.setStatus(txn, c, id, SEEN); - else db.setStatus(txn, c, id, NEW); + if(incoming) db.addStatus(txn, c, id, true); + else db.addStatus(txn, c, id, false); // Count the bytes stored synchronized(spaceLock) { bytesStoredSinceLastCheck += m.getSerialised().length; @@ -526,7 +524,8 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - db.addOutstandingMessages(txn, c, ids); + // FIXME: Calculate the expiry time + db.addOutstandingMessages(txn, c, ids, Long.MAX_VALUE); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -585,7 +584,8 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - db.addOutstandingMessages(txn, c, ids); + // FIXME: Calculate the expiry time + db.addOutstandingMessages(txn, c, ids, Long.MAX_VALUE); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); diff --git a/briar-core/src/net/sf/briar/db/DatabaseModule.java b/briar-core/src/net/sf/briar/db/DatabaseModule.java index ce5cdcf6bc2a13815921eb30f95520721b5f6087..fccc1e44484377b595a6f6367c9112474fdab900 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseModule.java +++ b/briar-core/src/net/sf/briar/db/DatabaseModule.java @@ -4,6 +4,7 @@ import java.sql.Connection; import java.util.concurrent.Executor; import net.sf.briar.api.clock.Clock; +import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.db.DatabaseExecutor; @@ -41,7 +42,7 @@ public class DatabaseModule extends AbstractModule { @Provides Database<Connection> getDatabase(DatabaseConfig config) { - return new H2Database(config); + return new H2Database(config, new SystemClock()); } @Provides @Singleton diff --git a/briar-core/src/net/sf/briar/db/H2Database.java b/briar-core/src/net/sf/briar/db/H2Database.java index eeaf79069ad2bf42191360a61c268e80189d26fb..6a396bddd0575aebb9bd5ba56a9eefd28d07a804 100644 --- a/briar-core/src/net/sf/briar/db/H2Database.java +++ b/briar-core/src/net/sf/briar/db/H2Database.java @@ -8,6 +8,7 @@ import java.sql.SQLException; import java.util.Arrays; import java.util.Properties; +import net.sf.briar.api.clock.Clock; import net.sf.briar.api.crypto.Password; import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.db.DbException; @@ -29,8 +30,8 @@ class H2Database extends JdbcDatabase { private final long maxSize; @Inject - H2Database(DatabaseConfig config) { - super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE); + H2Database(DatabaseConfig config, Clock clock) { + super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock); home = new File(config.getDataDirectory(), "db"); url = "jdbc:h2:split:" + home.getPath() + ";CIPHER=AES;MULTI_THREADED=1;DB_CLOSE_ON_EXIT=false"; diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index c2494b3e9500d26921684d56d28905e7904db25c..596b7a8632100047227a1fdba213be7fc2f7dbc7 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -5,9 +5,6 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static net.sf.briar.api.Rating.UNRATED; import static net.sf.briar.db.DatabaseConstants.RETENTION_MODULUS; -import static net.sf.briar.db.Status.NEW; -import static net.sf.briar.db.Status.SEEN; -import static net.sf.briar.db.Status.SENT; import java.io.File; import java.io.FileNotFoundException; @@ -31,6 +28,7 @@ import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.clock.Clock; import net.sf.briar.api.db.DbClosedException; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; @@ -157,7 +155,9 @@ abstract class JdbcDatabase implements Database<Connection> { "CREATE TABLE statuses" + " (messageId HASH NOT NULL," + " contactId INT NOT NULL," - + " status SMALLINT NOT NULL," + + " seen BOOLEAN NOT NULL," + + " transmissionCount INT NOT NULL," + + " expiry BIGINT NOT NULL," + " PRIMARY KEY (messageId, contactId)," + " FOREIGN KEY (messageId)" + " REFERENCES messages (messageId)" @@ -298,6 +298,7 @@ abstract class JdbcDatabase implements Database<Connection> { // Different database libraries use different names for certain types private final String hashType, binaryType, counterType, secretType; + private final Clock clock; private final LinkedList<Connection> connections = new LinkedList<Connection>(); // Locking: self @@ -308,11 +309,12 @@ abstract class JdbcDatabase implements Database<Connection> { protected abstract Connection createConnection() throws SQLException; JdbcDatabase(String hashType, String binaryType, String counterType, - String secretType) { + String secretType, Clock clock) { this.hashType = hashType; this.binaryType = binaryType; this.counterType = counterType; this.secretType = secretType; + this.clock = clock; } protected void open(boolean resume, File dir, String driverClass) @@ -646,18 +648,19 @@ abstract class JdbcDatabase implements Database<Connection> { } public void addOutstandingMessages(Connection txn, ContactId c, - Collection<MessageId> sent) throws DbException { + Collection<MessageId> sent, long expiry) throws DbException { PreparedStatement ps = null; try { - // Set the status of each message to SENT if it's currently NEW - String sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ? AND status = ?"; + // Update the transmission count and expiry time of each message + String sql = "UPDATE statuses SET expiry = ?," + + " transmissionCount = transmissionCount + ?" + + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); - ps.setShort(1, (short) SENT.ordinal()); - ps.setInt(3, c.getInt()); - ps.setShort(4, (short) NEW.ordinal()); + ps.setLong(1, expiry); + ps.setInt(2, 1); + ps.setInt(4, c.getInt()); for(MessageId m : sent) { - ps.setBytes(2, m.getBytes()); + ps.setBytes(3, m.getBytes()); ps.addBatch(); } int[] batchAffected = ps.executeBatch(); @@ -705,6 +708,26 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public void addStatus(Connection txn, ContactId c, MessageId m, + boolean seen) throws DbException { + PreparedStatement ps = null; + try { + String sql = "INSERT INTO statuses" + + " (messageId, contactId, seen, transmissionCount, expiry)" + + " VALUES (?, ?, ?, ZERO(), ZERO())"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, m.getBytes()); + ps.setInt(2, c.getInt()); + ps.setBoolean(3, seen); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + public void addSubscription(Connection txn, Group g) throws DbException { PreparedStatement ps = null; try { @@ -1220,6 +1243,7 @@ abstract class JdbcDatabase implements Database<Connection> { public Collection<MessageId> getMessagesToOffer(Connection txn, ContactId c, int maxMessages) throws DbException { + long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; try { @@ -1227,11 +1251,11 @@ abstract class JdbcDatabase implements Database<Connection> { String sql = "SELECT m.messageId FROM messages AS m" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " WHERE m.contactId = ? AND status = ?" + + " WHERE m.contactId = ? AND seen = FALSE AND expiry < ?" + " ORDER BY timestamp DESC LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); ps.setInt(3, maxMessages); rs = ps.executeQuery(); List<MessageId> ids = new ArrayList<MessageId>(); @@ -1254,12 +1278,12 @@ abstract class JdbcDatabase implements Database<Connection> { + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" + " AND timestamp >= retention" - + " AND status = ?" + + " AND seen = FALSE AND expiry < ?" + " AND sendability > ZERO()" + " ORDER BY timestamp DESC LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); ps.setInt(3, maxMessages - ids.size()); rs = ps.executeQuery(); while(rs.next()) ids.add(new MessageId(rs.getBytes(2))); @@ -1383,6 +1407,7 @@ abstract class JdbcDatabase implements Database<Connection> { public byte[] getRawMessageIfSendable(Connection txn, ContactId c, MessageId m) throws DbException { + long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; try { @@ -1391,11 +1416,11 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN statuses AS s" + " ON m.messageId = s.messageId" + " WHERE m.messageId = ? AND m.contactId = ?" - + " AND status = ?"; + + " AND seen = FALSE AND expiry < ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); - ps.setShort(3, (short) NEW.ordinal()); + ps.setLong(3, now); rs = ps.executeQuery(); byte[] raw = null; if(rs.next()) { @@ -1422,12 +1447,12 @@ abstract class JdbcDatabase implements Database<Connection> { + " WHERE m.messageId = ?" + " AND cg.contactId = ?" + " AND timestamp >= retention" - + " AND status = ?" + + " AND seen = FALSE AND expiry < ?" + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); - ps.setShort(3, (short) NEW.ordinal()); + ps.setLong(3, now); rs = ps.executeQuery(); if(rs.next()) { int length = rs.getInt(1); @@ -1631,6 +1656,7 @@ abstract class JdbcDatabase implements Database<Connection> { public Collection<MessageId> getSendableMessages(Connection txn, ContactId c, int maxLength) throws DbException { + long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; try { @@ -1638,11 +1664,11 @@ abstract class JdbcDatabase implements Database<Connection> { String sql = "SELECT length, m.messageId FROM messages AS m" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " WHERE m.contactId = ? AND status = ?" + + " WHERE m.contactId = ? AND seen = FALSE AND expiry < ?" + " ORDER BY timestamp DESC"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); rs = ps.executeQuery(); List<MessageId> ids = new ArrayList<MessageId>(); int total = 0; @@ -1669,12 +1695,12 @@ abstract class JdbcDatabase implements Database<Connection> { + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" + " AND timestamp >= retention" - + " AND status = ?" + + " AND seen = FALSE AND expiry < ?" + " AND sendability > ZERO()" + " ORDER BY timestamp DESC"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); rs = ps.executeQuery(); while(rs.next()) { int length = rs.getInt(1); @@ -1988,6 +2014,7 @@ abstract class JdbcDatabase implements Database<Connection> { public boolean hasSendableMessages(Connection txn, ContactId c) throws DbException { + long now = clock.currentTimeMillis(); PreparedStatement ps = null; ResultSet rs = null; try { @@ -1995,11 +2022,11 @@ abstract class JdbcDatabase implements Database<Connection> { String sql = "SELECT m.messageId FROM messages AS m" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " WHERE m.contactId = ? AND status = ?" + + " WHERE m.contactId = ? AND seen = FALSE AND expiry < ?" + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); ps.setInt(3, 1); rs = ps.executeQuery(); boolean found = rs.next(); @@ -2021,12 +2048,12 @@ abstract class JdbcDatabase implements Database<Connection> { + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" + " AND timestamp >= retention" - + " AND status = ?" + + " AND seen = FALSE AND expiry < ?" + " AND sendability > ZERO()" + " LIMIT ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setShort(2, (short) NEW.ordinal()); + ps.setLong(2, now); ps.setInt(3, 1); rs = ps.executeQuery(); found = rs.next(); @@ -2100,13 +2127,11 @@ abstract class JdbcDatabase implements Database<Connection> { Collection<MessageId> acked) throws DbException { PreparedStatement ps = null; try { - // Set the status of each message to SEEN if it's currently SENT - String sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ? AND status = ?"; + // Set the status of each message to seen = true + String sql = "UPDATE statuses SET seen = TRUE" + + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); - ps.setShort(1, (short) SEEN.ordinal()); - ps.setInt(3, c.getInt()); - ps.setShort(4, (short) SENT.ordinal()); + ps.setInt(1, c.getInt()); for(MessageId m : acked) { ps.setBytes(2, m.getBytes()); ps.addBatch(); @@ -2612,55 +2637,6 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setStatus(Connection txn, ContactId c, MessageId m, Status s) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT status FROM statuses" - + " WHERE messageId = ? AND contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, m.getBytes()); - ps.setInt(2, c.getInt()); - rs = ps.executeQuery(); - if(rs.next()) { - // A status row exists - update it if necessary - Status old = Status.values()[rs.getByte(1)]; - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - if(old != SEEN && old != s) { - sql = "UPDATE statuses SET status = ?" - + " WHERE messageId = ? AND contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setShort(1, (short) s.ordinal()); - ps.setBytes(2, m.getBytes()); - ps.setInt(3, c.getInt()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); - } - } else { - // No status row exists - create one - rs.close(); - ps.close(); - sql = "INSERT INTO statuses (messageId, contactId, status)" - + " VALUES (?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, m.getBytes()); - ps.setInt(2, c.getInt()); - ps.setShort(3, (short) s.ordinal()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); - } - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public boolean setStatusSeenIfVisible(Connection txn, ContactId c, MessageId m) throws DbException { PreparedStatement ps = null; @@ -2686,10 +2662,10 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); if(!found) return false; - sql = "UPDATE statuses SET status = ?" + sql = "UPDATE statuses SET seen = ?" + " WHERE messageId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); - ps.setShort(1, (short) SEEN.ordinal()); + ps.setBoolean(1, true); ps.setBytes(2, m.getBytes()); ps.setInt(3, c.getInt()); int affected = ps.executeUpdate(); diff --git a/briar-core/src/net/sf/briar/db/Status.java b/briar-core/src/net/sf/briar/db/Status.java deleted file mode 100644 index 8fcda6803bd2690d6b3cb3458da82c2dd0f89c93..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/db/Status.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.sf.briar.db; - -/** The status of a message with respect to a particular contact. */ -enum Status { - /** The message has not been sent, received, or acked. */ - NEW, - /** The message has been sent, but not received or acked. */ - SENT, - /** The message has been received or acked. */ - SEEN -} diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java index d3b04d168ba7890768a6a78ce3c02a71a6672866..6099975be85d806a061b9ab5d5725f11b0cfa5ad 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java @@ -2,8 +2,6 @@ package net.sf.briar.db; import static net.sf.briar.api.Rating.GOOD; import static net.sf.briar.api.Rating.UNRATED; -import static net.sf.briar.db.Status.NEW; -import static net.sf.briar.db.Status.SEEN; import java.util.ArrayList; import java.util.Arrays; @@ -364,7 +362,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); - oneOf(database).setStatus(txn, contactId, messageId, NEW); + oneOf(database).addStatus(txn, contactId, messageId, false); // The author is unrated and there are no sendable children oneOf(database).getRating(txn, authorId); will(returnValue(UNRATED)); @@ -399,7 +397,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); - oneOf(database).setStatus(txn, contactId, messageId, NEW); + oneOf(database).addStatus(txn, contactId, messageId, false); // The author is rated GOOD and there are two sendable children oneOf(database).getRating(txn, authorId); will(returnValue(GOOD)); @@ -460,7 +458,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // addLocalPrivateMessage(privateMessage, contactId) oneOf(database).addPrivateMessage(txn, privateMessage, contactId); will(returnValue(true)); - oneOf(database).setStatus(txn, contactId, messageId, NEW); + oneOf(database).addStatus(txn, contactId, messageId, false); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, shutdown); @@ -698,7 +696,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).getRawMessage(txn, messageId1); will(returnValue(raw1)); // Record the outstanding messages - oneOf(database).addOutstandingMessages(txn, contactId, sendable); + // FIXME: Calculate the expiry time + oneOf(database).addOutstandingMessages(txn, contactId, sendable, + Long.MAX_VALUE); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, shutdown); @@ -734,8 +734,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).getRawMessageIfSendable(txn, contactId, messageId2); will(returnValue(null)); // Message is not sendable // Record the outstanding messages + // FIXME: Calculate the expiry time oneOf(database).addOutstandingMessages(txn, contactId, - Collections.singletonList(messageId1)); + Collections.singletonList(messageId1), Long.MAX_VALUE); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, shutdown); @@ -922,7 +923,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message is stored oneOf(database).addPrivateMessage(txn, privateMessage, contactId); will(returnValue(true)); - oneOf(database).setStatus(txn, contactId, messageId, SEEN); + oneOf(database).addStatus(txn, contactId, messageId, true); // The message must be acked oneOf(database).addMessageToAck(txn, contactId, messageId); }}); @@ -1011,7 +1012,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message is stored, but it's a duplicate oneOf(database).addGroupMessage(txn, message); will(returnValue(false)); - oneOf(database).setStatus(txn, contactId, messageId, SEEN); + oneOf(database).addStatus(txn, contactId, messageId, true); // The message must be acked oneOf(database).addMessageToAck(txn, contactId, messageId); }}); @@ -1043,8 +1044,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message is stored, and it's not a duplicate oneOf(database).addGroupMessage(txn, message); will(returnValue(true)); - oneOf(database).setStatus(txn, contactId, messageId, SEEN); - // Set the status to NEW for all other contacts (there are none) + oneOf(database).addStatus(txn, contactId, messageId, true); + // Set the status to seen = true for all other contacts (none) oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); // Calculate the sendability - zero, so ancestors aren't updated @@ -1085,8 +1086,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message is stored, and it's not a duplicate oneOf(database).addGroupMessage(txn, message); will(returnValue(true)); - oneOf(database).setStatus(txn, contactId, messageId, SEEN); - // Set the status to NEW for all other contacts (there are none) + oneOf(database).addStatus(txn, contactId, messageId, true); + // Set the status to seen = true for all other contacts (none) oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); // Calculate the sendability - ancestors are updated @@ -1214,7 +1215,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); - oneOf(database).setStatus(txn, contactId, messageId, NEW); + oneOf(database).addStatus(txn, contactId, messageId, false); oneOf(database).getRating(txn, authorId); will(returnValue(UNRATED)); oneOf(database).getNumberOfSendableChildren(txn, messageId); @@ -1250,7 +1251,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // addLocalPrivateMessage(privateMessage, contactId) oneOf(database).addPrivateMessage(txn, privateMessage, contactId); will(returnValue(true)); - oneOf(database).setStatus(txn, contactId, messageId, NEW); + oneOf(database).addStatus(txn, contactId, messageId, false); // The message was added, so the listener should be called oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class))); }}); diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java index 15d2ec9b61d2051621bd9061575f6c10dbcac6a6..17546db32a11b47a94684ccbf7a4320b6fccae50 100644 --- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java +++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java @@ -3,9 +3,6 @@ package net.sf.briar.db; import static java.util.concurrent.TimeUnit.SECONDS; import static net.sf.briar.api.Rating.GOOD; import static net.sf.briar.api.Rating.UNRATED; -import static net.sf.briar.db.Status.NEW; -import static net.sf.briar.db.Status.SEEN; -import static net.sf.briar.db.Status.SENT; import static org.junit.Assert.assertArrayEquals; import java.io.File; @@ -29,6 +26,7 @@ import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.messaging.AuthorId; @@ -224,7 +222,7 @@ public class H2DatabaseTest extends BriarTestCase { } @Test - public void testSendablePrivateMessagesMustHaveStatusNew() + public void testSendablePrivateMessagesMustHaveSeenFlagFalse() throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -239,25 +237,14 @@ public class H2DatabaseTest extends BriarTestCase { db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); - // Changing the status to NEW should make the message sendable - db.setStatus(txn, contactId, messageId1, NEW); + // Adding a status with seen = false should make the message sendable + db.addStatus(txn, contactId, messageId1, false); assertTrue(db.hasSendableMessages(txn, contactId)); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertTrue(it.hasNext()); assertEquals(messageId1, it.next()); assertFalse(it.hasNext()); - // Changing the status to SENT should make the message unsendable - db.setStatus(txn, contactId, messageId1, SENT); - assertFalse(db.hasSendableMessages(txn, contactId)); - it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); - assertFalse(it.hasNext()); - - // Changing the status to SEEN should also make the message unsendable - db.setStatus(txn, contactId, messageId1, SEEN); - it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); - assertFalse(it.hasNext()); - db.commitTransaction(txn); db.close(); } @@ -271,7 +258,7 @@ public class H2DatabaseTest extends BriarTestCase { // Add a contact and store a private message assertEquals(contactId, db.addContact(txn)); db.addPrivateMessage(txn, privateMessage, contactId); - db.setStatus(txn, contactId, messageId1, NEW); + db.addStatus(txn, contactId, messageId1, false); // The message is sendable, but too large to send assertTrue(db.hasSendableMessages(txn, contactId)); @@ -302,7 +289,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addVisibility(txn, contactId, groupId); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The message should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); @@ -329,7 +316,7 @@ public class H2DatabaseTest extends BriarTestCase { } @Test - public void testSendableGroupMessagesMustHaveStatusNew() + public void testSendableGroupMessagesMustHaveSeenFlagFalse() throws Exception { Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -348,25 +335,20 @@ public class H2DatabaseTest extends BriarTestCase { db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); - // Changing the status to NEW should make the message sendable - db.setStatus(txn, contactId, messageId, NEW); + // Adding a status with seen = false should make the message sendable + db.addStatus(txn, contactId, messageId, false); assertTrue(db.hasSendableMessages(txn, contactId)); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); - // Changing the status to SENT should make the message unsendable - db.setStatus(txn, contactId, messageId, SENT); + // Changing the status to seen = true should make the message unsendable + db.setStatusSeenIfVisible(txn, contactId, messageId); assertFalse(db.hasSendableMessages(txn, contactId)); it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); assertFalse(it.hasNext()); - // Changing the status to SEEN should also make the message unsendable - db.setStatus(txn, contactId, messageId, SEEN); - it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); - assertFalse(it.hasNext()); - db.commitTransaction(txn); db.close(); } @@ -382,7 +364,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addVisibility(txn, contactId, groupId); db.addGroupMessage(txn, message); db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The contact is not subscribed, so the message should not be sendable assertFalse(db.hasSendableMessages(txn, contactId)); @@ -420,7 +402,7 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The message is sendable, but too large to send assertTrue(db.hasSendableMessages(txn, contactId)); @@ -450,7 +432,7 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The subscription is not visible to the contact, so the message // should not be sendable @@ -534,7 +516,7 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // Retrieve the message from the database and mark it as sent Iterator<MessageId> it = @@ -542,9 +524,9 @@ public class H2DatabaseTest extends BriarTestCase { assertTrue(it.hasNext()); assertEquals(messageId, it.next()); assertFalse(it.hasNext()); - db.setStatus(txn, contactId, messageId, SENT); + // FIXME: Calculate the expiry time db.addOutstandingMessages(txn, contactId, - Collections.singletonList(messageId)); + Collections.singletonList(messageId), Long.MAX_VALUE); // The message should no longer be sendable it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); @@ -913,11 +895,11 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); - // Set the sendability to > 0 and the status to SEEN + // Set the sendability to > 0 and the status to seen = true db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, SEEN); + db.addStatus(txn, contactId, messageId, true); - // The message is not sendable because its status is SEEN + // The message is not sendable because its status is seen = true assertNull(db.getRawMessageIfSendable(txn, contactId, messageId)); db.commitTransaction(txn); @@ -936,9 +918,9 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); - // Set the sendability to 0 and the status to NEW + // Set the sendability to 0 and the status to seen = false db.setSendability(txn, messageId, 0); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The message is not sendable because its sendability is 0 assertNull(db.getRawMessageIfSendable(txn, contactId, messageId)); @@ -961,9 +943,9 @@ public class H2DatabaseTest extends BriarTestCase { db.setRetentionTime(txn, contactId, timestamp + 1, 1); db.addGroupMessage(txn, message); - // Set the sendability to > 0 and the status to NEW + // Set the sendability to > 0 and the status to seen = false db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The message is not sendable because it's too old assertNull(db.getRawMessageIfSendable(txn, contactId, messageId)); @@ -984,9 +966,9 @@ public class H2DatabaseTest extends BriarTestCase { db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.addGroupMessage(txn, message); - // Set the sendability to > 0 and the status to NEW + // Set the sendability to > 0 and the status to seen = false db.setSendability(txn, messageId, 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The message is sendable so it should be returned byte[] b = db.getRawMessageIfSendable(txn, contactId, messageId); @@ -1042,7 +1024,7 @@ public class H2DatabaseTest extends BriarTestCase { assertEquals(contactId, db.addContact(txn)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // There's no contact subscription for the group assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); @@ -1062,7 +1044,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addSubscription(txn, group); db.addGroupMessage(txn, message); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); // The subscription is not visible assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); @@ -1085,7 +1067,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addGroupMessage(txn, message); // The message has already been seen by the contact - db.setStatus(txn, contactId, messageId, SEEN); + db.addStatus(txn, contactId, messageId, true); assertTrue(db.setStatusSeenIfVisible(txn, contactId, messageId)); @@ -1107,7 +1089,7 @@ public class H2DatabaseTest extends BriarTestCase { db.addGroupMessage(txn, message); // The message has not been seen by the contact - db.setStatus(txn, contactId, messageId, NEW); + db.addStatus(txn, contactId, messageId, false); assertTrue(db.setStatusSeenIfVisible(txn, contactId, messageId)); @@ -1828,7 +1810,7 @@ public class H2DatabaseTest extends BriarTestCase { private Database<Connection> open(boolean resume) throws Exception { Database<Connection> db = new H2Database(new TestDatabaseConfig(testDir, - MAX_SIZE)); + MAX_SIZE), new SystemClock()); db.open(resume); return db; }