From 00bf1eac0acff7ff1c094012f01c92437bd7c887 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 27 Nov 2020 10:00:12 -0300 Subject: [PATCH] Factor out MessageEncoder from AvatarManager --- .../api/avatar/AvatarMessageEncoder.java | 18 +++++ .../briar/avatar/AvatarManagerImpl.java | 41 +++-------- .../avatar/AvatarMessageEncoderImpl.java | 70 +++++++++++++++++++ .../briar/avatar/AvatarModule.java | 8 +++ .../briar/avatar/AvatarManagerImplTest.java | 38 ++++------ 5 files changed, 122 insertions(+), 53 deletions(-) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java diff --git a/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java new file mode 100644 index 000000000..e186522f8 --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/avatar/AvatarMessageEncoder.java @@ -0,0 +1,18 @@ +package org.briarproject.briar.api.avatar; + +import org.briarproject.bramble.api.Pair; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.Message; + +import java.io.IOException; +import java.io.InputStream; + +public interface AvatarMessageEncoder { + /** + * Returns an update message and its metadata. + */ + Pair encodeUpdateMessage(GroupId groupId, + long version, String contentType, InputStream in) + throws IOException; +} diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java index 752a93ccf..ad0a1342f 100644 --- a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarManagerImpl.java @@ -1,12 +1,12 @@ package org.briarproject.briar.avatar; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactManager.ContactHook; import org.briarproject.bramble.api.data.BdfDictionary; -import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; @@ -25,15 +25,13 @@ import org.briarproject.bramble.api.sync.InvalidMessageException; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.validation.IncomingMessageHook; -import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook; import org.briarproject.briar.api.avatar.AvatarManager; +import org.briarproject.briar.api.avatar.AvatarMessageEncoder; import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent; import org.briarproject.briar.api.media.AttachmentHeader; -import org.briarproject.briar.api.media.FileTooBigException; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Map; @@ -42,13 +40,9 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; -import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; -import static org.briarproject.bramble.util.IoUtils.copyAndClose; +import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_CONTENT_TYPE; import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION; -import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE; -import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_CONTENT_TYPE; -import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH; @Immutable @NotNullByDefault @@ -61,7 +55,7 @@ class AvatarManagerImpl implements AvatarManager, OpenDatabaseHook, ContactHook, private final ClientVersioningManager clientVersioningManager; private final MetadataParser metadataParser; private final GroupFactory groupFactory; - private final Clock clock; + private final AvatarMessageEncoder avatarMessageEncoder; @Inject AvatarManagerImpl( @@ -71,14 +65,14 @@ class AvatarManagerImpl implements AvatarManager, OpenDatabaseHook, ContactHook, ClientVersioningManager clientVersioningManager, MetadataParser metadataParser, GroupFactory groupFactory, - Clock clock) { + AvatarMessageEncoder avatarMessageEncoder) { this.db = db; this.identityManager = identityManager; this.clientHelper = clientHelper; this.clientVersioningManager = clientVersioningManager; this.metadataParser = metadataParser; this.groupFactory = groupFactory; - this.clock = clock; + this.avatarMessageEncoder = avatarMessageEncoder; } @Override @@ -178,24 +172,11 @@ class AvatarManagerImpl implements AvatarManager, OpenDatabaseHook, ContactHook, db.endTransaction(txn); } long version = latest == null ? 0 : latest.version + 1; - // 0.0: Message Type, Version, Content-Type - BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType); - byte[] descriptor = clientHelper.toByteArray(list); - // add BdfList and stream content to body - ByteArrayOutputStream bodyOut = new ByteArrayOutputStream(); - bodyOut.write(descriptor); - copyAndClose(in, bodyOut); - if (bodyOut.size() > MAX_MESSAGE_BODY_LENGTH) - throw new FileTooBigException(); - // assemble message - byte[] body = bodyOut.toByteArray(); - long timestamp = clock.currentTimeMillis(); - Message m = clientHelper.createMessage(groupId, timestamp, body); - // add metadata to message - BdfDictionary meta = new BdfDictionary(); - meta.put(MSG_KEY_VERSION, version); - meta.put(MSG_KEY_CONTENT_TYPE, contentType); - meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptor.length); + // encode message and metadata + Pair encodedMessage = avatarMessageEncoder + .encodeUpdateMessage(groupId, version, contentType, in); + Message m = encodedMessage.getFirst(); + BdfDictionary meta = encodedMessage.getSecond(); // save/send avatar and delete old one return db.transactionWithResult(false, txn2 -> { // re-query latest update as it might have changed since last query diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java new file mode 100644 index 000000000..e1ff8c3c6 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarMessageEncoderImpl.java @@ -0,0 +1,70 @@ +package org.briarproject.briar.avatar; + +import org.briarproject.bramble.api.Pair; +import org.briarproject.bramble.api.client.ClientHelper; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfList; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.bramble.api.sync.Message; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.avatar.AvatarMessageEncoder; +import org.briarproject.briar.api.media.FileTooBigException; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; +import static org.briarproject.bramble.util.IoUtils.copyAndClose; +import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_CONTENT_TYPE; +import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH; +import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION; +import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE; + +@Immutable +@NotNullByDefault +class AvatarMessageEncoderImpl implements AvatarMessageEncoder { + + private final ClientHelper clientHelper; + private final Clock clock; + + @Inject + AvatarMessageEncoderImpl(ClientHelper clientHelper, Clock clock) { + this.clientHelper = clientHelper; + this.clock = clock; + } + + @Override + public Pair encodeUpdateMessage(GroupId groupId, + long version, String contentType, InputStream in) + throws IOException { + // 0.0: Message Type, Version, Content-Type + BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType); + byte[] descriptor = clientHelper.toByteArray(list); + + // add BdfList and stream content to body + ByteArrayOutputStream bodyOut = new ByteArrayOutputStream(); + bodyOut.write(descriptor); + copyAndClose(in, bodyOut); + if (bodyOut.size() > MAX_MESSAGE_BODY_LENGTH) + throw new FileTooBigException(); + + // assemble message + byte[] body = bodyOut.toByteArray(); + long timestamp = clock.currentTimeMillis(); + Message m = clientHelper.createMessage(groupId, timestamp, body); + + // encode metadata + BdfDictionary meta = new BdfDictionary(); + meta.put(MSG_KEY_VERSION, version); + meta.put(MSG_KEY_CONTENT_TYPE, contentType); + meta.put(MSG_KEY_DESCRIPTOR_LENGTH, descriptor.length); + + return new Pair<>(m, meta); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java index cfc9807e4..86c4696e2 100644 --- a/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/avatar/AvatarModule.java @@ -8,6 +8,7 @@ import org.briarproject.bramble.api.sync.validation.ValidationManager; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.briar.api.avatar.AvatarManager; +import org.briarproject.briar.api.avatar.AvatarMessageEncoder; import javax.inject.Inject; import javax.inject.Singleton; @@ -41,6 +42,13 @@ public class AvatarModule { return avatarValidator; } + @Provides + @Singleton + AvatarMessageEncoder provideMessageEncoder( + AvatarMessageEncoderImpl messageEncoder) { + return messageEncoder; + } + @Provides @Singleton AvatarManager provideAvatarManager( diff --git a/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java index c7029cd34..f4b1f43d4 100644 --- a/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/avatar/AvatarManagerImplTest.java @@ -1,12 +1,12 @@ package org.briarproject.briar.avatar; import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfEntry; -import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; @@ -24,10 +24,10 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.InvalidMessageException; import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.versioning.ClientVersioningManager; import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.DbExpectations; +import org.briarproject.briar.api.avatar.AvatarMessageEncoder; import org.briarproject.briar.api.avatar.event.AvatarUpdatedEvent; import org.briarproject.briar.api.media.AttachmentHeader; import org.jmock.Expectations; @@ -54,11 +54,10 @@ import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.briar.api.avatar.AvatarManager.CLIENT_ID; import static org.briarproject.briar.api.avatar.AvatarManager.MAJOR_VERSION; import static org.briarproject.briar.api.media.MediaConstants.MAX_CONTENT_TYPE_BYTES; -import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID; -import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION; -import static org.briarproject.briar.avatar.AvatarConstants.MSG_TYPE_UPDATE; import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_CONTENT_TYPE; import static org.briarproject.briar.api.media.MediaConstants.MSG_KEY_DESCRIPTOR_LENGTH; +import static org.briarproject.briar.avatar.AvatarConstants.GROUP_KEY_CONTACT_ID; +import static org.briarproject.briar.avatar.AvatarConstants.MSG_KEY_VERSION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -73,7 +72,8 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { private final MetadataParser metadataParser = context.mock(MetadataParser.class); private final GroupFactory groupFactory = context.mock(GroupFactory.class); - private final Clock clock = context.mock(Clock.class); + private final AvatarMessageEncoder avatarMessageEncoder = + context.mock(AvatarMessageEncoder.class); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final GroupId localGroupId = localGroup.getId(); @@ -93,11 +93,10 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { private final AvatarManagerImpl avatarManager = new AvatarManagerImpl(db, identityManager, clientHelper, clientVersioningManager, metadataParser, groupFactory, - clock); + avatarMessageEncoder); @Test - public void testOpenDatabaseHookWhenGroupExists() - throws DbException, FormatException { + public void testOpenDatabaseHookWhenGroupExists() throws DbException { Transaction txn = new Transaction(null, false); // local group already exists, so nothing more to do @@ -269,7 +268,7 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { @Test(expected = InvalidMessageException.class) public void testIncomingMessageInOwnGroup() - throws DbException, InvalidMessageException, FormatException { + throws DbException, InvalidMessageException { Transaction txn = new Transaction(null, false); expectGetOurGroup(txn); avatarManager.incomingMessage(txn, ourMsg, meta); @@ -283,8 +282,6 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { Transaction txn2 = new Transaction(null, false); long latestVersion = metaDict.getLong(MSG_KEY_VERSION); long version = latestVersion + 1; - BdfList list = BdfList.of(MSG_TYPE_UPDATE, version, contentType); - long now = System.currentTimeMillis(); Message newMsg = getMessage(localGroupId); BdfDictionary newMeta = BdfDictionary.of( new BdfEntry(MSG_KEY_VERSION, version), @@ -296,12 +293,9 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { will(returnValue(txn)); oneOf(db).commitTransaction(txn); oneOf(db).endTransaction(txn); - oneOf(clientHelper).toByteArray(list); - oneOf(clock).currentTimeMillis(); - will(returnValue(now)); - oneOf(clientHelper).createMessage(with(equal(localGroupId)), - with(equal(now)), with(any(byte[].class))); - will(returnValue(newMsg)); + oneOf(avatarMessageEncoder).encodeUpdateMessage(localGroupId, + version, contentType, inputStream); + will(returnValue(new Pair<>(newMsg, newMeta))); oneOf(db).transactionWithResult(with(false), withDbCallable(txn2)); oneOf(db).deleteMessage(txn2, ourMsg.getId()); oneOf(db).deleteMessageMetadata(txn2, ourMsg.getId()); @@ -336,11 +330,9 @@ public class AvatarManagerImplTest extends BrambleMockTestCase { will(returnValue(txn)); oneOf(db).commitTransaction(txn); oneOf(db).endTransaction(txn); - oneOf(clientHelper).toByteArray(with(any(BdfList.class))); - oneOf(clock).currentTimeMillis(); - oneOf(clientHelper).createMessage(with(equal(localGroupId)), - with(any(long.class)), with(any(byte[].class))); - will(returnValue(newMsg)); + oneOf(avatarMessageEncoder).encodeUpdateMessage(localGroupId, + latestVersion + 1, contentType, inputStream); + will(returnValue(new Pair<>(newMsg, newMeta))); oneOf(db).transactionWithResult(with(false), withDbCallable(txn2)); // no deletion or storing happening }});