From b91fe66461f18ef5ee642c48ba5b58d2aa41193f Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 16 Jul 2020 11:38:55 +0100 Subject: [PATCH] Broadcast an event when remote transport properties are updated. --- .../TransportPropertyConstants.java | 11 +++ ...RemoteTransportPropertiesUpdatedEvent.java | 35 +++++++++ .../TransportPropertyManagerImpl.java | 47 +++++++++++- .../TransportPropertyManagerImplTest.java | 75 ++++++++++++++++++- 4 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/properties/event/RemoteTransportPropertiesUpdatedEvent.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyConstants.java index 99f2fa798..3e6fe3865 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/TransportPropertyConstants.java @@ -35,4 +35,15 @@ public interface TransportPropertyConstants { * contact, as a BDF dictionary. */ String GROUP_KEY_DISCOVERED = "discovered"; + + /** + * Group metadata key for the contact's ID as a BDF long. + */ + String GROUP_KEY_CONTACT_ID = "contactId"; + + /** + * Group metadata key for the local group, indicating that contact IDs have + * been stored in the group metadata of contact groups. + */ + String GROUP_KEY_CONTACT_IDS_STORED = "contactIdsStored"; } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/properties/event/RemoteTransportPropertiesUpdatedEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/event/RemoteTransportPropertiesUpdatedEvent.java new file mode 100644 index 000000000..9d5c41954 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/properties/event/RemoteTransportPropertiesUpdatedEvent.java @@ -0,0 +1,35 @@ +package org.briarproject.bramble.api.properties.event; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.event.Event; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.properties.TransportProperties; + +import javax.annotation.concurrent.Immutable; + +/** + * An event that is broadcast when {@link TransportProperties} are received + * from a contact. + */ +@Immutable +@NotNullByDefault +public class RemoteTransportPropertiesUpdatedEvent extends Event { + + private final ContactId contactId; + private final TransportId transportId; + + public RemoteTransportPropertiesUpdatedEvent(ContactId contactId, + TransportId transportId) { + this.contactId = contactId; + this.transportId = transportId; + } + + public ContactId getContactId() { + return contactId; + } + + public TransportId getTransportId() { + return transportId; + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java index f0bcde0e6..3ef9236fa 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/properties/TransportPropertyManagerImpl.java @@ -18,6 +18,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportPropertyManager; +import org.briarproject.bramble.api.properties.event.RemoteTransportPropertiesUpdatedEvent; import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.GroupId; @@ -37,6 +38,8 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_CONTACT_ID; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_CONTACT_IDS_STORED; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID; @@ -74,7 +77,10 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, @Override public void onDatabaseOpened(Transaction txn) throws DbException { - if (db.containsGroup(txn, localGroup.getId())) return; + if (db.containsGroup(txn, localGroup.getId())) { + storeContactIdsForContactGroups(txn); + return; + } db.addGroup(txn, localGroup); // Set things up for any pre-existing contacts for (Contact c : db.getContacts(txn)) addingContact(txn, c); @@ -89,6 +95,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, Visibility client = clientVersioningManager.getClientVisibility(txn, c.getId(), CLIENT_ID, MAJOR_VERSION); db.setGroupVisibility(txn, c.getId(), g.getId(), client); + // Attach the contact ID to the group + storeContactId(txn, c, g); // Copy the latest local properties into the group Map local = getLocalProperties(txn); for (Entry e : local.entrySet()) { @@ -127,8 +135,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, // We've already received a newer update - delete this one db.deleteMessage(txn, m.getId()); db.deleteMessageMetadata(txn, m.getId()); + return false; } } + ContactId c = getContactId(txn, m.getGroupId()); + txn.attach(new RemoteTransportPropertiesUpdatedEvent(c, t)); } catch (FormatException e) { throw new InvalidMessageException(e); } @@ -224,6 +235,40 @@ class TransportPropertyManagerImpl implements TransportPropertyManager, }); } + private void storeContactIdsForContactGroups(Transaction txn) + throws DbException { + try { + BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn, + localGroup.getId()); + if (meta.containsKey(GROUP_KEY_CONTACT_IDS_STORED)) return; + for (Contact c : db.getContacts(txn)) { + storeContactId(txn, c, getContactGroup(c)); + } + meta.put(GROUP_KEY_CONTACT_IDS_STORED, true); + clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta); + } catch (FormatException e) { + throw new DbException(e); + } + } + + private void storeContactId(Transaction txn, Contact c, Group contactGroup) + throws DbException { + BdfDictionary meta = new BdfDictionary(); + meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt()); + try { + clientHelper.mergeGroupMetadata(txn, contactGroup.getId(), meta); + } catch (FormatException e) { + throw new AssertionError(e); + } + } + + private ContactId getContactId(Transaction txn, GroupId contactGroupId) + throws DbException, FormatException { + BdfDictionary meta = + clientHelper.getGroupMetadataAsDictionary(txn, contactGroupId); + return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue()); + } + private TransportProperties getRemoteProperties(Transaction txn, Contact c, TransportId t) throws DbException { Group g = getContactGroup(c); diff --git a/bramble-core/src/test/java/org/briarproject/bramble/properties/TransportPropertyManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/properties/TransportPropertyManagerImplTest.java index 6b11df71e..4d3831fdc 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/properties/TransportPropertyManagerImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/properties/TransportPropertyManagerImplTest.java @@ -32,6 +32,8 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_CONTACT_ID; +import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_CONTACT_IDS_STORED; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.GROUP_KEY_DISCOVERED; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_LOCAL; import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MSG_KEY_TRANSPORT_ID; @@ -94,6 +96,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { Transaction txn = new Transaction(null, false); Contact contact = getContact(); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); + BdfDictionary contactGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()) + ); context.checking(new Expectations() {{ oneOf(db).containsGroup(txn, localGroup.getId()); @@ -110,6 +115,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { will(returnValue(SHARED)); oneOf(db).setGroupVisibility(txn, contact.getId(), contactGroup.getId(), SHARED); + oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(), + contactGroupMeta); }}); // Copy the latest local properties into the group expectGetLocalProperties(txn); @@ -123,13 +130,57 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { } @Test - public void testDoesNotCreateGroupsAtStartupIfAlreadyCreated() + public void testAddsContactIdsToGroupsAtStartupIfAlreadyCreated() throws Exception { Transaction txn = new Transaction(null, false); + Contact contact = getContact(); + Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); + BdfDictionary contactGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()) + ); + BdfDictionary localGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_IDS_STORED, true) + ); context.checking(new Expectations() {{ oneOf(db).containsGroup(txn, localGroup.getId()); will(returnValue(true)); + // Contact IDs have not been added to contact groups + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(new BdfDictionary())); + // Add contact IDs to contact groups + oneOf(db).getContacts(txn); + will(returnValue(singletonList(contact))); + oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, + MAJOR_VERSION, contact); + will(returnValue(contactGroup)); + oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(), + contactGroupMeta); + // Remember that contact IDs have been added + oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(), + localGroupMeta); + }}); + + TransportPropertyManagerImpl t = createInstance(); + t.onDatabaseOpened(txn); + } + + @Test + public void testDoesNotAddContactIdsToGroupsAtStartupIfAlreadyAdded() + throws Exception { + Transaction txn = new Transaction(null, false); + BdfDictionary localGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_IDS_STORED, true) + ); + + context.checking(new Expectations() {{ + oneOf(db).containsGroup(txn, localGroup.getId()); + will(returnValue(true)); + // Contact IDs have already been added to contact groups + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + localGroup.getId()); + will(returnValue(localGroupMeta)); }}); TransportPropertyManagerImpl t = createInstance(); @@ -141,6 +192,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { Transaction txn = new Transaction(null, false); Contact contact = getContact(); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); + BdfDictionary contactGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()) + ); context.checking(new Expectations() {{ // Create the group and share it with the contact @@ -153,6 +207,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { will(returnValue(SHARED)); oneOf(db).setGroupVisibility(txn, contact.getId(), contactGroup.getId(), SHARED); + oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(), + contactGroupMeta); }}); // Copy the latest local properties into the group expectGetLocalProperties(txn); @@ -186,7 +242,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { public void testDoesNotDeleteAnythingWhenFirstUpdateIsDelivered() throws Exception { Transaction txn = new Transaction(null, false); + Contact contact = getContact(); GroupId contactGroupId = new GroupId(getRandomId()); + BdfDictionary contactGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()) + ); Message message = getMessage(contactGroupId); Metadata meta = new Metadata(); BdfDictionary metaDictionary = BdfDictionary.of( @@ -217,6 +277,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { oneOf(clientHelper).getMessageMetadataAsDictionary(txn, contactGroupId); will(returnValue(messageMetadata)); + // Look up the contact ID to broadcast an event + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + contactGroupId); + will(returnValue(contactGroupMeta)); }}); TransportPropertyManagerImpl t = createInstance(); @@ -227,7 +291,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { public void testDeletesOlderUpdatesWhenUpdateIsDelivered() throws Exception { Transaction txn = new Transaction(null, false); + Contact contact = getContact(); GroupId contactGroupId = new GroupId(getRandomId()); + BdfDictionary contactGroupMeta = BdfDictionary.of( + new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()) + ); Message message = getMessage(contactGroupId); Metadata meta = new Metadata(); // Version 4 is being delivered @@ -255,6 +323,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { // The previous update (version 3) should be deleted oneOf(db).deleteMessage(txn, fooVersion3); oneOf(db).deleteMessageMetadata(txn, fooVersion3); + // Look up the contact ID to broadcast an event + oneOf(clientHelper).getGroupMetadataAsDictionary(txn, + contactGroupId); + will(returnValue(contactGroupMeta)); }}); TransportPropertyManagerImpl t = createInstance(); @@ -292,6 +364,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase { // The update being delivered (version 3) should be deleted oneOf(db).deleteMessage(txn, message.getId()); oneOf(db).deleteMessageMetadata(txn, message.getId()); + // No event should be broadcast }}); TransportPropertyManagerImpl t = createInstance();