diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java index 6399ad328a2b2ae755e22f09be95c781c07bde79..e986f4a5fadd8b472e7497c50d4230fe5518945c 100644 --- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java +++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java @@ -11,8 +11,8 @@ import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; @@ -99,22 +99,22 @@ public interface DatabaseComponent { Collection<MessageId> requested) throws DbException; /** - * Generates an expiry ack for the given contact. Returns null if no ack - * is due. + * Generates an offer for the given contact. Returns null if there are no + * messages to offer. */ - ExpiryAck generateExpiryAck(ContactId c) throws DbException; + Offer generateOffer(ContactId c, int maxMessages) throws DbException; /** - * Generates an expiry update for the given contact. Returns null if no - * update is due. + * Generates a retention ack for the given contact. Returns null if no ack + * is due. */ - ExpiryUpdate generateExpiryUpdate(ContactId c) throws DbException; + RetentionAck generateRetentionAck(ContactId c) throws DbException; /** - * Generates an offer for the given contact. Returns null if there are no - * messages to offer. + * Generates a retention update for the given contact. Returns null if no + * update is due. */ - Offer generateOffer(ContactId c, int maxMessages) throws DbException; + RetentionUpdate generateRetentionUpdate(ContactId c) throws DbException; /** * Generates a subscription ack for the given contact. Returns null if no @@ -200,12 +200,6 @@ public interface DatabaseComponent { /** Processes an ack from the given contact. */ void receiveAck(ContactId c, Ack a) throws DbException; - /** Processes an expiry ack from the given contact. */ - void receiveExpiryAck(ContactId c, ExpiryAck a) throws DbException; - - /** Processes an expiry update from the given contact. */ - void receiveExpiryUpdate(ContactId c, ExpiryUpdate u) throws DbException; - /** Processes a message from the given contact. */ void receiveMessage(ContactId c, Message m) throws DbException; @@ -219,6 +213,13 @@ public interface DatabaseComponent { */ Request receiveOffer(ContactId c, Offer o) throws DbException; + /** Processes a retention ack from the given contact. */ + void receiveRetentionAck(ContactId c, RetentionAck a) throws DbException; + + /** Processes a retention update from the given contact. */ + void receiveRetentionUpdate(ContactId c, RetentionUpdate u) + throws DbException; + /** Processes a subscription ack from the given contact. */ void receiveSubscriptionAck(ContactId c, SubscriptionAck a) throws DbException; diff --git a/briar-api/src/net/sf/briar/api/protocol/ExpiryUpdate.java b/briar-api/src/net/sf/briar/api/protocol/ExpiryUpdate.java deleted file mode 100644 index 4b3c81f3080cfb9a7a43e2c085bd45c5a79e97fe..0000000000000000000000000000000000000000 --- a/briar-api/src/net/sf/briar/api/protocol/ExpiryUpdate.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.sf.briar.api.protocol; - -/** - * A packet updating the recipient's view of the expiry time of the sender's - * database. - */ -public class ExpiryUpdate { - - private final long expiry, version; - - public ExpiryUpdate(long expiry, long version) { - this.expiry = expiry; - this.version = version; - } - - public long getExpiryTime() { - return expiry; - } - - public long getVersionNumber() { - return version; - } -} diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java index 0499180c03d0835fc001a873681ef11eca09ea79..12f675b5bdb3d1cf46aba7c30430e193f2a64619 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java @@ -9,12 +9,6 @@ public interface ProtocolReader { boolean hasAck() throws IOException; Ack readAck() throws IOException; - boolean hasExpiryAck() throws IOException; - ExpiryAck readExpiryAck() throws IOException; - - boolean hasExpiryUpdate() throws IOException; - ExpiryUpdate readExpiryUpdate() throws IOException; - boolean hasMessage() throws IOException; UnverifiedMessage readMessage() throws IOException; @@ -24,6 +18,12 @@ public interface ProtocolReader { boolean hasRequest() throws IOException; Request readRequest() throws IOException; + boolean hasRetentionAck() throws IOException; + RetentionAck readRetentionAck() throws IOException; + + boolean hasRetentionUpdate() throws IOException; + RetentionUpdate readRetentionUpdate() throws IOException; + boolean hasSubscriptionAck() throws IOException; SubscriptionAck readSubscriptionAck() throws IOException; diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java index 718e8be37cb94748b4d5807dd677fda7c7ce12d9..f2d9fa2b6dc7b3c8228bda415c9a8245a3eaf8b6 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java @@ -10,16 +10,16 @@ public interface ProtocolWriter { void writeAck(Ack a) throws IOException; - void writeExpiryAck(ExpiryAck a) throws IOException; - - void writeExpiryUpdate(ExpiryUpdate e) throws IOException; - void writeMessage(byte[] raw) throws IOException; void writeOffer(Offer o) throws IOException; void writeRequest(Request r) throws IOException; + void writeRetentionAck(RetentionAck a) throws IOException; + + void writeRetentionUpdate(RetentionUpdate u) throws IOException; + void writeSubscriptionAck(SubscriptionAck a) throws IOException; void writeSubscriptionUpdate(SubscriptionUpdate u) throws IOException; diff --git a/briar-api/src/net/sf/briar/api/protocol/ExpiryAck.java b/briar-api/src/net/sf/briar/api/protocol/RetentionAck.java similarity index 56% rename from briar-api/src/net/sf/briar/api/protocol/ExpiryAck.java rename to briar-api/src/net/sf/briar/api/protocol/RetentionAck.java index 08640279d24aa27145ece490e684a9fa9a43812f..f5e46caae96fd6d16afd873a9a956452d438c017 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ExpiryAck.java +++ b/briar-api/src/net/sf/briar/api/protocol/RetentionAck.java @@ -1,11 +1,11 @@ package net.sf.briar.api.protocol; -/** A packet acknowledging a (@link ExpiryUpdate} */ -public class ExpiryAck { +/** A packet acknowledging a (@link RetentionUpdate} */ +public class RetentionAck { private final long version; - public ExpiryAck(long version) { + public RetentionAck(long version) { this.version = version; } diff --git a/briar-api/src/net/sf/briar/api/protocol/RetentionUpdate.java b/briar-api/src/net/sf/briar/api/protocol/RetentionUpdate.java new file mode 100644 index 0000000000000000000000000000000000000000..837154cd31153a374a123d5297ea912b1c9f2d92 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/protocol/RetentionUpdate.java @@ -0,0 +1,23 @@ +package net.sf.briar.api.protocol; + +/** + * A packet updating the recipient's view of the retention time of the sender's + * database. + */ +public class RetentionUpdate { + + private final long retention, version; + + public RetentionUpdate(long retention, long version) { + this.retention = retention; + this.version = version; + } + + public long getRetentionTime() { + return retention; + } + + public long getVersionNumber() { + return version; + } +} diff --git a/briar-api/src/net/sf/briar/api/protocol/Types.java b/briar-api/src/net/sf/briar/api/protocol/Types.java index d17e26ab5f50799feea3cda98586c9c482cab70e..eefa685cf6b6bd07132696d81530049c95f42275 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Types.java +++ b/briar-api/src/net/sf/briar/api/protocol/Types.java @@ -6,11 +6,11 @@ public interface Types { int AUTHOR = 0; int GROUP = 1; int ACK = 2; - int EXPIRY_ACK = 3; - int EXPIRY_UPDATE = 4; - int MESSAGE = 5; - int OFFER = 6; - int REQUEST = 7; + int MESSAGE = 3; + int OFFER = 4; + int REQUEST = 5; + int RETENTION_ACK = 6; + int RETENTION_UPDATE = 7; int SUBSCRIPTION_ACK = 8; int SUBSCRIPTION_UPDATE = 9; int TRANSPORT_ACK = 10; diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index 3e1e06ec2276c0a31e67f488cb26fc25dace6713..1a65cccc5fd27d724dc44a6b2e5ea3e17e588a43 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -11,8 +11,8 @@ import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; @@ -36,9 +36,9 @@ import net.sf.briar.api.transport.TemporarySecret; * deadlock, locks must be acquired in the following (alphabetical) order: * <ul> * <li> contact - * <li> expiry * <li> message * <li> rating + * <li> retention * <li> subscription * <li> transport * <li> window @@ -208,21 +208,6 @@ interface Database<T> { */ Collection<ContactTransport> getContactTransports(T txn) throws DbException; - /** - * Returns an expiry ack for the given contact, or null if no ack is due. - * <p> - * Locking: contact read, expiry write. - */ - ExpiryAck getExpiryAck(T txn, ContactId c) throws DbException; - - /** - * Returns an expiry update for the given contact, or null if no update is - * due. - * <p> - * Locking: contact read, expiry write. - */ - ExpiryUpdate getExpiryUpdate(T txn, ContactId c) throws DbException; - /** * Returns the amount of free storage space available to the database, in * bytes. This is based on the minimum of the space available on the device @@ -346,6 +331,21 @@ interface Database<T> { Map<ContactId, TransportProperties> getRemoteProperties(T txn, TransportId t) throws DbException; + /** + * Returns a retention ack for the given contact, or null if no ack is due. + * <p> + * Locking: contact read, retention write. + */ + RetentionAck getRetentionAck(T txn, ContactId c) throws DbException; + + /** + * Returns a retention update for the given contact, or null if no update + * is due. + * <p> + * Locking: contact read, retention write. + */ + RetentionUpdate getRetentionUpdate(T txn, ContactId c) throws DbException; + /** * Returns all temporary secrets. * <p> @@ -459,12 +459,12 @@ interface Database<T> { long period) throws DbException; /** - * Increments the expiry versions for all contacts to indicate that the - * database's expiry time has changed and expiry updates should be sent. + * Increments the retention time versions for all contacts to indicate that + * the database's retention time has changed and updates should be sent. * <p> - * Locking: contact read, expiry write. + * Locking: contact read, retention write. */ - void incrementExpiryVersions(T txn) throws DbException; + void incrementRetentionVersions(T txn) throws DbException; /** * Merges the given configuration with the existing configuration for the @@ -549,16 +549,6 @@ interface Database<T> { void setConnectionWindow(T txn, ContactId c, TransportId t, long period, long centre, byte[] bitmap) throws DbException; - /** - * Sets the expiry time of the given contact's database, unless an update - * with an equal or higher version number has already been received from - * the contact. - * <p> - * Locking: contact read, expiry write. - */ - void setExpiryTime(T txn, ContactId c, long expiry, long version) - throws DbException; - /** * Sets the user's rating for the given author. * <p> @@ -585,6 +575,16 @@ interface Database<T> { void setRemoteProperties(T txn, ContactId c, TransportUpdate u) throws DbException; + /** + * Sets the retention time of the given contact's database, unless an + * update with an equal or higher version number has already been received + * from the contact. + * <p> + * Locking: contact read, retention write. + */ + void setRetentionTime(T txn, ContactId c, long retention, long version) + throws DbException; + /** * Sets the sendability score of the given message. * <p> @@ -630,12 +630,12 @@ interface Database<T> { throws DbException; /** - * Records an expiry ack from the given contact for the given version + * Records a retention ack from the given contact for the given version * unless the contact has already acked an equal or higher version. * <p> - * Locking: contact read, expiry write. + * Locking: contact read, retention write. */ - void setExpiryUpdateAcked(T txn, ContactId c, long version) + void setRetentionUpdateAcked(T txn, ContactId c, long version) throws DbException; /** diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index 40ccb3af1a318d7681d21ad8cc4f635ea0aafe22..515bbf06027b380a28451118ae6d59f8fb00fb83 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -46,8 +46,8 @@ import net.sf.briar.api.db.event.TransportRemovedEvent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; @@ -83,12 +83,12 @@ DatabaseCleaner.Callback { private final ReentrantReadWriteLock contactLock = new ReentrantReadWriteLock(true); - private final ReentrantReadWriteLock expiryLock = - new ReentrantReadWriteLock(true); private final ReentrantReadWriteLock messageLock = new ReentrantReadWriteLock(true); private final ReentrantReadWriteLock ratingLock = new ReentrantReadWriteLock(true); + private final ReentrantReadWriteLock retentionLock = + new ReentrantReadWriteLock(true); private final ReentrantReadWriteLock subscriptionLock = new ReentrantReadWriteLock(true); private final ReentrantReadWriteLock transportLock = @@ -594,78 +594,79 @@ DatabaseCleaner.Callback { return Collections.unmodifiableList(messages); } - public ExpiryAck generateExpiryAck(ContactId c) throws DbException { + public Offer generateOffer(ContactId c, int maxMessages) + throws DbException { + Collection<MessageId> offered; contactLock.readLock().lock(); try { - expiryLock.writeLock().lock(); + messageLock.readLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - ExpiryAck a = db.getExpiryAck(txn, c); + offered = db.getMessagesToOffer(txn, c, maxMessages); db.commitTransaction(txn); - return a; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - expiryLock.writeLock().unlock(); + messageLock.readLock().unlock(); } } finally { contactLock.readLock().unlock(); } + return new Offer(offered); } - public ExpiryUpdate generateExpiryUpdate(ContactId c) throws DbException { + public RetentionAck generateRetentionAck(ContactId c) throws DbException { contactLock.readLock().lock(); try { - expiryLock.writeLock().lock(); + retentionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - ExpiryUpdate e = db.getExpiryUpdate(txn, c); + RetentionAck a = db.getRetentionAck(txn, c); db.commitTransaction(txn); - return e; + return a; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - expiryLock.writeLock().unlock(); + retentionLock.writeLock().unlock(); } } finally { contactLock.readLock().unlock(); } } - public Offer generateOffer(ContactId c, int maxMessages) + public RetentionUpdate generateRetentionUpdate(ContactId c) throws DbException { - Collection<MessageId> offered; contactLock.readLock().lock(); try { - messageLock.readLock().lock(); + retentionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - offered = db.getMessagesToOffer(txn, c, maxMessages); + RetentionUpdate u = db.getRetentionUpdate(txn, c); db.commitTransaction(txn); + return u; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - messageLock.readLock().unlock(); + retentionLock.writeLock().unlock(); } } finally { contactLock.readLock().unlock(); } - return new Offer(offered); } public SubscriptionAck generateSubscriptionAck(ContactId c) @@ -1090,54 +1091,6 @@ DatabaseCleaner.Callback { } } - public void receiveExpiryAck(ContactId c, ExpiryAck a) throws DbException { - contactLock.readLock().lock(); - try { - expiryLock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - if(!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.setExpiryUpdateAcked(txn, c, a.getVersionNumber()); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - expiryLock.writeLock().unlock(); - } - } finally { - contactLock.readLock().unlock(); - } - } - - public void receiveExpiryUpdate(ContactId c, ExpiryUpdate u) - throws DbException { - contactLock.readLock().lock(); - try { - expiryLock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - if(!db.containsContact(txn, c)) - throw new NoSuchContactException(); - db.setExpiryTime(txn, c, u.getExpiryTime(), - u.getVersionNumber()); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - expiryLock.writeLock().unlock(); - } - } finally { - contactLock.readLock().unlock(); - } - } - public void receiveMessage(ContactId c, Message m) throws DbException { boolean added = false; contactLock.readLock().lock(); @@ -1226,6 +1179,55 @@ DatabaseCleaner.Callback { return new Request(request, offered.size()); } + public void receiveRetentionAck(ContactId c, RetentionAck a) + throws DbException { + contactLock.readLock().lock(); + try { + retentionLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setRetentionUpdateAcked(txn, c, a.getVersionNumber()); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + retentionLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + + public void receiveRetentionUpdate(ContactId c, RetentionUpdate u) + throws DbException { + contactLock.readLock().lock(); + try { + retentionLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setRetentionTime(txn, c, u.getRetentionTime(), + u.getVersionNumber()); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + retentionLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + public void receiveSubscriptionAck(ContactId c, SubscriptionAck a) throws DbException { contactLock.readLock().lock(); @@ -1606,9 +1608,9 @@ DatabaseCleaner.Callback { boolean removed = false; contactLock.readLock().lock(); try { - expiryLock.writeLock().lock(); + messageLock.writeLock().lock(); try { - messageLock.writeLock().lock(); + retentionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { @@ -1616,7 +1618,7 @@ DatabaseCleaner.Callback { db.getOldMessages(txn, size); if(!old.isEmpty()) { for(MessageId m : old) removeMessage(txn, m); - db.incrementExpiryVersions(txn); + db.incrementRetentionVersions(txn); removed = true; } db.commitTransaction(txn); @@ -1625,10 +1627,10 @@ DatabaseCleaner.Callback { throw e; } } finally { - messageLock.writeLock().unlock(); + retentionLock.writeLock().unlock(); } } finally { - expiryLock.writeLock().unlock(); + messageLock.writeLock().unlock(); } } finally { contactLock.readLock().unlock(); diff --git a/briar-core/src/net/sf/briar/db/DatabaseConstants.java b/briar-core/src/net/sf/briar/db/DatabaseConstants.java index eb00f1290c2c7e5500c98b62766520cbb31690e1..16686d41c21a0939fee1350d71d4b1df92a50400 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseConstants.java +++ b/briar-core/src/net/sf/briar/db/DatabaseConstants.java @@ -40,7 +40,7 @@ interface DatabaseConstants { * The timestamp of the oldest message in the database is rounded using * this modulus to avoid revealing the presence of any particular message. */ - long EXPIRY_MODULUS = 60L * 60L * 1000L; // 1 hour + long RETENTION_MODULUS = 60L * 60L * 1000L; // 1 hour /** * The time in milliseconds after which a subscription or transport update diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index d96a0ae6a44778bde8b1fc4125696c7ce47a4b3e..dbfcb80a56ae38a4f0c686f72aeca2841ea09dfa 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -3,7 +3,7 @@ package net.sf.briar.db; import static java.sql.Types.BINARY; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static net.sf.briar.db.DatabaseConstants.EXPIRY_MODULUS; +import static net.sf.briar.db.DatabaseConstants.RETENTION_MODULUS; import java.io.File; import java.io.FileNotFoundException; @@ -31,8 +31,8 @@ import net.sf.briar.api.db.DbClosedException; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; @@ -58,20 +58,6 @@ abstract class JdbcDatabase implements Database<Connection> { + " (contactId COUNTER," + " PRIMARY KEY (contactId))"; - // Locking: expiry - private static final String CREATE_EXPIRY_VERSIONS = - "CREATE TABLE expiryVersions" - + " (contactId INT NOT NULL," - + " expiry BIGINT NOT NULL," - + " localVersion BIGINT NOT NULL," - + " localAcked BIGINT NOT NULL," - + " remoteVersion BIGINT NOT NULL," - + " remoteAcked BOOLEAN NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId)" - + " REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - // Locking: message private static final String CREATE_MESSAGES = "CREATE TABLE messages" @@ -155,6 +141,20 @@ abstract class JdbcDatabase implements Database<Connection> { + " rating SMALLINT NOT NULL," + " PRIMARY KEY (authorId))"; + // Locking: contact read, retention + private static final String CREATE_RETENTION_VERSIONS = + "CREATE TABLE retentionVersions" + + " (contactId INT NOT NULL," + + " retention BIGINT NOT NULL," + + " localVersion BIGINT NOT NULL," + + " localAcked BIGINT NOT NULL," + + " remoteVersion BIGINT NOT NULL," + + " remoteAcked BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + // Locking: subscription private static final String CREATE_GROUPS = "CREATE TABLE groups" @@ -355,7 +355,6 @@ abstract class JdbcDatabase implements Database<Connection> { try { s = txn.createStatement(); s.executeUpdate(insertTypeNames(CREATE_CONTACTS)); - s.executeUpdate(insertTypeNames(CREATE_EXPIRY_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES)); s.executeUpdate(INDEX_MESSAGES_BY_PARENT); s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR); @@ -367,6 +366,7 @@ abstract class JdbcDatabase implements Database<Connection> { s.executeUpdate(INDEX_STATUSES_BY_CONTACT); s.executeUpdate(insertTypeNames(CREATE_FLAGS)); s.executeUpdate(insertTypeNames(CREATE_RATINGS)); + s.executeUpdate(insertTypeNames(CREATE_RETENTION_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_GROUPS)); s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS)); @@ -513,8 +513,8 @@ abstract class JdbcDatabase implements Database<Connection> { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - // Create an expiry version row - sql = "INSERT INTO expiryVersions (contactId, expiry," + // Create a retention version row + sql = "INSERT INTO retentionVersions (contactId, retention," + " localVersion, localAcked, remoteVersion, remoteAcked)" + " VALUES (?, ZERO(), ?, ZERO(), ZERO(), TRUE)"; ps = txn.prepareStatement(sql); @@ -1042,72 +1042,6 @@ abstract class JdbcDatabase implements Database<Connection> { } else return f.length(); } - public ExpiryAck getExpiryAck(Connection txn, ContactId c) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT remoteVersion FROM expiryVersions" - + " WHERE contactId = ? AND remoteAcked = FALSE"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - rs = ps.executeQuery(); - if(!rs.next()) { - rs.close(); - ps.close(); - return null; - } - long version = rs.getLong(1); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - sql = "UPDATE expiryVersions SET remoteAcked = TRUE" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); - return new ExpiryAck(version); - } catch(SQLException e) { - tryToClose(ps); - tryToClose(rs); - throw new DbException(e); - } - } - - public ExpiryUpdate getExpiryUpdate(Connection txn, ContactId c) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT timestamp, localVersion" - + " FROM messages JOIN expiryVersions" - + " WHERE contactId = ? AND localVersion > localAcked" - + " ORDER BY timestamp LIMIT ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setInt(2, 1); - rs = ps.executeQuery(); - if(!rs.next()) { - rs.close(); - ps.close(); - return null; - } - long expiry = rs.getLong(1); - expiry -= expiry % EXPIRY_MODULUS; - long version = rs.getLong(2); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - return new ExpiryUpdate(expiry, version); - } catch(SQLException e) { - tryToClose(ps); - tryToClose(rs); - throw new DbException(e); - } - } - public MessageId getGroupMessageParent(Connection txn, MessageId m) throws DbException { PreparedStatement ps = null; @@ -1277,14 +1211,14 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN groupVisibilities AS gv" + " ON m.groupId = gv.groupId" + " AND cg.contactId = gv.contactId" - + " JOIN expiryVersions AS ev" - + " ON cg.contactId = ev.contactId" + + " JOIN retentionVersions AS rv" + + " ON cg.contactId = rv.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" + " AND cg.contactId = s.contactId" + " WHERE m.messageId = ?" + " AND cg.contactId = ?" - + " AND timestamp >= expiry" + + " AND timestamp >= retention" + " AND status = ?" + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); @@ -1382,13 +1316,13 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN groupVisibilities AS gv" + " ON m.groupId = gv.groupId" + " AND cg.contactId = gv.contactId" - + " JOIN expiryVersions AS ev" - + " ON cg.contactId = ev.contactId" + + " JOIN retentionVersions AS rv" + + " ON cg.contactId = rv.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" - + " AND timestamp >= expiry" + + " AND timestamp >= retention" + " AND status = ?" + " AND sendability > ZERO()" + " ORDER BY timestamp DESC LIMIT ?"; @@ -1548,6 +1482,72 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public RetentionAck getRetentionAck(Connection txn, ContactId c) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT remoteVersion FROM retentionVersions" + + " WHERE contactId = ? AND remoteAcked = FALSE"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + rs = ps.executeQuery(); + if(!rs.next()) { + rs.close(); + ps.close(); + return null; + } + long version = rs.getLong(1); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + sql = "UPDATE retentionVersions SET remoteAcked = TRUE" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + return new RetentionAck(version); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + + public RetentionUpdate getRetentionUpdate(Connection txn, ContactId c) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT timestamp, localVersion" + + " FROM messages JOIN retentionVersions" + + " WHERE contactId = ? AND localVersion > localAcked" + + " ORDER BY timestamp LIMIT ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setInt(2, 1); + rs = ps.executeQuery(); + if(!rs.next()) { + rs.close(); + ps.close(); + return null; + } + long retention = rs.getLong(1); + retention -= retention % RETENTION_MODULUS; + long version = rs.getLong(2); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + return new RetentionUpdate(retention, version); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + public Collection<TemporarySecret> getSecrets(Connection txn) throws DbException { PreparedStatement ps = null; @@ -1642,13 +1642,13 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN groupVisibilities AS gv" + " ON m.groupId = gv.groupId" + " AND cg.contactId = gv.contactId" - + " JOIN expiryVersions AS ev" - + " ON cg.contactId = ev.contactId" + + " JOIN retentionVersions AS rv" + + " ON cg.contactId = rv.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" - + " AND timestamp >= expiry" + + " AND timestamp >= retention" + " AND status = ?" + " AND sendability > ZERO()" + " ORDER BY timestamp DESC"; @@ -1974,13 +1974,13 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN groupVisibilities AS gv" + " ON m.groupId = gv.groupId" + " AND cg.contactId = gv.contactId" - + " JOIN expiryVersions AS ev" - + " ON cg.contactId = ev.contactId" + + " JOIN retentionVersios AS rv" + + " ON cg.contactId = rv.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" + " AND cg.contactId = s.contactId" + " WHERE cg.contactId = ?" - + " AND timestamp >= expiry" + + " AND timestamp >= retention" + " AND status = ?" + " AND sendability > ZERO()" + " LIMIT ?"; @@ -2042,10 +2042,10 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void incrementExpiryVersions(Connection txn) throws DbException { + public void incrementRetentionVersions(Connection txn) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE expiryVersions" + String sql = "UPDATE retentionVersions" + " SET localVersion = localVersion + ?"; ps = txn.prepareStatement(sql); ps.setInt(1, 1); @@ -2323,15 +2323,15 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setExpiryTime(Connection txn, ContactId c, long expiry, + public void setRetentionTime(Connection txn, ContactId c, long retention, long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE expiryVersions" - + " SET expiry = ?, remoteVersion = ?, remoteAcked = FALSE" + String sql = "UPDATE retentionVersions SET retention = ?," + + " remoteVersion = ?, remoteAcked = FALSE" + " WHERE contactId = ? AND remoteVersion < ?"; ps = txn.prepareStatement(sql); - ps.setLong(1, expiry); + ps.setLong(1, retention); ps.setLong(2, version); ps.setInt(3, c.getInt()); ps.setLong(4, version); @@ -2344,11 +2344,11 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setExpiryUpdateAcked(Connection txn, ContactId c, long version) - throws DbException { + public void setRetentionUpdateAcked(Connection txn, ContactId c, + long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE expiryVersions SET localAcked = ?" + String sql = "UPDATE retentionVersions SET localAcked = ?" + " WHERE contactId = ?" + " AND localAcked < ? AND localVersion >= ?"; ps = txn.prepareStatement(sql); @@ -2672,11 +2672,11 @@ abstract class JdbcDatabase implements Database<Connection> { + " JOIN groupVisibilities AS gv" + " ON m.groupId = gv.groupId" + " AND cg.contactId = gv.contactId" - + " JOIN expiryVersions AS ev" - + " ON cg.contactId = ev.contactId" + + " JOIN retentionVersions AS rv" + + " ON cg.contactId = rv.contactId" + " WHERE messageId = ?" + " AND cg.contactId = ?" - + " AND timestamp >= expiry"; + + " AND timestamp >= retention"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); ps.setInt(2, c.getInt()); diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java index 1f4b5acdaa99f5367df35f50e498600b3a39a9a2..2e49cb2b6bad638f5e9c187d29a77defe5d147fa 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java @@ -4,8 +4,8 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; import static net.sf.briar.api.protocol.Types.ACK; -import static net.sf.briar.api.protocol.Types.EXPIRY_ACK; -import static net.sf.briar.api.protocol.Types.EXPIRY_UPDATE; +import static net.sf.briar.api.protocol.Types.RETENTION_ACK; +import static net.sf.briar.api.protocol.Types.RETENTION_UPDATE; import static net.sf.briar.api.protocol.Types.MESSAGE; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; @@ -26,8 +26,8 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.FormatException; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReader; @@ -90,30 +90,6 @@ class ProtocolReaderImpl implements ProtocolReader { return new Ack(Collections.unmodifiableList(acked)); } - public boolean hasExpiryAck() throws IOException { - return r.hasStruct(EXPIRY_ACK); - } - - public ExpiryAck readExpiryAck() throws IOException { - r.readStructId(EXPIRY_ACK); - long version = r.readInt64(); - if(version < 0L) throw new FormatException(); - return new ExpiryAck(version); - } - - public boolean hasExpiryUpdate() throws IOException { - return r.hasStruct(EXPIRY_UPDATE); - } - - public ExpiryUpdate readExpiryUpdate() throws IOException { - r.readStructId(EXPIRY_UPDATE); - long expiry = r.readInt64(); - if(expiry < 0L) throw new FormatException(); - long version = r.readInt64(); - if(version < 0L) throw new FormatException(); - return new ExpiryUpdate(expiry, version); - } - public boolean hasMessage() throws IOException { return r.hasStruct(MESSAGE); } @@ -173,6 +149,30 @@ class ProtocolReaderImpl implements ProtocolReader { return new Request(b, length); } + public boolean hasRetentionAck() throws IOException { + return r.hasStruct(RETENTION_ACK); + } + + public RetentionAck readRetentionAck() throws IOException { + r.readStructId(RETENTION_ACK); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new RetentionAck(version); + } + + public boolean hasRetentionUpdate() throws IOException { + return r.hasStruct(RETENTION_UPDATE); + } + + public RetentionUpdate readRetentionUpdate() throws IOException { + r.readStructId(RETENTION_UPDATE); + long retention = r.readInt64(); + if(retention < 0L) throw new FormatException(); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new RetentionUpdate(retention, version); + } + public boolean hasSubscriptionAck() throws IOException { return r.hasStruct(SUBSCRIPTION_ACK); } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java index e345aa478f2a057ad4dc487fc823bcd33d330cbf..3e3f2e04c59f125e44be35c4670c9e62f0acad35 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java @@ -2,8 +2,8 @@ package net.sf.briar.protocol; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.protocol.Types.ACK; -import static net.sf.briar.api.protocol.Types.EXPIRY_ACK; -import static net.sf.briar.api.protocol.Types.EXPIRY_UPDATE; +import static net.sf.briar.api.protocol.Types.RETENTION_ACK; +import static net.sf.briar.api.protocol.Types.RETENTION_UPDATE; import static net.sf.briar.api.protocol.Types.GROUP; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; @@ -17,8 +17,8 @@ import java.io.OutputStream; import java.util.BitSet; import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.ExpiryAck; -import net.sf.briar.api.protocol.ExpiryUpdate; +import net.sf.briar.api.protocol.RetentionAck; +import net.sf.briar.api.protocol.RetentionUpdate; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; @@ -74,19 +74,6 @@ class ProtocolWriterImpl implements ProtocolWriter { if(flush) out.flush(); } - public void writeExpiryAck(ExpiryAck a) throws IOException { - w.writeStructId(EXPIRY_ACK); - w.writeInt64(a.getVersionNumber()); - if(flush) out.flush(); - } - - public void writeExpiryUpdate(ExpiryUpdate e) throws IOException { - w.writeStructId(EXPIRY_UPDATE); - w.writeInt64(e.getExpiryTime()); - w.writeInt64(e.getVersionNumber()); - if(flush) out.flush(); - } - public void writeMessage(byte[] raw) throws IOException { out.write(raw); if(flush) out.flush(); @@ -120,6 +107,19 @@ class ProtocolWriterImpl implements ProtocolWriter { if(flush) out.flush(); } + public void writeRetentionAck(RetentionAck a) throws IOException { + w.writeStructId(RETENTION_ACK); + w.writeInt64(a.getVersionNumber()); + if(flush) out.flush(); + } + + public void writeRetentionUpdate(RetentionUpdate u) throws IOException { + w.writeStructId(RETENTION_UPDATE); + w.writeInt64(u.getRetentionTime()); + w.writeInt64(u.getVersionNumber()); + if(flush) out.flush(); + } + public void writeSubscriptionAck(SubscriptionAck a) throws IOException { w.writeStructId(SUBSCRIPTION_ACK); w.writeInt64(a.getVersionNumber());