Store message count, unread count and timestamp of latest message

in group metadata to be able to speed up group listings.

Closes #584, #586, #585
This commit is contained in:
Torsten Grote
2016-10-04 13:37:29 -03:00
parent 3fa84ec7a8
commit a727a0817e
25 changed files with 421 additions and 141 deletions

View File

@@ -8,6 +8,7 @@ import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.blogs.BlogManager; import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPostFactory; import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
@@ -25,6 +26,7 @@ import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.SyncSession; import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory; import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.sync.ValidationManager.State; import org.briarproject.api.sync.ValidationManager.State;
@@ -62,12 +64,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class BlogSharingIntegrationTest extends BriarTestCase { public class BlogSharingIntegrationTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1, private LifecycleManager lifecycleManager0, lifecycleManager1,
lifecycleManager2; lifecycleManager2;
private SyncSessionFactory sync0, sync1, sync2; private SyncSessionFactory sync0, sync1;
private BlogManager blogManager0, blogManager1, blogManager2; private BlogManager blogManager0, blogManager1;
private ContactManager contactManager0, contactManager1, contactManager2; private ContactManager contactManager0, contactManager1, contactManager2;
private Contact contact1, contact2, contact01, contact02; private Contact contact1, contact2, contact01, contact02;
private ContactId contactId1, contactId2, contactId01, contactId02; private ContactId contactId1, contactId2, contactId01, contactId02;
@@ -83,6 +85,8 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
@Inject @Inject
AuthorFactory authorFactory; AuthorFactory authorFactory;
@Inject @Inject
ContactGroupFactory contactGroupFactory;
@Inject
BlogPostFactory blogPostFactory; BlogPostFactory blogPostFactory;
@Inject @Inject
CryptoComponent cryptoComponent; CryptoComponent cryptoComponent;
@@ -138,13 +142,11 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
contactManager2 = t2.getContactManager(); contactManager2 = t2.getContactManager();
blogManager0 = t0.getBlogManager(); blogManager0 = t0.getBlogManager();
blogManager1 = t1.getBlogManager(); blogManager1 = t1.getBlogManager();
blogManager2 = t2.getBlogManager();
blogSharingManager0 = t0.getBlogSharingManager(); blogSharingManager0 = t0.getBlogSharingManager();
blogSharingManager1 = t1.getBlogSharingManager(); blogSharingManager1 = t1.getBlogSharingManager();
blogSharingManager2 = t2.getBlogSharingManager(); blogSharingManager2 = t2.getBlogSharingManager();
sync0 = t0.getSyncSessionFactory(); sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory(); sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test // initialize waiters fresh for each test
eventWaiter = new Waiter(); eventWaiter = new Waiter();
@@ -187,15 +189,23 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
// invitee has own blog and that of the sharer // invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size()); assertEquals(2, blogManager1.getBlogs().size());
// get sharing group and assert group message count
GroupId g = contactGroupFactory
.createContactGroup(blogSharingManager0.getClientId(),
contact1).getId();
assertGroupCount(blogSharingManager0, g, 1, 0);
// sync first request message // sync first request message
sync0To1(); sync0To1();
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived); assertTrue(listener1.requestReceived);
assertGroupCount(blogSharingManager1, g, 2, 1);
// sync response back // sync response back
sync1To0(); sync1To0();
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
assertGroupCount(blogSharingManager0, g, 2, 1);
// blog was added successfully // blog was added successfully
assertEquals(0, blogSharingManager0.getInvitations().size()); assertEquals(0, blogSharingManager0.getInvitations().size());
@@ -232,6 +242,10 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1)); assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1));
assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01)); assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01));
// group message count is still correct
assertGroupCount(blogSharingManager0, g, 2, 1);
assertGroupCount(blogSharingManager1, g, 2, 1);
stopLifecycles(); stopLifecycles();
} }
@@ -510,8 +524,7 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
private class SharerListener implements EventListener { private class SharerListener implements EventListener {
volatile boolean requestReceived = false; private volatile boolean responseReceived = false;
volatile boolean responseReceived = false;
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
@@ -534,7 +547,6 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
BlogInvitationReceivedEvent event = BlogInvitationReceivedEvent event =
(BlogInvitationReceivedEvent) e; (BlogInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId()); eventWaiter.assertEquals(contactId1, event.getContactId());
requestReceived = true;
Blog b = event.getBlog(); Blog b = event.getBlog();
try { try {
Contact c = contactManager0.getContact(contactId1); Contact c = contactManager0.getContact(contactId1);
@@ -550,17 +562,16 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
private class InviteeListener implements EventListener { private class InviteeListener implements EventListener {
volatile boolean requestReceived = false; private volatile boolean requestReceived = false;
volatile boolean responseReceived = false;
private final boolean accept, answer; private final boolean accept, answer;
InviteeListener(boolean accept, boolean answer) { private InviteeListener(boolean accept, boolean answer) {
this.accept = accept; this.accept = accept;
this.answer = answer; this.answer = answer;
} }
InviteeListener(boolean accept) { private InviteeListener(boolean accept) {
this(accept, true); this(accept, true);
} }
@@ -596,7 +607,6 @@ public class BlogSharingIntegrationTest extends BriarTestCase {
BlogInvitationResponseReceivedEvent event = BlogInvitationResponseReceivedEvent event =
(BlogInvitationResponseReceivedEvent) e; (BlogInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId01, event.getContactId()); eventWaiter.assertEquals(contactId01, event.getContactId());
responseReceived = true;
eventWaiter.resume(); eventWaiter.resume();
} }
} }

View File

@@ -0,0 +1,32 @@
package org.briarproject;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
import static org.junit.Assert.assertEquals;
public abstract class BriarIntegrationTest extends BriarTestCase {
// TODO maybe we could add uncaught exception handlers for other threads here (#670)
protected void assertGroupCount(MessageTracker tracker, GroupId g,
long msgCount, long unreadCount, long latestMsg)
throws DbException {
GroupCount groupCount = tracker.getGroupCount(g);
assertEquals(msgCount, groupCount.getMsgCount());
assertEquals(unreadCount, groupCount.getUnreadCount());
assertEquals(latestMsg, groupCount.getLatestMsgTime());
}
protected void assertGroupCount(MessageTracker tracker, GroupId g,
long msgCount, long unreadCount) throws DbException {
GroupCount c1 = tracker.getGroupCount(g);
assertEquals(msgCount, c1.getMsgCount());
assertEquals(unreadCount, c1.getUnreadCount());
}
}

View File

@@ -59,7 +59,7 @@ import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING; import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class ForumManagerTest { public class ForumManagerTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1; private LifecycleManager lifecycleManager0, lifecycleManager1;
private SyncSessionFactory sync0, sync1; private SyncSessionFactory sync0, sync1;
@@ -150,9 +150,19 @@ public class ForumManagerTest {
createForumPost(forum.getGroup().getId(), post1, body2, ms2); createForumPost(forum.getGroup().getId(), post1, body2, ms2);
assertEquals(ms2, post2.getMessage().getTimestamp()); assertEquals(ms2, post2.getMessage().getTimestamp());
forumManager0.addLocalPost(post1); forumManager0.addLocalPost(post1);
forumManager0.setReadFlag(post1.getMessage().getId(), true); forumManager0.setReadFlag(forum.getGroup().getId(),
post1.getMessage().getId(), true);
assertGroupCount(forumManager0, forum.getGroup().getId(), 1, 0,
post1.getMessage().getTimestamp());
forumManager0.addLocalPost(post2); forumManager0.addLocalPost(post2);
forumManager0.setReadFlag(post2.getMessage().getId(), false); forumManager0.setReadFlag(forum.getGroup().getId(),
post2.getMessage().getId(), false);
assertGroupCount(forumManager0, forum.getGroup().getId(), 2, 1,
post2.getMessage().getTimestamp());
forumManager0.setReadFlag(forum.getGroup().getId(),
post2.getMessage().getId(), false);
assertGroupCount(forumManager0, forum.getGroup().getId(), 2, 1,
post2.getMessage().getTimestamp());
Collection<ForumPostHeader> headers = Collection<ForumPostHeader> headers =
forumManager0.getPostHeaders(forum.getGroup().getId()); forumManager0.getPostHeaders(forum.getGroup().getId());
assertEquals(2, headers.size()); assertEquals(2, headers.size());
@@ -202,23 +212,29 @@ public class ForumManagerTest {
forumManager0.addLocalPost(post1); forumManager0.addLocalPost(post1);
assertEquals(1, forumManager0.getPostHeaders(g).size()); assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size()); assertEquals(0, forumManager1.getPostHeaders(g).size());
assertGroupCount(forumManager0, g, 1, 0, time);
assertGroupCount(forumManager1, g, 0, 0, 0);
// send post to 1 // send post to 1
sync0To1(); sync0To1();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
assertEquals(1, forumManager1.getPostHeaders(g).size()); assertEquals(1, forumManager1.getPostHeaders(g).size());
assertGroupCount(forumManager1, g, 1, 1, time);
// add another forum post // add another forum post
time = clock.currentTimeMillis(); long time2 = clock.currentTimeMillis();
ForumPost post2 = createForumPost(g, null, "b", time); ForumPost post2 = createForumPost(g, null, "b", time2);
forumManager1.addLocalPost(post2); forumManager1.addLocalPost(post2);
assertEquals(1, forumManager0.getPostHeaders(g).size()); assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(2, forumManager1.getPostHeaders(g).size()); assertEquals(2, forumManager1.getPostHeaders(g).size());
assertGroupCount(forumManager0, g, 1, 0, time);
assertGroupCount(forumManager1, g, 2, 1, time2);
// send post to 0 // send post to 0
sync1To0(); sync1To0();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
assertEquals(2, forumManager1.getPostHeaders(g).size()); assertEquals(2, forumManager1.getPostHeaders(g).size());
assertGroupCount(forumManager0, g, 2, 1, time2);
stopLifecycles(); stopLifecycles();
} }

View File

@@ -4,7 +4,7 @@ import android.support.annotation.Nullable;
import net.jodah.concurrentunit.Waiter; import net.jodah.concurrentunit.Waiter;
import org.briarproject.BriarTestCase; import org.briarproject.BriarIntegrationTest;
import org.briarproject.TestDatabaseModule; import org.briarproject.TestDatabaseModule;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
@@ -99,7 +99,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class IntroductionIntegrationTest extends BriarTestCase { public class IntroductionIntegrationTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1, private LifecycleManager lifecycleManager0, lifecycleManager1,
lifecycleManager2; lifecycleManager2;
@@ -200,29 +200,43 @@ public class IntroductionIntegrationTest extends BriarTestCase {
introductionManager0 introductionManager0
.makeIntroduction(introducee1, introducee2, "Hi!", time); .makeIntroduction(introducee1, introducee2, "Hi!", time);
// check that messages are tracked properly
Group g1 = introductionGroupFactory
.createIntroductionGroup(introducee1);
Group g2 = introductionGroupFactory
.createIntroductionGroup(introducee2);
assertGroupCount(introductionManager0, g1.getId(), 1, 0, time);
assertGroupCount(introductionManager0, g2.getId(), 1, 0, time);
// sync first request message // sync first request message
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1"); deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived); assertTrue(listener1.requestReceived);
assertGroupCount(introductionManager1, g1.getId(), 2, 1);
// sync second request message // sync second request message
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2"); deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener2.requestReceived); assertTrue(listener2.requestReceived);
assertGroupCount(introductionManager2, g2.getId(), 2, 1);
// sync first response // sync first response
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0"); deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.response1Received); assertTrue(listener0.response1Received);
assertGroupCount(introductionManager0, g1.getId(), 2, 1);
// sync second response // sync second response
deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0"); deliverMessage(sync2, contactId2, sync0, contactId0, "2 to 0");
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.response2Received); assertTrue(listener0.response2Received);
assertGroupCount(introductionManager0, g2.getId(), 2, 1);
// sync forwarded responses to introducees // sync forwarded responses to introducees
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1"); deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2"); deliverMessage(sync0, contactId0, sync2, contactId2, "0 to 2");
assertGroupCount(introductionManager1, g1.getId(), 3, 2);
assertGroupCount(introductionManager2, g2.getId(), 3, 2);
// sync first ACK and its forward // sync first ACK and its forward
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0"); deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
@@ -255,6 +269,10 @@ public class IntroductionIntegrationTest extends BriarTestCase {
} }
assertDefaultUiMessages(); assertDefaultUiMessages();
assertGroupCount(introductionManager0, g1.getId(), 2, 1);
assertGroupCount(introductionManager0, g2.getId(), 2, 1);
assertGroupCount(introductionManager1, g1.getId(), 3, 2);
assertGroupCount(introductionManager2, g2.getId(), 3, 2);
} finally { } finally {
stopLifecycles(); stopLifecycles();
} }

View File

@@ -499,7 +499,7 @@ public class ConversationActivity extends BriarActivity
for (MessageId m : unread) for (MessageId m : unread)
// not really clean, but the messaging manager can // not really clean, but the messaging manager can
// handle introduction messages as well // handle introduction messages as well
messagingManager.setReadFlag(m, true); messagingManager.setReadFlag(groupId, m, true);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Marking read took " + duration + " ms"); LOG.info("Marking read took " + duration + " ms");
@@ -614,7 +614,7 @@ public class ConversationActivity extends BriarActivity
@Override @Override
public void run() { public void run() {
try { try {
messagingManager.setReadFlag(m, true); messagingManager.setReadFlag(groupId, m, true);
loadMessages(); loadMessages();
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))

View File

@@ -291,13 +291,15 @@ public class ForumControllerImpl extends DbControllerImpl
@Override @Override
public void entriesRead(final Collection<ForumEntry> forumEntries) { public void entriesRead(final Collection<ForumEntry> forumEntries) {
if (forum == null) return;
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
for (ForumEntry fe : forumEntries) { for (ForumEntry fe : forumEntries) {
forumManager.setReadFlag(fe.getId(), true); forumManager
.setReadFlag(forum.getId(), fe.getId(), true);
} }
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))

View File

@@ -0,0 +1,44 @@
package org.briarproject.api.clients;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
public interface MessageTracker {
/**
* Gets the number of visible and unread messages in the group
* as well as the timestamp of the latest message
**/
GroupCount getGroupCount(GroupId g) throws DbException;
/**
* Marks a message as read or unread and updates the group counts in g.
**/
void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException;
class GroupCount {
private final long msgCount, unreadCount, latestMsgTime;
public GroupCount(long msgCount, long unreadCount, long latestMsgTime) {
this.msgCount = msgCount;
this.unreadCount = unreadCount;
this.latestMsgTime = latestMsgTime;
}
public long getMsgCount() {
return msgCount;
}
public long getUnreadCount() {
return unreadCount;
}
public long getLatestMsgTime() {
return latestMsgTime;
}
}
}

View File

@@ -28,6 +28,5 @@ public interface ForumConstants {
String KEY_PUBLIC_NAME = "publicKey"; String KEY_PUBLIC_NAME = "publicKey";
String KEY_AUTHOR = "author"; String KEY_AUTHOR = "author";
String KEY_LOCAL = "local"; String KEY_LOCAL = "local";
String KEY_READ = "read";
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.forum; package org.briarproject.api.forum;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
@@ -8,7 +9,7 @@ import org.briarproject.api.sync.MessageId;
import java.util.Collection; import java.util.Collection;
public interface ForumManager { public interface ForumManager extends MessageTracker {
/** Returns the unique ID of the forum client. */ /** Returns the unique ID of the forum client. */
ClientId getClientId(); ClientId getClientId();
@@ -37,9 +38,6 @@ public interface ForumManager {
/** Returns the headers of all posts in the given forum. */ /** Returns the headers of all posts in the given forum. */
Collection<ForumPostHeader> getPostHeaders(GroupId g) throws DbException; Collection<ForumPostHeader> getPostHeaders(GroupId g) throws DbException;
/** Marks a forum post as read or unread. */
void setReadFlag(MessageId m, boolean read) throws DbException;
/** Registers a hook to be called whenever a forum is removed. */ /** Registers a hook to be called whenever a forum is removed. */
void registerRemoveForumHook(RemoveForumHook hook); void registerRemoveForumHook(RemoveForumHook hook);

View File

@@ -45,7 +45,6 @@ public interface IntroductionConstants {
String CONTACT_ID_2 = "contactId2"; String CONTACT_ID_2 = "contactId2";
String RESPONSE_1 = "response1"; String RESPONSE_1 = "response1";
String RESPONSE_2 = "response2"; String RESPONSE_2 = "response2";
String READ = "read";
/* Introduction Request Action */ /* Introduction Request Action */
String PUBLIC_KEY1 = "publicKey1"; String PUBLIC_KEY1 = "publicKey1";

View File

@@ -1,6 +1,7 @@
package org.briarproject.api.introduction; package org.briarproject.api.introduction;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -9,7 +10,7 @@ import org.briarproject.api.sync.ClientId;
import java.util.Collection; import java.util.Collection;
public interface IntroductionManager { public interface IntroductionManager extends MessageTracker {
/** Returns the unique ID of the introduction client. */ /** Returns the unique ID of the introduction client. */
ClientId getClientId(); ClientId getClientId();

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.messaging; package org.briarproject.api.messaging;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
@@ -8,7 +9,7 @@ import org.briarproject.api.sync.MessageId;
import java.util.Collection; import java.util.Collection;
public interface MessagingManager { public interface MessagingManager extends MessageTracker {
/** Returns the unique ID of the messaging client. */ /** Returns the unique ID of the messaging client. */
ClientId getClientId(); ClientId getClientId();
@@ -31,6 +32,4 @@ public interface MessagingManager {
/** Returns the body of the private message with the given ID. */ /** Returns the body of the private message with the given ID. */
byte[] getMessageBody(MessageId m) throws DbException; byte[] getMessageBody(MessageId m) throws DbException;
/** Marks a private message as read or unread. */
void setReadFlag(MessageId m, boolean read) throws DbException;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.privategroup; package org.briarproject.api.privategroup;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
@@ -9,7 +10,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection; import java.util.Collection;
public interface PrivateGroupManager { public interface PrivateGroupManager extends MessageTracker {
/** Returns the unique ID of the private group client. */ /** Returns the unique ID of the private group client. */
@NotNull @NotNull
@@ -40,7 +41,4 @@ public interface PrivateGroupManager {
@NotNull @NotNull
Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException; Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException;
/** Marks a group message as read or unread. */
void setReadFlag(MessageId m, boolean read) throws DbException;
} }

View File

@@ -16,7 +16,6 @@ public interface SharingConstants {
String STATE = "state"; String STATE = "state";
String LOCAL = "local"; String LOCAL = "local";
String TIME = "time"; String TIME = "time";
String READ = "read";
String IS_SHARER = "isSharer"; String IS_SHARER = "isSharer";
String SHAREABLE_ID = "shareableId"; String SHAREABLE_ID = "shareableId";
String INVITATION_MSG = "invitationMsg"; String INVITATION_MSG = "invitationMsg";

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.sharing; package org.briarproject.api.sharing;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -8,7 +9,7 @@ import org.briarproject.api.sync.GroupId;
import java.util.Collection; import java.util.Collection;
public interface SharingManager<S extends Shareable> { public interface SharingManager<S extends Shareable> extends MessageTracker {
/** Returns the unique ID of the group sharing client. */ /** Returns the unique ID of the group sharing client. */
ClientId getClientId(); ClientId getClientId();

View File

@@ -86,7 +86,6 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
"dafbe56f0c8971365cea4bb5f08ec9a6" + "dafbe56f0c8971365cea4bb5f08ec9a6" +
"1d686e058b943997b6ff259ba423f613")); "1d686e058b943997b6ff259ba423f613"));
private final DatabaseComponent db;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final ContactManager contactManager; private final ContactManager contactManager;
private final BlogFactory blogFactory; private final BlogFactory blogFactory;
@@ -98,9 +97,8 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper, MetadataParser metadataParser,
ContactManager contactManager, BlogFactory blogFactory, ContactManager contactManager, BlogFactory blogFactory,
BlogPostFactory blogPostFactory) { BlogPostFactory blogPostFactory) {
super(clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.db = db;
this.identityManager = identityManager; this.identityManager = identityManager;
this.contactManager = contactManager; this.contactManager = contactManager;
this.blogFactory = blogFactory; this.blogFactory = blogFactory;

View File

@@ -0,0 +1,11 @@
package org.briarproject.clients;
public interface BdfConstants {
String GROUP_KEY_MSG_COUNT = "messageCount";
String GROUP_KEY_UNREAD_COUNT = "unreadCount";
String GROUP_KEY_LATEST_MSG = "latestMessageTime";
String MSG_KEY_READ = "read";
}

View File

@@ -3,27 +3,38 @@ package org.briarproject.clients;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager.IncomingQueueMessageHook; import org.briarproject.api.clients.MessageQueueManager.IncomingQueueMessageHook;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.clients.QueueMessage; import org.briarproject.api.clients.QueueMessage;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataParser; import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Metadata; import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.ValidationManager.IncomingMessageHook; import org.briarproject.api.sync.ValidationManager.IncomingMessageHook;
import static org.briarproject.api.clients.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH; import static org.briarproject.api.clients.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_LATEST_MSG;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_MSG_COUNT;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_UNREAD_COUNT;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
public abstract class BdfIncomingMessageHook implements IncomingMessageHook, public abstract class BdfIncomingMessageHook implements IncomingMessageHook,
IncomingQueueMessageHook { IncomingQueueMessageHook, MessageTracker {
protected final DatabaseComponent db;
protected final ClientHelper clientHelper; protected final ClientHelper clientHelper;
protected final MetadataParser metadataParser; protected final MetadataParser metadataParser;
protected BdfIncomingMessageHook(ClientHelper clientHelper, protected BdfIncomingMessageHook(DatabaseComponent db,
MetadataParser metadataParser) { ClientHelper clientHelper, MetadataParser metadataParser) {
this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
} }
@@ -56,4 +67,102 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook,
throw new DbException(e); throw new DbException(e);
} }
} }
protected void trackIncomingMessage(Transaction txn, Message m)
throws DbException {
trackMessage(txn, m.getGroupId(), m.getTimestamp(), false);
}
protected void trackOutgoingMessage(Transaction txn, Message m)
throws DbException {
trackMessage(txn, m.getGroupId(), m.getTimestamp(), true);
}
protected void trackMessage(Transaction txn, GroupId g, long time,
boolean read) throws DbException {
GroupCount c = getGroupCount(txn, g);
long msgCount = c.getMsgCount() + 1;
long unreadCount = c.getUnreadCount() + (read ? 0 : 1);
long latestTime =
time > c.getLatestMsgTime() ? time : c.getLatestMsgTime();
storeGroupCount(txn, g,
new GroupCount(msgCount, unreadCount, latestTime));
}
@Override
public GroupCount getGroupCount(GroupId g) throws DbException {
GroupCount count;
Transaction txn = db.startTransaction(true);
try {
count = getGroupCount(txn, g);
txn.setComplete();
}
finally {
db.endTransaction(txn);
}
return count;
}
private GroupCount getGroupCount(Transaction txn, GroupId g)
throws DbException {
GroupCount count;
try {
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
count = new GroupCount(
d.getLong(GROUP_KEY_MSG_COUNT, 0L),
d.getLong(GROUP_KEY_UNREAD_COUNT, 0L),
d.getLong(GROUP_KEY_LATEST_MSG, 0L)
);
} catch (FormatException e) {
throw new DbException(e);
}
return count;
}
private void storeGroupCount(Transaction txn, GroupId g, GroupCount c)
throws DbException{
try {
BdfDictionary d = BdfDictionary.of(
new BdfEntry(GROUP_KEY_MSG_COUNT, c.getMsgCount()),
new BdfEntry(GROUP_KEY_UNREAD_COUNT, c.getUnreadCount()),
new BdfEntry(GROUP_KEY_LATEST_MSG, c.getLatestMsgTime())
);
clientHelper.mergeGroupMetadata(txn, g, d);
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public void setReadFlag(GroupId g, MessageId m, boolean read)
throws DbException {
Transaction txn = db.startTransaction(false);
try {
// check current read status of message
BdfDictionary old =
clientHelper.getMessageMetadataAsDictionary(txn, m);
boolean wasRead = old.getBoolean(MSG_KEY_READ, false);
// if status changed
if (wasRead != read) {
// mark individual message as read
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_READ, read);
clientHelper.mergeMessageMetadata(txn, m, meta);
// update unread counter in group metadata
GroupCount c = getGroupCount(txn, g);
BdfDictionary d = new BdfDictionary();
d.put(GROUP_KEY_UNREAD_COUNT,
c.getUnreadCount() + (read ? -1 : 1));
clientHelper.mergeGroupMetadata(txn, g, d);
}
txn.setComplete();
} catch (FormatException e) {
throw new DbException(e);
} finally {
db.endTransaction(txn);
}
}
} }

View File

@@ -45,9 +45,9 @@ import static org.briarproject.api.forum.ForumConstants.KEY_LOCAL;
import static org.briarproject.api.forum.ForumConstants.KEY_NAME; import static org.briarproject.api.forum.ForumConstants.KEY_NAME;
import static org.briarproject.api.forum.ForumConstants.KEY_PARENT; import static org.briarproject.api.forum.ForumConstants.KEY_PARENT;
import static org.briarproject.api.forum.ForumConstants.KEY_PUBLIC_NAME; import static org.briarproject.api.forum.ForumConstants.KEY_PUBLIC_NAME;
import static org.briarproject.api.forum.ForumConstants.KEY_READ;
import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP; import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP;
import static org.briarproject.api.identity.Author.Status.ANONYMOUS; import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
@@ -55,7 +55,6 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
"859a7be50dca035b64bd6902fb797097" "859a7be50dca035b64bd6902fb797097"
+ "795af837abbf8c16d750b3c2ccc186ea")); + "795af837abbf8c16d750b3c2ccc186ea"));
private final DatabaseComponent db;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final ForumFactory forumFactory; private final ForumFactory forumFactory;
private final List<RemoveForumHook> removeHooks; private final List<RemoveForumHook> removeHooks;
@@ -64,9 +63,8 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager, ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper, MetadataParser metadataParser,
ForumFactory forumFactory) { ForumFactory forumFactory) {
super(db, clientHelper, metadataParser);
super(clientHelper, metadataParser);
this.db = db;
this.identityManager = identityManager; this.identityManager = identityManager;
this.forumFactory = forumFactory; this.forumFactory = forumFactory;
removeHooks = new CopyOnWriteArrayList<RemoveForumHook>(); removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
@@ -76,6 +74,8 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
protected boolean incomingMessage(Transaction txn, Message m, BdfList body, protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException { BdfDictionary meta) throws DbException, FormatException {
trackIncomingMessage(txn, m);
ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta); ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta);
ForumPostReceivedEvent event = ForumPostReceivedEvent event =
new ForumPostReceivedEvent(post, m.getGroupId()); new ForumPostReceivedEvent(post, m.getGroupId());
@@ -119,6 +119,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
@Override @Override
public void addLocalPost(ForumPost p) throws DbException { public void addLocalPost(ForumPost p) throws DbException {
Transaction txn = db.startTransaction(false);
try { try {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp()); meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
@@ -132,10 +133,14 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
meta.put(KEY_AUTHOR, authorMeta); meta.put(KEY_AUTHOR, authorMeta);
} }
meta.put(KEY_LOCAL, true); meta.put(KEY_LOCAL, true);
meta.put(KEY_READ, true); meta.put(MSG_KEY_READ, true);
clientHelper.addLocalMessage(p.getMessage(), meta, true); clientHelper.addLocalMessage(txn, p.getMessage(), meta, true);
trackOutgoingMessage(txn, p.getMessage());
txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
db.endTransaction(txn);
} }
} }
@@ -230,17 +235,6 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
} }
} }
@Override
public void setReadFlag(MessageId m, boolean read) throws DbException {
try {
BdfDictionary meta = new BdfDictionary();
meta.put(KEY_READ, read);
clientHelper.mergeMessageMetadata(m, meta);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
@Override @Override
public void registerRemoveForumHook(RemoveForumHook hook) { public void registerRemoveForumHook(RemoveForumHook hook) {
removeHooks.add(hook); removeHooks.add(hook);
@@ -281,7 +275,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
status = identityManager.getAuthorStatus(txn, author.getId()); status = identityManager.getAuthorStatus(txn, author.getId());
} }
} }
boolean read = meta.getBoolean(KEY_READ); boolean read = meta.getBoolean(MSG_KEY_READ);
return new ForumPostHeader(id, parentId, timestamp, author, status, return new ForumPostHeader(id, parentId, timestamp, author, status,
read); read);

View File

@@ -60,7 +60,6 @@ import static org.briarproject.api.introduction.IntroductionConstants.MESSAGE_TI
import static org.briarproject.api.introduction.IntroductionConstants.MSG; import static org.briarproject.api.introduction.IntroductionConstants.MSG;
import static org.briarproject.api.introduction.IntroductionConstants.NAME; import static org.briarproject.api.introduction.IntroductionConstants.NAME;
import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.NOT_OUR_RESPONSE;
import static org.briarproject.api.introduction.IntroductionConstants.READ;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_ID;
import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US; import static org.briarproject.api.introduction.IntroductionConstants.REMOTE_AUTHOR_IS_US;
import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1; import static org.briarproject.api.introduction.IntroductionConstants.RESPONSE_1;
@@ -75,6 +74,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class IntroductionManagerImpl extends BdfIncomingMessageHook class IntroductionManagerImpl extends BdfIncomingMessageHook
implements IntroductionManager, Client, AddContactHook, implements IntroductionManager, Client, AddContactHook,
@@ -87,7 +87,6 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(IntroductionManagerImpl.class.getName()); Logger.getLogger(IntroductionManagerImpl.class.getName());
private final DatabaseComponent db;
private final IntroducerManager introducerManager; private final IntroducerManager introducerManager;
private final IntroduceeManager introduceeManager; private final IntroduceeManager introduceeManager;
private final IntroductionGroupFactory introductionGroupFactory; private final IntroductionGroupFactory introductionGroupFactory;
@@ -98,8 +97,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
IntroduceeManager introduceeManager, IntroduceeManager introduceeManager,
IntroductionGroupFactory introductionGroupFactory) { IntroductionGroupFactory introductionGroupFactory) {
super(clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.db = db;
this.introducerManager = introducerManager; this.introducerManager = introducerManager;
this.introduceeManager = introduceeManager; this.introduceeManager = introduceeManager;
this.introductionGroupFactory = introductionGroupFactory; this.introductionGroupFactory = introductionGroupFactory;
@@ -208,7 +206,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
*/ */
@Override @Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList body, protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary message) throws DbException { BdfDictionary message) throws DbException {
// Get message data and type // Get message data and type
GroupId groupId = m.getGroupId(); GroupId groupId = m.getGroupId();
@@ -237,6 +235,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
} }
try { try {
introduceeManager.incomingMessage(txn, state, message); introduceeManager.incomingMessage(txn, state, message);
trackIncomingMessage(txn, m);
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
introduceeManager.abort(txn, state); introduceeManager.abort(txn, state);
@@ -270,6 +269,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
deleteMessage(txn, m.getId()); deleteMessage(txn, m.getId());
} }
} }
if (type == TYPE_RESPONSE) trackIncomingMessage(txn, m);
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
if (role == ROLE_INTRODUCER) introducerManager.abort(txn, state); if (role == ROLE_INTRODUCER) introducerManager.abort(txn, state);
@@ -296,6 +296,10 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
introducerManager.makeIntroduction(txn, c1, c2, msg, timestamp); introducerManager.makeIntroduction(txn, c1, c2, msg, timestamp);
Group g1 = introductionGroupFactory.createIntroductionGroup(c1);
Group g2 = introductionGroupFactory.createIntroductionGroup(c2);
trackMessage(txn, g1.getId(), timestamp, true);
trackMessage(txn, g2.getId(), timestamp, true);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
@@ -315,6 +319,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
getSessionState(txn, g.getId(), sessionId.getBytes()); getSessionState(txn, g.getId(), sessionId.getBytes());
introduceeManager.acceptIntroduction(txn, state, timestamp); introduceeManager.acceptIntroduction(txn, state, timestamp);
trackMessage(txn, g.getId(), timestamp, true);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
@@ -334,6 +339,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
getSessionState(txn, g.getId(), sessionId.getBytes()); getSessionState(txn, g.getId(), sessionId.getBytes());
introduceeManager.declineIntroduction(txn, state, timestamp); introduceeManager.declineIntroduction(txn, state, timestamp);
trackMessage(txn, g.getId(), timestamp, true);
txn.setComplete(); txn.setComplete();
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
@@ -377,7 +383,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
boolean local; boolean local;
long time = msg.getLong(MESSAGE_TIME); long time = msg.getLong(MESSAGE_TIME);
boolean accepted = msg.getBoolean(ACCEPT, false); boolean accepted = msg.getBoolean(ACCEPT, false);
boolean read = msg.getBoolean(READ, false); boolean read = msg.getBoolean(MSG_KEY_READ, false);
AuthorId authorId; AuthorId authorId;
String name; String name;
if (type == TYPE_RESPONSE) { if (type == TYPE_RESPONSE) {

View File

@@ -33,6 +33,8 @@ import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class MessagingManagerImpl extends BdfIncomingMessageHook class MessagingManagerImpl extends BdfIncomingMessageHook
implements MessagingManager, Client, AddContactHook, RemoveContactHook { implements MessagingManager, Client, AddContactHook, RemoveContactHook {
@@ -40,16 +42,13 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
"6bcdc006c0910b0f44e40644c3b31f1a" "6bcdc006c0910b0f44e40644c3b31f1a"
+ "8bf9a6d6021d40d219c86b731b903070")); + "8bf9a6d6021d40d219c86b731b903070"));
private final DatabaseComponent db;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
@Inject @Inject
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
MetadataParser metadataParser, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory) { ContactGroupFactory contactGroupFactory) {
super(clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.db = db;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
} }
@@ -100,12 +99,13 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
long timestamp = meta.getLong("timestamp"); long timestamp = meta.getLong("timestamp");
String contentType = meta.getString("contentType"); String contentType = meta.getString("contentType");
boolean local = meta.getBoolean("local"); boolean local = meta.getBoolean("local");
boolean read = meta.getBoolean("read"); boolean read = meta.getBoolean(MSG_KEY_READ);
PrivateMessageHeader header = new PrivateMessageHeader( PrivateMessageHeader header = new PrivateMessageHeader(
m.getId(), timestamp, contentType, local, read, false, false); m.getId(), timestamp, contentType, local, read, false, false);
PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent( PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent(
header, groupId); header, groupId);
txn.attach(event); txn.attach(event);
trackIncomingMessage(txn, m);
// don't share message // don't share message
return false; return false;
@@ -113,6 +113,7 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
@Override @Override
public void addLocalMessage(PrivateMessage m) throws DbException { public void addLocalMessage(PrivateMessage m) throws DbException {
Transaction txn = db.startTransaction(false);
try { try {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put("timestamp", m.getMessage().getTimestamp()); meta.put("timestamp", m.getMessage().getTimestamp());
@@ -120,9 +121,13 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
meta.put("contentType", m.getContentType()); meta.put("contentType", m.getContentType());
meta.put("local", true); meta.put("local", true);
meta.put("read", true); meta.put("read", true);
clientHelper.addLocalMessage(m.getMessage(), meta, true); clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
trackOutgoingMessage(txn, m.getMessage());
txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} finally {
db.endTransaction(txn);
} }
} }
@@ -196,14 +201,4 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
} }
} }
@Override
public void setReadFlag(MessageId m, boolean read) throws DbException {
try {
BdfDictionary meta = new BdfDictionary();
meta.put("read", read);
clientHelper.mergeMessageMetadata(m, meta);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
} }

View File

@@ -14,6 +14,7 @@ import org.briarproject.clients.BdfMessageValidator;
import static org.briarproject.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH; import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class PrivateMessageValidator extends BdfMessageValidator { class PrivateMessageValidator extends BdfMessageValidator {
@@ -42,7 +43,7 @@ class PrivateMessageValidator extends BdfMessageValidator {
if (parentId != null) meta.put("parent", parentId); if (parentId != null) meta.put("parent", parentId);
meta.put("contentType", contentType); meta.put("contentType", contentType);
meta.put("local", false); meta.put("local", false);
meta.put("read", false); meta.put(MSG_KEY_READ, false);
return new BdfMessageContext(meta); return new BdfMessageContext(meta);
} }
} }

View File

@@ -28,8 +28,6 @@ import java.util.Collections;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.privategroup.Constants.KEY_READ;
public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
PrivateGroupManager { PrivateGroupManager {
@@ -37,7 +35,6 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
StringUtils.fromHexString("5072697661746547726f75704d616e61" StringUtils.fromHexString("5072697661746547726f75704d616e61"
+ "67657220627920546f727374656e2047")); + "67657220627920546f727374656e2047"));
private final DatabaseComponent db;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
@@ -46,9 +43,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
MetadataParser metadataParser, DatabaseComponent db, MetadataParser metadataParser, DatabaseComponent db,
IdentityManager identityManager, IdentityManager identityManager,
PrivateGroupFactory privateGroupFactory) { PrivateGroupFactory privateGroupFactory) {
super(clientHelper, metadataParser); super(db, clientHelper, metadataParser);
this.db = db;
this.identityManager = identityManager; this.identityManager = identityManager;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
} }
@@ -61,11 +57,16 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
@Override @Override
public void addLocalMessage(GroupMessage m) throws DbException { public void addLocalMessage(GroupMessage m) throws DbException {
Transaction txn = db.startTransaction(false);
try { try {
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
clientHelper.addLocalMessage(m.getMessage(), meta, true); clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
trackOutgoingMessage(txn, m.getMessage());
txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} finally {
db.endTransaction(txn);
} }
} }
@@ -104,21 +105,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
return Collections.emptyList(); return Collections.emptyList();
} }
@Override
public void setReadFlag(MessageId m, boolean read) throws DbException {
try {
BdfDictionary meta = new BdfDictionary();
meta.put(KEY_READ, read);
clientHelper.mergeMessageMetadata(m, meta);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
@Override @Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList body, protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException { BdfDictionary meta) throws DbException, FormatException {
trackIncomingMessage(txn, m);
return true; return true;
} }

View File

@@ -58,7 +58,6 @@ import static org.briarproject.api.clients.ProtocolEngine.StateUpdate;
import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID; import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.sharing.SharingConstants.LOCAL; import static org.briarproject.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.api.sharing.SharingConstants.READ;
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID; import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID;
import static org.briarproject.api.sharing.SharingConstants.SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.SHARED_BY_US;
@@ -83,6 +82,7 @@ import static org.briarproject.api.sharing.SharingConstants.TO_BE_SHARED_BY_US;
import static org.briarproject.api.sharing.SharingConstants.TYPE; import static org.briarproject.api.sharing.SharingConstants.TYPE;
import static org.briarproject.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.Invitation; import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent> abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent>
@@ -93,7 +93,6 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SharingManagerImpl.class.getName()); Logger.getLogger(SharingManagerImpl.class.getName());
private final DatabaseComponent db;
private final MessageQueueManager messageQueueManager; private final MessageQueueManager messageQueueManager;
private final MetadataEncoder metadataEncoder; private final MetadataEncoder metadataEncoder;
private final SecureRandom random; private final SecureRandom random;
@@ -106,9 +105,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
MetadataParser metadataParser, MetadataEncoder metadataEncoder, MetadataParser metadataParser, MetadataEncoder metadataEncoder,
SecureRandom random, ContactGroupFactory contactGroupFactory, SecureRandom random, ContactGroupFactory contactGroupFactory,
Clock clock) { Clock clock) {
super(db, clientHelper, metadataParser);
super(clientHelper, metadataParser);
this.db = db;
this.messageQueueManager = messageQueueManager; this.messageQueueManager = messageQueueManager;
this.metadataEncoder = metadataEncoder; this.metadataEncoder = metadataEncoder;
this.random = random; this.random = random;
@@ -226,6 +224,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
new InviteeEngine<IS, IR>(getIRFactory()); new InviteeEngine<IS, IR>(getIRFactory());
processInviteeStateUpdate(txn, m.getId(), processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
trackIncomingMessage(txn, m);
} catch (FormatException e) { } catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
deleteMessage(txn, m.getId()); deleteMessage(txn, m.getId());
@@ -239,6 +238,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
getIRRFactory()); getIRRFactory());
processSharerStateUpdate(txn, m.getId(), processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
trackIncomingMessage(txn, m);
} else if (msg.getType() == SHARE_MSG_TYPE_LEAVE || } else if (msg.getType() == SHARE_MSG_TYPE_LEAVE ||
msg.getType() == SHARE_MSG_TYPE_ABORT) { msg.getType() == SHARE_MSG_TYPE_ABORT) {
// we don't know who we are, so figure it out // we don't know who we are, so figure it out
@@ -290,6 +290,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
engine.onLocalAction(localState, engine.onLocalAction(localState,
SharerSessionState.Action.LOCAL_INVITATION)); SharerSessionState.Action.LOCAL_INVITATION));
// track message
long time = clock.currentTimeMillis();
trackMessage(txn, localState.getGroupId(), time, true);
txn.setComplete(); txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(); throw new DbException();
@@ -321,6 +325,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
processInviteeStateUpdate(txn, null, processInviteeStateUpdate(txn, null,
engine.onLocalAction(localState, localAction)); engine.onLocalAction(localState, localAction));
// track message
long time = clock.currentTimeMillis();
trackMessage(txn, localState.getGroupId(), time, true);
txn.setComplete(); txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
@@ -352,7 +360,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
db.getMessageStatus(txn, contactId, m.getKey()); db.getMessageStatus(txn, contactId, m.getKey());
long time = d.getLong(TIME); long time = d.getLong(TIME);
boolean local = d.getBoolean(LOCAL); boolean local = d.getBoolean(LOCAL);
boolean read = d.getBoolean(READ, false); boolean read = d.getBoolean(MSG_KEY_READ, false);
boolean available = false; boolean available = false;
if (type == SHARE_MSG_TYPE_INVITATION) { if (type == SHARE_MSG_TYPE_INVITATION) {

View File

@@ -4,28 +4,24 @@ import org.briarproject.BriarTestCase;
import org.briarproject.TestUtils; import org.briarproject.TestUtils;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry; import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.data.MetadataParser; import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus; import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.system.Clock;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
@@ -46,30 +42,29 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_LATEST_MSG;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_MSG_COUNT;
import static org.briarproject.clients.BdfConstants.GROUP_KEY_UNREAD_COUNT;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
public class IntroductionManagerImplTest extends BriarTestCase { public class IntroductionManagerImplTest extends BriarTestCase {
final Mockery context; private final Mockery context;
final IntroductionManagerImpl introductionManager; private final IntroductionManagerImpl introductionManager;
final IntroducerManager introducerManager; private final IntroducerManager introducerManager;
final IntroduceeManager introduceeManager; private final IntroduceeManager introduceeManager;
final DatabaseComponent db; private final DatabaseComponent db;
final ContactGroupFactory contactGroupFactory; private final ClientHelper clientHelper;
final ClientHelper clientHelper; private final IntroductionGroupFactory introductionGroupFactory;
final MetadataEncoder metadataEncoder; private final SessionId sessionId = new SessionId(TestUtils.getRandomId());
final MessageQueueManager messageQueueManager; private final long time = 42L;
final IntroductionGroupFactory introductionGroupFactory; private final Contact introducee1;
final Clock clock; private final Contact introducee2;
final SessionId sessionId = new SessionId(TestUtils.getRandomId()); private final Group introductionGroup1;
final long time = 42L; private final Group introductionGroup2;
final Contact introducee1; private final Message message1;
final Contact introducee2; private Transaction txn;
final Group localGroup0; private BdfDictionary metadataBefore, metadataAfter;
final Group introductionGroup1;
final Group introductionGroup2;
final Message message1;
Transaction txn;
public IntroductionManagerImplTest() { public IntroductionManagerImplTest() {
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
@@ -89,8 +84,6 @@ public class IntroductionManagerImplTest extends BriarTestCase {
new Contact(contactId2, author2, localAuthorId2, true, true); new Contact(contactId2, author2, localAuthorId2, true, true);
ClientId clientId = new ClientId(TestUtils.getRandomId()); ClientId clientId = new ClientId(TestUtils.getRandomId());
localGroup0 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]);
introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()), introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
clientId, new byte[0]); clientId, new byte[0]);
introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()), introductionGroup2 = new Group(new GroupId(TestUtils.getRandomId()),
@@ -102,6 +95,14 @@ public class IntroductionManagerImplTest extends BriarTestCase {
time, time,
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1) TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
); );
metadataBefore = BdfDictionary.of(
new BdfEntry(GROUP_KEY_MSG_COUNT, 41L),
new BdfEntry(GROUP_KEY_LATEST_MSG, 0L)
);
metadataAfter = BdfDictionary.of(
new BdfEntry(GROUP_KEY_MSG_COUNT, 42L),
new BdfEntry(GROUP_KEY_LATEST_MSG, time)
);
// mock ALL THE THINGS!!! // mock ALL THE THINGS!!!
context = new Mockery(); context = new Mockery();
@@ -109,15 +110,9 @@ public class IntroductionManagerImplTest extends BriarTestCase {
introducerManager = context.mock(IntroducerManager.class); introducerManager = context.mock(IntroducerManager.class);
introduceeManager = context.mock(IntroduceeManager.class); introduceeManager = context.mock(IntroduceeManager.class);
db = context.mock(DatabaseComponent.class); db = context.mock(DatabaseComponent.class);
contactGroupFactory = context.mock(ContactGroupFactory.class);
clientHelper = context.mock(ClientHelper.class); clientHelper = context.mock(ClientHelper.class);
metadataEncoder =
context.mock(MetadataEncoder.class);
messageQueueManager =
context.mock(MessageQueueManager.class);
MetadataParser metadataParser = context.mock(MetadataParser.class); MetadataParser metadataParser = context.mock(MetadataParser.class);
introductionGroupFactory = context.mock(IntroductionGroupFactory.class); introductionGroupFactory = context.mock(IntroductionGroupFactory.class);
clock = context.mock(Clock.class);
introductionManager = new IntroductionManagerImpl( introductionManager = new IntroductionManagerImpl(
db, clientHelper, metadataParser, introducerManager, db, clientHelper, metadataParser, introducerManager,
@@ -135,6 +130,27 @@ public class IntroductionManagerImplTest extends BriarTestCase {
oneOf(introducerManager) oneOf(introducerManager)
.makeIntroduction(txn, introducee1, introducee2, null, .makeIntroduction(txn, introducee1, introducee2, null,
time); time);
// get both introduction groups
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee1);
will(returnValue(introductionGroup1));
oneOf(introductionGroupFactory)
.createIntroductionGroup(introducee2);
will(returnValue(introductionGroup2));
// track message for group 1
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup1.getId(),
metadataAfter);
// track message for group 2
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup2.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup2.getId(),
metadataAfter);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
@@ -163,6 +179,13 @@ public class IntroductionManagerImplTest extends BriarTestCase {
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state)); will(returnValue(state));
oneOf(introduceeManager).acceptIntroduction(txn, state, time); oneOf(introduceeManager).acceptIntroduction(txn, state, time);
// track message
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup1.getId(),
metadataAfter);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
@@ -191,6 +214,13 @@ public class IntroductionManagerImplTest extends BriarTestCase {
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state)); will(returnValue(state));
oneOf(introduceeManager).declineIntroduction(txn, state, time); oneOf(introduceeManager).declineIntroduction(txn, state, time);
// track message
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup1.getId(),
metadataAfter);
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
@@ -241,12 +271,22 @@ public class IntroductionManagerImplTest extends BriarTestCase {
final BdfDictionary state = new BdfDictionary(); final BdfDictionary state = new BdfDictionary();
txn = new Transaction(null, false); txn = new Transaction(null, false);
metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 1L);
metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 2L);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(introduceeManager) oneOf(introduceeManager)
.initialize(txn, introductionGroup1.getId(), msg); .initialize(txn, introductionGroup1.getId(), msg);
will(returnValue(state)); will(returnValue(state));
oneOf(introduceeManager) oneOf(introduceeManager)
.incomingMessage(txn, state, msg); .incomingMessage(txn, state, msg);
// track message
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup1.getId(),
metadataAfter);
}}); }});
introductionManager introductionManager
@@ -272,10 +312,20 @@ public class IntroductionManagerImplTest extends BriarTestCase {
txn = new Transaction(null, false); txn = new Transaction(null, false);
metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 41L);
metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 42L);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);
will(returnValue(state)); will(returnValue(state));
oneOf(introducerManager).incomingMessage(txn, state, msg); oneOf(introducerManager).incomingMessage(txn, state, msg);
// track message
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
introductionGroup1.getId());
will(returnValue(metadataBefore));
oneOf(clientHelper)
.mergeGroupMetadata(txn, introductionGroup1.getId(),
metadataAfter);
}}); }});
introductionManager introductionManager