diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index b5cf1d617..3c79254d3 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -292,6 +292,16 @@ public interface DatabaseComponent extends TransactionManager {
*/
Message getMessage(Transaction txn, MessageId m) throws DbException;
+ /**
+ * Returns the total length, including headers, of any messages that are
+ * eligible to be sent to the given contact via a transport with the given
+ * max latency.
+ *
+ * Read-only.
+ */
+ long getMessageBytesToSend(Transaction txn, ContactId c, int maxLatency)
+ throws DbException;
+
/**
* Returns the IDs of all delivered messages in the given group.
*
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index b6832d1e9..ab291fa30 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -347,6 +347,16 @@ interface Database {
*/
Message getMessage(T txn, MessageId m) throws DbException;
+ /**
+ * Returns the total length, including headers, of any messages that are
+ * eligible to be sent to the given contact via a transport with the given
+ * max latency.
+ *
+ * Read-only.
+ */
+ long getMessageBytesToSend(T txn, ContactId c, int maxLatency)
+ throws DbException;
+
/**
* Returns the IDs and states of all dependencies of the given message.
* For missing dependencies and dependencies in other groups, the state
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index f3e818644..40ba6fa31 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -575,6 +575,15 @@ class DatabaseComponentImpl implements DatabaseComponent {
return db.getMessage(txn, m);
}
+ @Override
+ public long getMessageBytesToSend(Transaction transaction, ContactId c,
+ int maxLatency) throws DbException {
+ T txn = unbox(transaction);
+ if (!db.containsContact(txn, c))
+ throw new NoSuchContactException();
+ return db.getMessageBytesToSend(txn, c, maxLatency);
+ }
+
@Override
public Collection getMessageIds(Transaction transaction,
GroupId g) throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index 72ceec594..af54d4e9d 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -1758,6 +1758,37 @@ abstract class JdbcDatabase implements Database {
}
}
+ @Override
+ public long getMessageBytesToSend(Connection txn, ContactId c,
+ int maxLatency) throws DbException {
+ long now = clock.currentTimeMillis();
+ long eta = now + maxLatency;
+ PreparedStatement ps = null;
+ ResultSet rs = null;
+ try {
+ String sql = "SELECT SUM(length) FROM statuses"
+ + " WHERE contactId = ? AND state = ?"
+ + " AND groupShared = TRUE AND messageShared = TRUE"
+ + " AND deleted = FALSE AND seen = FALSE"
+ + " AND (expiry <= ? OR eta > ?)";
+ ps = txn.prepareStatement(sql);
+ ps.setInt(1, c.getInt());
+ ps.setInt(2, DELIVERED.getValue());
+ ps.setLong(3, now);
+ ps.setLong(4, eta);
+ rs = ps.executeQuery();
+ rs.next();
+ long total = rs.getInt(1);
+ rs.close();
+ ps.close();
+ return total;
+ } catch (SQLException e) {
+ tryToClose(rs, LOG, WARNING);
+ tryToClose(ps, LOG, WARNING);
+ throw new DbException(e);
+ }
+ }
+
@Override
public Collection getMessageIds(Connection txn, GroupId g)
throws DbException {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
index 39615b2e5..4b53a781d 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
@@ -298,11 +298,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception {
context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not)
- exactly(18).of(database).startTransaction();
+ exactly(19).of(database).startTransaction();
will(returnValue(txn));
- exactly(18).of(database).containsContact(txn, contactId);
+ exactly(19).of(database).containsContact(txn, contactId);
will(returnValue(false));
- exactly(18).of(database).abortTransaction(txn);
+ exactly(19).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
@@ -349,7 +349,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
- db.transaction(false, transaction ->
+ db.transaction(true, transaction ->
db.getContact(transaction, contactId));
fail();
} catch (NoSuchContactException expected) {
@@ -357,7 +357,15 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
- db.transaction(false, transaction ->
+ db.transaction(true, transaction ->
+ db.getMessageBytesToSend(transaction, contactId, 123));
+ fail();
+ } catch (NoSuchContactException expected) {
+ // Expected
+ }
+
+ try {
+ db.transaction(true, transaction ->
db.getMessageStatus(transaction, contactId, groupId));
fail();
} catch (NoSuchContactException expected) {
@@ -365,7 +373,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
- db.transaction(false, transaction ->
+ db.transaction(true, transaction ->
db.getMessageStatus(transaction, contactId, messageId));
fail();
} catch (NoSuchContactException expected) {
@@ -373,7 +381,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
- db.transaction(false, transaction ->
+ db.transaction(true, transaction ->
db.getGroupVisibility(transaction, contactId, groupId));
fail();
} catch (NoSuchContactException expected) {
@@ -381,7 +389,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}
try {
- db.transaction(false, transaction ->
+ db.transaction(true, transaction ->
db.getSyncVersions(transaction, contactId));
fail();
} catch (NoSuchContactException expected) {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index 4dfec5fce..69bc97671 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -227,6 +227,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Changing the status to seen = true should make the message unsendable
db.raiseSeenFlag(txn, contactId, messageId);
@@ -234,6 +236,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();
@@ -258,6 +261,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message delivered should make it sendable
db.setMessageState(txn, messageId, DELIVERED);
@@ -265,6 +269,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message invalid should make it unsendable
db.setMessageState(txn, messageId, INVALID);
@@ -272,6 +278,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Marking the message pending should make it unsendable
db.setMessageState(txn, messageId, PENDING);
@@ -279,6 +286,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();
@@ -302,6 +310,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Making the group visible should not make the message sendable
db.addGroupVisibility(txn, contactId, groupId, false);
@@ -309,6 +318,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Sharing the group should make the message sendable
db.setGroupVisibility(txn, contactId, groupId, true);
@@ -316,6 +326,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Unsharing the group should make the message unsendable
db.setGroupVisibility(txn, contactId, groupId, false);
@@ -323,6 +335,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Making the group invisible should make the message unsendable
db.removeGroupVisibility(txn, contactId, groupId);
@@ -330,6 +343,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();
@@ -354,6 +368,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertTrue(ids.isEmpty());
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(0, db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
// Sharing the message should make it sendable
db.setMessageShared(txn, messageId, true);
@@ -361,6 +376,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(singletonList(messageId), ids);
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();
@@ -384,10 +401,15 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.getMessagesToSend(txn, contactId, message.getRawLength() - 1,
MAX_LATENCY);
assertTrue(ids.isEmpty());
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
+
// The message is just the right size to send
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
MAX_LATENCY);
assertEquals(singletonList(messageId), ids);
+ assertEquals(message.getRawLength(),
+ db.getMessageBytesToSend(txn, contactId, MAX_LATENCY));
db.commitTransaction(txn);
db.close();