diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index d38dc08d686a20c2428404d5593302e675be95dc..5592042b12992ccb4cf6aed23a18a4c931f8e225 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -49,9 +49,9 @@ public interface DatabaseComponent { Transaction startTransaction() throws DbException; /** - * Ends a transaction. If the transaction's - * {@link Transaction#setComplete() commit} flag is set, the - * transaction is committed, otherwise it is aborted. + * Ends a transaction. If the transaction is marked as complete, the + * transaction is committed and any events attached to the transaction are + * broadcast; otherwise the transaction is aborted. */ void endTransaction(Transaction txn) throws DbException; diff --git a/briar-api/src/org/briarproject/api/db/Transaction.java b/briar-api/src/org/briarproject/api/db/Transaction.java index 46497a039e8dc8c43a99ca2e11165377030b871b..cb89f142ca3461582180f53f9ffb9e557e0cdc64 100644 --- a/briar-api/src/org/briarproject/api/db/Transaction.java +++ b/briar-api/src/org/briarproject/api/db/Transaction.java @@ -1,11 +1,19 @@ package org.briarproject.api.db; +import org.briarproject.api.event.Event; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * A wrapper around a database transaction. Transactions are not thread-safe. */ public class Transaction { private final Object txn; + + private List<Event> events = null; private boolean complete = false; public Transaction(Object txn) { @@ -20,6 +28,23 @@ public class Transaction { return txn; } + /** + * Attaches an event to be broadcast when the transaction has been + * committed. + */ + public void attach(Event e) { + if (events == null) events = new ArrayList<Event>(); + events.add(e); + } + + /** + * Returns any events attached to the transaction. + */ + public List<Event> getEvents() { + if (events == null) return Collections.emptyList(); + return events; + } + /** * Returns true if the transaction is ready to be committed. */ diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java index 7bdb3bc450357afc1f5b5c215e902a523ac76829..4edc0654584013b2350c7b85f6c44690d06b6438 100644 --- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java +++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java @@ -9,9 +9,6 @@ import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.db.NoSuchContactException; import org.briarproject.api.db.Transaction; -import org.briarproject.api.event.ContactAddedEvent; -import org.briarproject.api.event.ContactRemovedEvent; -import org.briarproject.api.event.EventBus; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook; @@ -37,14 +34,12 @@ class ContactManagerImpl implements ContactManager, Service, Logger.getLogger(ContactManagerImpl.class.getName()); private final DatabaseComponent db; - private final EventBus eventBus; private final List<AddContactHook> addHooks; private final List<RemoveContactHook> removeHooks; @Inject - ContactManagerImpl(DatabaseComponent db, EventBus eventBus) { + ContactManagerImpl(DatabaseComponent db) { this.db = db; - this.eventBus = eventBus; addHooks = new CopyOnWriteArrayList<AddContactHook>(); removeHooks = new CopyOnWriteArrayList<RemoveContactHook>(); } @@ -53,8 +48,6 @@ class ContactManagerImpl implements ContactManager, Service, public boolean start() { // Finish adding/removing any partly added/removed contacts try { - List<ContactId> added = new ArrayList<ContactId>(); - List<ContactId> removed = new ArrayList<ContactId>(); Transaction txn = db.startTransaction(); try { for (Contact c : db.getContacts(txn)) { @@ -62,22 +55,16 @@ class ContactManagerImpl implements ContactManager, Service, for (AddContactHook hook : addHooks) hook.addingContact(txn, c); db.setContactStatus(txn, c.getId(), ACTIVE); - added.add(c.getId()); } else if (c.getStatus().equals(REMOVING)) { for (RemoveContactHook hook : removeHooks) hook.removingContact(txn, c); db.removeContact(txn, c.getId()); - removed.add(c.getId()); } } txn.setComplete(); } finally { db.endTransaction(txn); } - for (ContactId c : added) - eventBus.broadcast(new ContactAddedEvent(c)); - for (ContactId c : removed) - eventBus.broadcast(new ContactRemovedEvent(c)); return true; } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -115,7 +102,6 @@ class ContactManagerImpl implements ContactManager, Service, } finally { db.endTransaction(txn); } - eventBus.broadcast(new ContactAddedEvent(c)); return c; } @@ -159,7 +145,6 @@ class ContactManagerImpl implements ContactManager, Service, } finally { db.endTransaction(txn); } - eventBus.broadcast(new ContactRemovedEvent(c)); } private void removeContact(Transaction txn, ContactId c) diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 8d4c71166571957f33fb02f291dedf4ca47318f1..00e064883cf0a5fb7ebf6e4597ccbb53c53d8b29 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -15,10 +15,15 @@ import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.db.NoSuchTransportException; import org.briarproject.api.db.StorageStatus; import org.briarproject.api.db.Transaction; +import org.briarproject.api.event.ContactAddedEvent; +import org.briarproject.api.event.ContactRemovedEvent; +import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; import org.briarproject.api.event.GroupVisibilityUpdatedEvent; +import org.briarproject.api.event.LocalAuthorAddedEvent; +import org.briarproject.api.event.LocalAuthorRemovedEvent; import org.briarproject.api.event.MessageAddedEvent; import org.briarproject.api.event.MessageRequestedEvent; import org.briarproject.api.event.MessageSharedEvent; @@ -66,8 +71,6 @@ import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN; import static org.briarproject.api.sync.ValidationManager.Validity.VALID; import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES; -// TODO: Callers should broadcast events after committing transactions - /** * An implementation of DatabaseComponent using reentrant read-write locks. * Depending on the JVM's lock implementation, this implementation may allow @@ -127,8 +130,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { public void endTransaction(Transaction transaction) throws DbException { T txn = txnClass.cast(transaction.unbox()); - if (transaction.isComplete()) db.commitTransaction(txn); - else db.abortTransaction(txn); + if (transaction.isComplete()) { + db.commitTransaction(txn); + for (Event e : transaction.getEvents()) eventBus.broadcast(e); + } else { + db.abortTransaction(txn); + } } private T unbox(Transaction transaction) { @@ -143,42 +150,40 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { throw new NoSuchLocalAuthorException(); if (db.containsContact(txn, remote.getId(), local)) throw new ContactExistsException(); - return db.addContact(txn, remote, local); + ContactId c = db.addContact(txn, remote, local); + transaction.attach(new ContactAddedEvent(c)); + return c; } public void addGroup(Transaction transaction, Group g) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsGroup(txn, g.getId())) { db.addGroup(txn, g); - added = true; + transaction.attach(new GroupAddedEvent(g)); } - if (added) eventBus.broadcast(new GroupAddedEvent(g)); } public void addLocalAuthor(Transaction transaction, LocalAuthor a) throws DbException { T txn = unbox(transaction); - if (!db.containsLocalAuthor(txn, a.getId())) + if (!db.containsLocalAuthor(txn, a.getId())) { db.addLocalAuthor(txn, a); + transaction.attach(new LocalAuthorAddedEvent(a.getId())); + } } public void addLocalMessage(Transaction transaction, Message m, ClientId c, Metadata meta, boolean shared) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsGroup(txn, m.getGroupId())) throw new NoSuchGroupException(); if (!db.containsMessage(txn, m.getId())) { addMessage(txn, m, VALID, shared, null); - added = true; + transaction.attach(new MessageAddedEvent(m, null)); + transaction.attach(new MessageValidatedEvent(m, c, true, true)); + if (shared) transaction.attach(new MessageSharedEvent(m)); } db.mergeMessageMetadata(txn, m.getId(), meta); - if (added) { - eventBus.broadcast(new MessageAddedEvent(m, null)); - eventBus.broadcast(new MessageValidatedEvent(m, c, true, true)); - if (shared) eventBus.broadcast(new MessageSharedEvent(m)); - } } /** @@ -206,13 +211,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { public void addTransport(Transaction transaction, TransportId t, int maxLatency) throws DbException { - boolean added = false; T txn = unbox(transaction); if (!db.containsTransport(txn, t)) { db.addTransport(txn, t, maxLatency); - added = true; + transaction.attach(new TransportAddedEvent(t, maxLatency)); } - if (added) eventBus.broadcast(new TransportAddedEvent(t, maxLatency)); } public void addTransportKeys(Transaction transaction, ContactId c, @@ -266,9 +269,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - if (messages.isEmpty()) return null; - if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); + if (ids.isEmpty()) return null; + db.lowerRequestedFlag(txn, c, ids); + transaction.attach(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } @@ -309,9 +312,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { messages.add(db.getRawMessage(txn, m)); db.updateExpiryTime(txn, c, m, maxLatency); } - if (!ids.isEmpty()) db.lowerRequestedFlag(txn, c, ids); - if (messages.isEmpty()) return null; - if (!ids.isEmpty()) eventBus.broadcast(new MessagesSentEvent(c, ids)); + if (ids.isEmpty()) return null; + db.lowerRequestedFlag(txn, c, ids); + transaction.attach(new MessagesSentEvent(c, ids)); return Collections.unmodifiableList(messages); } @@ -486,7 +489,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { public void mergeSettings(Transaction transaction, Settings s, String namespace) throws DbException { - boolean changed = false; T txn = unbox(transaction); Settings old = db.getSettings(txn, namespace); Settings merged = new Settings(); @@ -494,9 +496,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { merged.putAll(s); if (!merged.equals(old)) { db.mergeSettings(txn, s, namespace); - changed = true; + transaction.attach(new SettingsUpdatedEvent(namespace)); } - if (changed) eventBus.broadcast(new SettingsUpdatedEvent(namespace)); } public void receiveAck(Transaction transaction, ContactId c, Ack a) @@ -511,24 +512,21 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { acked.add(m); } } - eventBus.broadcast(new MessagesAckedEvent(c, acked)); + transaction.attach(new MessagesAckedEvent(c, acked)); } public void receiveMessage(Transaction transaction, ContactId c, Message m) throws DbException { - boolean duplicate, visible; T txn = unbox(transaction); if (!db.containsContact(txn, c)) throw new NoSuchContactException(); - duplicate = db.containsMessage(txn, m.getId()); - visible = db.containsVisibleGroup(txn, c, m.getGroupId()); - if (visible) { - if (!duplicate) addMessage(txn, m, UNKNOWN, false, c); + if (db.containsVisibleGroup(txn, c, m.getGroupId())) { + if (!db.containsMessage(txn, m.getId())) { + addMessage(txn, m, UNKNOWN, false, c); + transaction.attach(new MessageAddedEvent(m, c)); + } db.raiseAckFlag(txn, c, m.getId()); - } - if (visible) { - if (!duplicate) eventBus.broadcast(new MessageAddedEvent(m, c)); - eventBus.broadcast(new MessageToAckEvent(c)); + transaction.attach(new MessageToAckEvent(c)); } } @@ -550,8 +548,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { count++; } } - if (ack) eventBus.broadcast(new MessageToAckEvent(c)); - if (request) eventBus.broadcast(new MessageToRequestEvent(c)); + if (ack) transaction.attach(new MessageToAckEvent(c)); + if (request) transaction.attach(new MessageToRequestEvent(c)); } public void receiveRequest(Transaction transaction, ContactId c, Request r) @@ -567,7 +565,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { requested = true; } } - if (requested) eventBus.broadcast(new MessageRequestedEvent(c)); + if (requested) transaction.attach(new MessageRequestedEvent(c)); } public void removeContact(Transaction transaction, ContactId c) @@ -576,6 +574,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (!db.containsContact(txn, c)) throw new NoSuchContactException(); db.removeContact(txn, c); + transaction.attach(new ContactRemovedEvent(c)); } public void removeGroup(Transaction transaction, Group g) @@ -587,8 +586,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { throw new NoSuchGroupException(); affected = db.getVisibility(txn, id); db.removeGroup(txn, id); - eventBus.broadcast(new GroupRemovedEvent(g)); - eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected)); + transaction.attach(new GroupRemovedEvent(g)); + transaction.attach(new GroupVisibilityUpdatedEvent(affected)); } public void removeLocalAuthor(Transaction transaction, AuthorId a) @@ -597,6 +596,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (!db.containsLocalAuthor(txn, a)) throw new NoSuchLocalAuthorException(); db.removeLocalAuthor(txn, a); + transaction.attach(new LocalAuthorRemovedEvent(a)); } public void removeTransport(Transaction transaction, TransportId t) @@ -605,7 +605,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (!db.containsTransport(txn, t)) throw new NoSuchTransportException(); db.removeTransport(txn, t); - eventBus.broadcast(new TransportRemovedEvent(t)); + transaction.attach(new TransportRemovedEvent(t)); } public void setContactStatus(Transaction transaction, ContactId c, @@ -630,7 +630,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (!db.containsMessage(txn, m.getId())) throw new NoSuchMessageException(); db.setMessageShared(txn, m.getId(), shared); - if (shared) eventBus.broadcast(new MessageSharedEvent(m)); + if (shared) transaction.attach(new MessageSharedEvent(m)); } public void setMessageValid(Transaction transaction, Message m, ClientId c, @@ -639,7 +639,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (!db.containsMessage(txn, m.getId())) throw new NoSuchMessageException(); db.setMessageValid(txn, m.getId(), valid); - eventBus.broadcast(new MessageValidatedEvent(m, c, false, valid)); + transaction.attach(new MessageValidatedEvent(m, c, false, valid)); } public void setReorderingWindow(Transaction transaction, ContactId c, @@ -665,8 +665,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { if (visible && !wasVisible) db.addVisibility(txn, c, g); else if (!visible && wasVisible) db.removeVisibility(txn, c, g); if (visible != wasVisible) { - eventBus.broadcast(new GroupVisibilityUpdatedEvent( - Collections.singletonList(c))); + List<ContactId> affected = Collections.singletonList(c); + transaction.attach(new GroupVisibilityUpdatedEvent(affected)); } } diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index 475ead3e4a1321e7906a115b38a09c68f24e3319..93997dbb3d9a9128b53303ad6c3a4dd7d0c434c3 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -6,9 +6,6 @@ import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.db.NoSuchLocalAuthorException; import org.briarproject.api.db.Transaction; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.LocalAuthorAddedEvent; -import org.briarproject.api.event.LocalAuthorRemovedEvent; import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; @@ -32,14 +29,12 @@ class IdentityManagerImpl implements IdentityManager, Service { Logger.getLogger(IdentityManagerImpl.class.getName()); private final DatabaseComponent db; - private final EventBus eventBus; private final List<AddIdentityHook> addHooks; private final List<RemoveIdentityHook> removeHooks; @Inject - IdentityManagerImpl(DatabaseComponent db, EventBus eventBus) { + IdentityManagerImpl(DatabaseComponent db) { this.db = db; - this.eventBus = eventBus; addHooks = new CopyOnWriteArrayList<AddIdentityHook>(); removeHooks = new CopyOnWriteArrayList<RemoveIdentityHook>(); } @@ -48,8 +43,6 @@ class IdentityManagerImpl implements IdentityManager, Service { public boolean start() { // Finish adding/removing any partly added/removed pseudonyms try { - List<AuthorId> added = new ArrayList<AuthorId>(); - List<AuthorId> removed = new ArrayList<AuthorId>(); Transaction txn = db.startTransaction(); try { for (LocalAuthor a : db.getLocalAuthors(txn)) { @@ -57,22 +50,16 @@ class IdentityManagerImpl implements IdentityManager, Service { for (AddIdentityHook hook : addHooks) hook.addingIdentity(txn, a); db.setLocalAuthorStatus(txn, a.getId(), ACTIVE); - added.add(a.getId()); } else if (a.getStatus().equals(REMOVING)) { for (RemoveIdentityHook hook : removeHooks) hook.removingIdentity(txn, a); db.removeLocalAuthor(txn, a.getId()); - removed.add(a.getId()); } } txn.setComplete(); } finally { db.endTransaction(txn); } - for (AuthorId a : added) - eventBus.broadcast(new LocalAuthorAddedEvent(a)); - for (AuthorId a : removed) - eventBus.broadcast(new LocalAuthorRemovedEvent(a)); return true; } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -107,7 +94,6 @@ class IdentityManagerImpl implements IdentityManager, Service { } finally { db.endTransaction(txn); } - eventBus.broadcast(new LocalAuthorAddedEvent(localAuthor.getId())); } @Override @@ -154,6 +140,5 @@ class IdentityManagerImpl implements IdentityManager, Service { } finally { db.endTransaction(txn); } - eventBus.broadcast(new LocalAuthorRemovedEvent(a)); } } diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java index 48e6ea037b05ad57cf77e2a91952323e5f4d0385..a56542d38f28f542cf90237815945e34b4986576 100644 --- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java @@ -15,10 +15,14 @@ import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.db.NoSuchTransportException; import org.briarproject.api.db.StorageStatus; import org.briarproject.api.db.Transaction; +import org.briarproject.api.event.ContactAddedEvent; +import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; import org.briarproject.api.event.GroupVisibilityUpdatedEvent; +import org.briarproject.api.event.LocalAuthorAddedEvent; +import org.briarproject.api.event.LocalAuthorRemovedEvent; import org.briarproject.api.event.MessageAddedEvent; import org.briarproject.api.event.MessageRequestedEvent; import org.briarproject.api.event.MessageSharedEvent; @@ -136,6 +140,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(false)); oneOf(database).addLocalAuthor(txn, localAuthor); + oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); // addContact() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); @@ -143,6 +148,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(false)); oneOf(database).addContact(txn, author, localAuthorId); will(returnValue(contactId)); + oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); // getContacts() oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contact))); @@ -170,10 +176,12 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsContact(txn, contactId); will(returnValue(true)); oneOf(database).removeContact(txn, contactId); + oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class))); // removeLocalAuthor() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); oneOf(database).removeLocalAuthor(txn, localAuthorId); + oneOf(eventBus).broadcast(with(any(LocalAuthorRemovedEvent.class))); // endTransaction() oneOf(database).commitTransaction(txn); // close() @@ -743,6 +751,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(false)); oneOf(database).addLocalAuthor(txn, localAuthor); + oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); // addContact() oneOf(database).containsLocalAuthor(txn, localAuthorId); will(returnValue(true)); @@ -750,6 +759,7 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(false)); oneOf(database).addContact(txn, author, localAuthorId); will(returnValue(contactId)); + oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); // endTransaction() oneOf(database).commitTransaction(txn); // Check whether the transport is in the DB (which it's not) @@ -1137,8 +1147,6 @@ public class DatabaseComponentImplTest extends BriarTestCase { will(returnValue(txn)); oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).containsMessage(txn, messageId); - will(returnValue(false)); oneOf(database).containsVisibleGroup(txn, contactId, groupId); will(returnValue(false)); oneOf(database).commitTransaction(txn);