diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index c4433c846e9ee5abceef41270ab73d6d9a14b8a8..b07d49a008e3f466338c27ab7dbf942e47338eb3 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -63,8 +63,11 @@ public interface DatabaseComponent { ContactId addContact(Map<String, Map<String, String>> transports, byte[] secret) throws DbException; - /** Adds a locally generated message to the database. */ - void addLocallyGeneratedMessage(Message m) throws DbException; + /** Adds a locally generated group message to the database. */ + void addLocalGroupMessage(Message m) throws DbException; + + /** Adds a locally generated private message to the database. */ + void addLocalPrivateMessage(Message m, ContactId c) throws DbException; /** * Finds any lost batches that were sent to the given contact, and marks any diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 677864257cb3642e1706cd898b55969b4f2b1701..ae45f60289cc9251773bcaa3e0822ba0ed4ac05e 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -1,6 +1,7 @@ package net.sf.briar.db; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -9,8 +10,8 @@ import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.db.DatabaseComponent; -import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DatabaseListener; +import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.Status; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.Message; @@ -175,8 +176,9 @@ DatabaseCleaner.Callback { * <p> * Locking: contacts read, messages write, messageStatuses write. */ - protected boolean storeMessage(Txn txn, Message m, ContactId sender) + protected boolean storeGroupMessage(Txn txn, Message m, ContactId sender) throws DbException { + if(m.getGroup() == null) throw new IllegalArgumentException(); boolean added = db.addMessage(txn, m); // Mark the message as seen by the sender MessageId id = m.getId(); @@ -198,6 +200,43 @@ DatabaseCleaner.Callback { return added; } + protected boolean storeMessages(Txn txn, ContactId c, + Collection<Message> messages) throws DbException { + boolean anyAdded = false; + for(Message m : messages) { + if(m.getGroup() == null) { + if(storePrivateMessage(txn, m, c, true)) anyAdded = true; + } else if(db.containsVisibleSubscription(txn, m.getGroup(), c, + m.getTimestamp())) { + if(storeGroupMessage(txn, m, c)) anyAdded = true; + } + } + return anyAdded; + } + + /** + * If the given message is already in the database, returns false. + * Otherwise stores the message and marks it as new or seen with respect to + * the given contact, depending on whether the message is outgoing or + * incoming, respectively. + * <p> + * Locking: contacts read, messages write, messageStatuses write. + */ + protected boolean storePrivateMessage(Txn txn, Message m, ContactId c, + boolean incoming) throws DbException { + if(m.getGroup() != null) throw new IllegalArgumentException(); + boolean added = db.addMessage(txn, m); + if(!added) return false; + MessageId id = m.getId(); + if(incoming) db.setStatus(txn, c, id, Status.SEEN); + else db.setStatus(txn, c, id, Status.NEW); + // Count the bytes stored + synchronized(spaceLock) { + bytesStoredSinceLastCheck += m.getSize(); + } + return true; + } + /** * Iteratively updates the sendability of a message's ancestors to reflect * a change in the message's sendability. Returns the number of ancestors diff --git a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java index f0211d04a8738617c0e0389339196b1b413351c1..b4b3bfeeb9b312b89403c3b4a09090c80d91390c 100644 --- a/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java +++ b/components/net/sf/briar/db/ReadWriteLockDatabaseComponent.java @@ -132,7 +132,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { return c; } - public void addLocallyGeneratedMessage(Message m) throws DbException { + public void addLocalGroupMessage(Message m) throws DbException { boolean added = false; waitForPermissionToWrite(); contactLock.readLock().lock(); @@ -150,7 +150,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { // predates the subscription if(db.containsSubscription(txn, m.getGroup(), m.getTimestamp())) { - added = storeMessage(txn, m, null); + added = storeGroupMessage(txn, m, null); } db.commitTransaction(txn); } catch(DbException e) { @@ -173,6 +173,38 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(added) callListeners(Event.MESSAGES_ADDED); } + public void addLocalPrivateMessage(Message m, ContactId c) + throws DbException { + boolean added = false; + waitForPermissionToWrite(); + contactLock.readLock().lock(); + try { + if(!containsContact(c)) throw new NoSuchContactException(); + messageLock.writeLock().lock(); + try { + messageStatusLock.writeLock().lock(); + try { + Txn txn = db.startTransaction(); + try { + added = storePrivateMessage(txn, m, c, false); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + messageStatusLock.writeLock().unlock(); + } + } finally { + messageLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + // Call the listeners outside the lock + if(added) callListeners(Event.MESSAGES_ADDED); + } + public void findLostBatches(ContactId c) throws DbException { // Find any lost batches that need to be retransmitted Collection<BatchId> lost; @@ -751,20 +783,7 @@ class ReadWriteLockDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { try { Txn txn = db.startTransaction(); try { - int received = 0, stored = 0; - for(Message m : b.getMessages()) { - received++; - if(db.containsVisibleSubscription(txn, - m.getGroup(), c, m.getTimestamp())) { - if(storeMessage(txn, m, c)) { - anyAdded = true; - stored++; - } - } - } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + received - + " messages, stored " + stored); + anyAdded = storeMessages(txn, c, b.getMessages()); db.addBatchToAck(txn, c, b.getId()); db.commitTransaction(txn); } catch(DbException e) { diff --git a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java index f27f9f86ffcd24de462fd6228805b5ee3549ffc3..4c7728f021db87acbfad11bfa8fa2ff6cb918550 100644 --- a/components/net/sf/briar/db/SynchronizedDatabaseComponent.java +++ b/components/net/sf/briar/db/SynchronizedDatabaseComponent.java @@ -109,7 +109,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { return c; } - public void addLocallyGeneratedMessage(Message m) throws DbException { + public void addLocalGroupMessage(Message m) throws DbException { boolean added = false; waitForPermissionToWrite(); synchronized(contactLock) { @@ -123,7 +123,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { // predates the subscription if(db.containsSubscription(txn, m.getGroup(), m.getTimestamp())) { - added = storeMessage(txn, m, null); + added = storeGroupMessage(txn, m, null); if(!added) { if(LOG.isLoggable(Level.FINE)) LOG.fine("Duplicate local message"); @@ -145,6 +145,28 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { if(added) callListeners(Event.MESSAGES_ADDED); } + public void addLocalPrivateMessage(Message m, ContactId c) + throws DbException { + boolean added = false; + waitForPermissionToWrite(); + synchronized(contactLock) { + synchronized(messageLock) { + synchronized(messageStatusLock) { + Txn txn = db.startTransaction(); + try { + added = storePrivateMessage(txn, m, c, false); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } + } + } + // Call the listeners outside the lock + if(added) callListeners(Event.MESSAGES_ADDED); + } + public void findLostBatches(ContactId c) throws DbException { // Find any lost batches that need to be retransmitted Collection<BatchId> lost; @@ -574,21 +596,7 @@ class SynchronizedDatabaseComponent<Txn> extends DatabaseComponentImpl<Txn> { synchronized(subscriptionLock) { Txn txn = db.startTransaction(); try { - int received = 0, stored = 0; - for(Message m : b.getMessages()) { - received++; - GroupId g = m.getGroup(); - if(db.containsVisibleSubscription(txn, g, c, - m.getTimestamp())) { - if(storeMessage(txn, m, c)) { - anyAdded = true; - stored++; - } - } - } - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Received " + received - + " messages, stored " + stored); + anyAdded = storeMessages(txn, c, b.getMessages()); db.addBatchToAck(txn, c, b.getId()); db.commitTransaction(txn); } catch(DbException e) { diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java index abcf512065429758dac2cfc0646d2de99faf3417..cdcef062c9b264935b2e521d8b8f881c5e175e76 100644 --- a/components/net/sf/briar/protocol/MessageReader.java +++ b/components/net/sf/briar/protocol/MessageReader.java @@ -123,8 +123,8 @@ class MessageReader implements ObjectReader<Message> { messageDigest.reset(); messageDigest.update(raw); MessageId id = new MessageId(messageDigest.digest()); - AuthorId authorId = author == null ? null : author.getId(); GroupId groupId = group == null ? null : group.getId(); + AuthorId authorId = author == null ? null : author.getId(); return new MessageImpl(id, parent, groupId, authorId, timestamp, raw); } } diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 0fb571e1685cf101b737487c6f6b2e7002245a02..ef0cc020fb708c4cd9fb7d12cca969d2c27fe885 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -376,7 +376,7 @@ public abstract class DatabaseComponentTest extends TestCase { }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); } @@ -399,7 +399,7 @@ public abstract class DatabaseComponentTest extends TestCase { }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); } @@ -431,7 +431,7 @@ public abstract class DatabaseComponentTest extends TestCase { }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); } @@ -467,7 +467,7 @@ public abstract class DatabaseComponentTest extends TestCase { }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); } @@ -1132,7 +1132,7 @@ public abstract class DatabaseComponentTest extends TestCase { DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addListener(listener); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); } @@ -1158,7 +1158,7 @@ public abstract class DatabaseComponentTest extends TestCase { DatabaseComponent db = createDatabaseComponent(database, cleaner); db.addListener(listener); - db.addLocallyGeneratedMessage(message); + db.addLocalGroupMessage(message); context.assertIsSatisfied(); }