diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index 7e3d83c9df06f3710fdb934af8c1a44502bba1f9..069d9473f07f7f3f2918a5ac68944986efd447c2 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -21,18 +21,17 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventListener; import org.briarproject.api.event.ForumInvitationReceivedEvent; +import org.briarproject.api.event.ForumPostReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent; import org.briarproject.api.event.IntroductionSucceededEvent; -import org.briarproject.api.event.MessageStateChangedEvent; +import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.SettingsUpdatedEvent; -import org.briarproject.api.forum.ForumManager; import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.ServiceException; import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.settings.Settings; import org.briarproject.api.settings.SettingsManager; -import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import org.briarproject.util.StringUtils; @@ -59,7 +58,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; import static java.util.logging.Level.WARNING; import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE; -import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; class AndroidNotificationManagerImpl implements AndroidNotificationManager, Service, EventListener { @@ -78,7 +76,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private final Executor dbExecutor; private final SettingsManager settingsManager; private final MessagingManager messagingManager; - private final ForumManager forumManager; private final AndroidExecutor androidExecutor; private final Context appContext; @@ -94,14 +91,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, private volatile Settings settings = new Settings(); @Inject - public AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor, + AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor, SettingsManager settingsManager, MessagingManager messagingManager, - ForumManager forumManager, AndroidExecutor androidExecutor, - Application app) { + AndroidExecutor androidExecutor, Application app) { this.dbExecutor = dbExecutor; this.settingsManager = settingsManager; this.messagingManager = messagingManager; - this.forumManager = forumManager; this.androidExecutor = androidExecutor; appContext = app.getApplicationContext(); } @@ -157,15 +152,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, if (e instanceof SettingsUpdatedEvent) { SettingsUpdatedEvent s = (SettingsUpdatedEvent) e; if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings(); - } else if (e instanceof MessageStateChangedEvent) { - MessageStateChangedEvent m = (MessageStateChangedEvent) e; - if (!m.isLocal() && m.getState() == DELIVERED) { - ClientId c = m.getClientId(); - if (c.equals(messagingManager.getClientId())) - showPrivateMessageNotification(m.getMessage().getGroupId()); - else if (c.equals(forumManager.getClientId())) - showForumPostNotification(m.getMessage().getGroupId()); - } + } else if (e instanceof PrivateMessageReceivedEvent) { + PrivateMessageReceivedEvent m = (PrivateMessageReceivedEvent) e; + showPrivateMessageNotification(m.getGroupId()); + } else if (e instanceof ForumPostReceivedEvent) { + ForumPostReceivedEvent m = (ForumPostReceivedEvent) e; + showForumPostNotification(m.getGroupId()); } else if (e instanceof IntroductionRequestReceivedEvent) { ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId(); showNotificationForPrivateConversation(c); diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java index 0fd7ab99244f8d20f3d9a60de6bd9afdff8db4e2..77f574f20f4a2a18f4471c01e0d9e1e63006fd06 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java @@ -65,7 +65,6 @@ public class ForumActivity extends BriarActivity implements private static final Logger LOG = Logger.getLogger(ForumActivity.class.getName()); - public static final String MIN_TIMESTAMP = "briar.MIN_TIMESTAMP"; static final String FORUM_NAME = "briar.FORUM_NAME"; private static final int REQUEST_FORUM_SHARED = 3; @@ -341,7 +340,6 @@ public class ForumActivity extends BriarActivity implements final View chevron, replyButton; final ViewGroup cell; final View topDivider; - public ValueAnimator highlightAnimator; ForumViewHolder(View v) { super(v); @@ -438,7 +436,7 @@ public class ForumActivity extends BriarActivity implements // TODO This loop doesn't really loop. @ernir please review! for (int i = visiblePos + 1; i < getItemCount(); i++) { ForumEntry entry = getVisibleEntry(i); - if (entry.getLevel() <= levelLimit) + if (entry != null && entry.getLevel() <= levelLimit) break; return true; } @@ -486,7 +484,7 @@ public class ForumActivity extends BriarActivity implements for (int i = pos + 1; i < getItemCount(); i++) { ForumEntry entry = getVisibleEntry(i); - if (entry.getLevel() > levelLimit) { + if (entry != null && entry.getLevel() > levelLimit) { indexList.add(i); } else { break; @@ -613,6 +611,8 @@ public class ForumActivity extends BriarActivity implements public void onBindViewHolder( final ForumViewHolder ui, final int position) { final ForumEntry data = getVisibleEntry(position); + if (data == null) return; + if (!data.isRead()) { data.setRead(true); forumController.entryRead(data); diff --git a/briar-android/src/org/briarproject/android/forum/ForumController.java b/briar-android/src/org/briarproject/android/forum/ForumController.java index 2fa4f0dab0c58cbcd7017f717a44468f97bf7317..a3419b812f84edf39839f587186c2bcb2adff167 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumController.java +++ b/briar-android/src/org/briarproject/android/forum/ForumController.java @@ -19,7 +19,7 @@ public interface ForumController extends ActivityLifecycleController { void createPost(byte[] body); void createPost(byte[] body, MessageId parentId); - public interface ForumPostListener { + interface ForumPostListener { void addLocalEntry(int index, ForumEntry entry); void addForeignEntry(int index, ForumEntry entry); } diff --git a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java index 29ee3609e922ca6f91da3494ba94aebe87db2e2a..b5e83f8f4fa1c317eefc0130c2aed156e0cc2f87 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java +++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java @@ -13,12 +13,13 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.ForumPostReceivedEvent; import org.briarproject.api.event.GroupRemovedEvent; -import org.briarproject.api.event.MessageStateChangedEvent; import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumPost; import org.briarproject.api.forum.ForumPostFactory; import org.briarproject.api.forum.ForumPostHeader; +import org.briarproject.api.identity.Author; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sync.GroupId; @@ -38,7 +39,7 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; +import static org.briarproject.api.identity.Author.Status.VERIFIED; public class ForumControllerImpl extends DbControllerImpl implements ForumController, EventListener { @@ -52,7 +53,7 @@ public class ForumControllerImpl extends DbControllerImpl @CryptoExecutor protected Executor cryptoExecutor; @Inject - protected volatile ForumPostFactory forumPostFactory; + volatile ForumPostFactory forumPostFactory; @Inject protected volatile CryptoComponent crypto; @Inject @@ -65,7 +66,6 @@ public class ForumControllerImpl extends DbControllerImpl protected ForumPersistentData data; private ForumPostListener listener; - private MessageId localAdd = null; @Inject ForumControllerImpl() { @@ -100,51 +100,13 @@ public class ForumControllerImpl extends DbControllerImpl } } - private void findSingleNewEntry() { - runOnDbThread(new Runnable() { - @Override - public void run() { - List<ForumEntry> oldEntries = getForumEntries(); - data.clearHeaders(); - try { - loadPosts(); - List<ForumEntry> allEntries = getForumEntries(); - int i = 0; - for (ForumEntry entry : allEntries) { - boolean isNew = true; - for (ForumEntry oldEntry : oldEntries) { - if (entry.getMessageId() - .equals(oldEntry.getMessageId())) { - isNew = false; - break; - } - } - if (isNew) { - if (localAdd != null && - entry.getMessageId().equals(localAdd)) { - addLocalEntry(i, entry); - } else { - addForeignEntry(i, entry); - } - break; - } - i++; - } - } catch (DbException e) { - e.printStackTrace(); - } - } - }); - } - @Override public void eventOccurred(Event e) { - if (e instanceof MessageStateChangedEvent) { - MessageStateChangedEvent m = (MessageStateChangedEvent) e; - if (m.getState() == DELIVERED && - m.getMessage().getGroupId().equals(data.getGroupId())) { - LOG.info("Message added, reloading"); - findSingleNewEntry(); + if (e instanceof ForumPostReceivedEvent) { + ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e; + if (pe.getGroupId().equals(data.getGroupId())) { + LOG.info("Forum Post received, adding..."); + addNewPost(pe.getForumPostHeader()); } } else if (e instanceof GroupRemovedEvent) { GroupRemovedEvent s = (GroupRemovedEvent) e; @@ -160,6 +122,40 @@ public class ForumControllerImpl extends DbControllerImpl } } + private void addNewPost(final ForumPostHeader h) { + if (data == null) return; + runOnDbThread(new Runnable() { + @Override + public void run() { + data.addHeader(h); + data.clearForumEntries(); + + try { + byte[] body = forumManager.getPostBody(h.getId()); + data.addBody(h.getId(), body); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + + Author a = data.getLocalAuthor(); + // FIXME we should not need to calculate the index here + // the index is essentially stored in two different locations + int i = 0; + for (ForumEntry entry : getForumEntries()) { + if (entry.getMessageId().equals(h.getId())) { + if (a != null && a.equals(h.getAuthor())) { + addLocalEntry(i, entry); + } else { + addForeignEntry(i, entry); + } + } + i++; + } + } + }); + } + private void loadAuthor() throws DbException { Collection<LocalAuthor> localAuthors = identityManager.getLocalAuthors(); @@ -343,6 +339,7 @@ public class ForumControllerImpl extends DbControllerImpl throw new RuntimeException(e); } storePost(p); + addNewPost(p); } }); } @@ -369,7 +366,6 @@ public class ForumControllerImpl extends DbControllerImpl runOnDbThread(new Runnable() { public void run() { try { - localAdd = p.getMessage().getId(); long now = System.currentTimeMillis(); forumManager.addLocalPost(p); long duration = System.currentTimeMillis() - now; @@ -384,4 +380,12 @@ public class ForumControllerImpl extends DbControllerImpl }); } + private void addNewPost(final ForumPost p) { + ForumPostHeader h = + new ForumPostHeader(p.getMessage().getId(), p.getParent(), + p.getMessage().getTimestamp(), p.getAuthor(), VERIFIED, + p.getContentType(), false); + addNewPost(h); + } + } diff --git a/briar-android/src/org/briarproject/android/forum/ForumEntry.java b/briar-android/src/org/briarproject/android/forum/ForumEntry.java index a24d94bbcf457d57eb5dc140878895f24c734a28..b3f2aed40ba472efd6256072799288f36525b61c 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumEntry.java +++ b/briar-android/src/org/briarproject/android/forum/ForumEntry.java @@ -17,7 +17,7 @@ public class ForumEntry { private boolean isShowingDescendants = true; private boolean isRead = true; - public ForumEntry(ForumPostHeader h, String text, int level) { + ForumEntry(ForumPostHeader h, String text, int level) { this(h.getId(), text, level, h.getTimestamp(), h.getAuthor().getName(), h.getAuthor().getId(), h.getAuthorStatus()); this.isRead = h.isRead(); @@ -50,7 +50,7 @@ public class ForumEntry { return author; } - public AuthorId getAuthorId() { + AuthorId getAuthorId() { return authorId; } @@ -58,15 +58,15 @@ public class ForumEntry { return status; } - public boolean isShowingDescendants() { + boolean isShowingDescendants() { return isShowingDescendants; } - public void setShowingDescendants(boolean showingDescendants) { + void setShowingDescendants(boolean showingDescendants) { this.isShowingDescendants = showingDescendants; } - public MessageId getMessageId() { + MessageId getMessageId() { return messageId; } @@ -74,7 +74,7 @@ public class ForumEntry { return isRead; } - public void setRead(boolean read) { + void setRead(boolean read) { isRead = read; } } diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java index c3dc8ebe40e82cc5c07f81038f33032704b8c0b9..1916bce322bd3fbc27663b28aadbf2d07f5f6fc5 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java @@ -22,14 +22,13 @@ import org.briarproject.api.db.NoSuchGroupException; import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.Event; import org.briarproject.api.event.ForumInvitationReceivedEvent; +import org.briarproject.api.event.ForumPostReceivedEvent; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; -import org.briarproject.api.event.MessageStateChangedEvent; import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumPostHeader; import org.briarproject.api.forum.ForumSharingManager; -import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import java.util.ArrayList; @@ -41,7 +40,6 @@ import javax.inject.Inject; import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; public class ForumListFragment extends BaseEventFragment implements View.OnClickListener { @@ -234,14 +232,10 @@ public class ForumListFragment extends BaseEventFragment implements LOG.info("Forum removed, removing from list"); removeForum(g.getGroup().getId()); } - } else if (e instanceof MessageStateChangedEvent) { - MessageStateChangedEvent m = (MessageStateChangedEvent) e; - ClientId c = m.getClientId(); - if (m.getState() == DELIVERED && - c.equals(forumManager.getClientId())) { - LOG.info("Forum post added, reloading"); - loadForumHeaders(m.getMessage().getGroupId()); - } + } else if (e instanceof ForumPostReceivedEvent) { + ForumPostReceivedEvent m = (ForumPostReceivedEvent) e; + LOG.info("Forum post added, reloading"); + loadForumHeaders(m.getGroupId()); } else if (e instanceof ForumInvitationReceivedEvent) { loadAvailableForums(); } diff --git a/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java b/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java index 19aaa33f071b44b42134c48dfe4c642b15f57514..6464be24ead03476e301674648cee386da43b0df 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java +++ b/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java @@ -12,7 +12,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; /** * This class is a singleton that defines the data that should persist, i.e. @@ -21,7 +20,7 @@ import java.util.logging.Logger; */ public class ForumPersistentData { - protected volatile MessageTree<ForumPostHeader> tree = + private volatile MessageTree<ForumPostHeader> tree = new MessageTreeImpl<>(); private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>(); private volatile LocalAuthor localAuthor; @@ -29,36 +28,36 @@ public class ForumPersistentData { private volatile GroupId groupId; private List<ForumEntry> forumEntries; - private static final Logger LOG = - Logger.getLogger(ForumControllerImpl.class.getName()); - - - public void clearAll() { - clearHeaders(); + void clearAll() { + clearForumEntries(); + tree.clear(); bodyCache.clear(); localAuthor = null; forum = null; groupId = null; } - public void clearHeaders() { - tree.clear(); + void clearForumEntries() { forumEntries = null; } - public void addHeaders(Collection<ForumPostHeader> headers) { + void addHeaders(Collection<ForumPostHeader> headers) { tree.add(headers); } + void addHeader(ForumPostHeader header) { + tree.add(header); + } + public Collection<ForumPostHeader> getHeaders() { return tree.depthFirstOrder(); } - public void addBody(MessageId messageId, byte[] body) { + void addBody(MessageId messageId, byte[] body) { bodyCache.put(messageId, body); } - public byte[] getBody(MessageId messageId) { + byte[] getBody(MessageId messageId) { return bodyCache.get(messageId); } @@ -87,11 +86,11 @@ public class ForumPersistentData { this.groupId = groupId; } - public List<ForumEntry> getForumEntries() { + List<ForumEntry> getForumEntries() { return forumEntries; } - public void setForumEntries(List<ForumEntry> forumEntries) { + void setForumEntries(List<ForumEntry> forumEntries) { this.forumEntries = forumEntries; } } diff --git a/briar-api/src/org/briarproject/api/clients/MessageTree.java b/briar-api/src/org/briarproject/api/clients/MessageTree.java index 500db6a583435f5371ff4e403b7b37778a7f3098..1c0c92e9feb378353fe96796dc6cb9a49257f418 100644 --- a/briar-api/src/org/briarproject/api/clients/MessageTree.java +++ b/briar-api/src/org/briarproject/api/clients/MessageTree.java @@ -8,9 +8,10 @@ import java.util.Comparator; public interface MessageTree<T extends MessageTree.MessageNode> { void add(Collection<T> nodes); + void add(T node); + void setComparator(Comparator<T> comparator); void clear(); Collection<T> depthFirstOrder(); - void setComparator(Comparator<T> comparator); interface MessageNode { MessageId getId(); diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 123d8e4b16ae98100ca856778314eacfdc4c98de..3f123566f7ea4af22c31b18cd4de727850567a16 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -262,7 +262,7 @@ public interface DatabaseComponent { byte[] getRawMessage(Transaction txn, MessageId m) throws DbException; /** - * Returns the metadata for all messages in the given group. + * Returns the metadata for all delivered messages in the given group. * <p/> * Read-only. */ @@ -280,13 +280,22 @@ public interface DatabaseComponent { Metadata query) throws DbException; /** - * Returns the metadata for the given message. + * Returns the metadata for the given delivered message. * <p/> * Read-only. */ Metadata getMessageMetadata(Transaction txn, MessageId m) throws DbException; + /** + * Returns the metadata for the given delivered and pending message. + * This is meant to be only used by the ValidationManager + * <p/> + * Read-only. + */ + Metadata getMessageMetadataForValidator(Transaction txn, MessageId m) + throws DbException; + /** * Returns the status of all messages in the given group with respect to * the given contact. diff --git a/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java b/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..b300f9ee276f063e6dd4c63de6e607b05907ea30 --- /dev/null +++ b/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java @@ -0,0 +1,29 @@ +package org.briarproject.api.event; + +import org.briarproject.api.forum.ForumPostHeader; +import org.briarproject.api.messaging.PrivateMessageHeader; +import org.briarproject.api.sync.GroupId; + +/** + * An event that is broadcast when a new forum post was received. + */ +public class ForumPostReceivedEvent extends Event { + + private final ForumPostHeader forumPostHeader; + private final GroupId groupId; + + public ForumPostReceivedEvent(ForumPostHeader forumPostHeader, + GroupId groupId) { + + this.forumPostHeader = forumPostHeader; + this.groupId = groupId; + } + + public ForumPostHeader getForumPostHeader() { + return forumPostHeader; + } + + public GroupId getGroupId() { + return groupId; + } +} diff --git a/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java b/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java index e8d0b5e2f7e8633fba85b00d79e21af9ddd0847d..f8211623d2c2012e1c4420b7760b286815a3136c 100644 --- a/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java +++ b/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java @@ -12,7 +12,6 @@ import org.briarproject.api.db.Metadata; import org.briarproject.api.db.Transaction; import org.briarproject.api.sync.Message; import org.briarproject.api.sync.ValidationManager.IncomingMessageHook; -import org.briarproject.api.system.Clock; import static org.briarproject.api.clients.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH; import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; diff --git a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java index 4759f39fdb9ec291ef08f9912b6048caba7067c6..58e5c65a47ebd6f0498929f184684bbcfb7c8ffb 100644 --- a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java +++ b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java @@ -14,8 +14,9 @@ import java.util.Map; public class MessageTreeImpl<T extends MessageTree.MessageNode> implements MessageTree<T> { - Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>(); - List<T> roots = new ArrayList<T>(); + private final Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>(); + private final List<T> roots = new ArrayList<T>(); + private final List<List<T>> unsortedLists = new ArrayList<List<T>>(); private Comparator<T> comparator = new Comparator<T>() { @Override @@ -38,26 +39,44 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode> } // parse the nodes for dependencies for (T node : nodes) { - if (node.getParentId() == null) { - roots.add(node); - } - else { - // retrieve the parent's children - List<T> pChildren = nodeMap.get(node.getParentId()); - pChildren.add(node); - } + parseNode(node); } - sortAll(); + sortUnsorted(); } - private void sortAll() { - Collections.sort(roots, comparator); - // Sort all the sub-lists - for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) { - Collections.sort(entry.getValue(), comparator); + @Override + public void add(T node) { + add(Collections.singletonList(node)); + } + + private void markAsUnsorted(List<T> list) { + if (!unsortedLists.contains(list)) + unsortedLists.add(list); + } + + private void parseNode(T node) { + if (node.getParentId() == null) { + roots.add(node); + markAsUnsorted(roots); + } else { + // retrieve the parent's children + List<T> pChildren = nodeMap.get(node.getParentId()); + pChildren.add(node); + markAsUnsorted(pChildren); } } + private void sortUnsorted() { + // leave unsorted if there is no comparator + if (comparator != null) { + for (List<T> list : unsortedLists) { + Collections.sort(list, comparator); + } + unsortedLists.clear(); + } + } + + private void traverse(List<T> list, T node) { list.add(node); for (T child : nodeMap.get(node.getId())) { @@ -65,6 +84,16 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode> } } + @Override + public void setComparator(Comparator<T> comparator) { + this.comparator = comparator; + // Sort all lists with the new comparator + Collections.sort(roots, comparator); + for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) { + Collections.sort(entry.getValue(), comparator); + } + } + @Override public Collection<T> depthFirstOrder() { List<T> orderedList = new ArrayList<T>(); @@ -74,9 +103,4 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode> return Collections.unmodifiableList(orderedList); } - @Override - public void setComparator(Comparator<T> comparator) { - this.comparator = comparator; - } - } diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index 9779217904ba814b9aaa7deefb05edd6171300b8..a63f411cbb1fda7dad9faf094b6a8008e013d74f 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -310,7 +310,7 @@ interface Database<T> { throws DbException; /** - * Returns the metadata for all messages in the given group. + * Returns the metadata for all delivered messages in the given group. * <p/> * Read-only. */ @@ -327,6 +327,14 @@ interface Database<T> { Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g, Metadata query) throws DbException; + /** + * Returns the metadata for the given delivered message. + * <p/> + * Read-only. + */ + Metadata getMessageMetadataForValidator(T txn, MessageId m) + throws DbException; + /** * Returns the metadata for the given message. * <p/> diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 4f84a04bd047e0a1054743cd7331a8e90ab7c3a0..f8eabd84c63453bfdc8bdbe6eb61acb3aec9a6a2 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -456,6 +456,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { return db.getMessageMetadata(txn, m); } + public Metadata getMessageMetadataForValidator(Transaction transaction, + MessageId m) + throws DbException { + T txn = unbox(transaction); + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + return db.getMessageMetadataForValidator(txn, m); + } + public Collection<MessageStatus> getMessageStatus(Transaction transaction, ContactId c, GroupId g) throws DbException { T txn = unbox(transaction); diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index a52fbe2a1da6c9729518c91fbb7e61cd5f39ddca..363075dfd6a6d33e67e20ff67020370b4f0ed5cc 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -1324,6 +1324,33 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public Metadata getMessageMetadataForValidator(Connection txn, MessageId m) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT key, value FROM messageMetadata AS md" + + " JOIN messages AS m" + + " ON m.messageId = md.messageId" + + " WHERE (m.state = ? OR m.state = ?)" + + " AND md.messageId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, DELIVERED.getValue()); + ps.setInt(2, PENDING.getValue()); + ps.setBytes(3, m.getBytes()); + rs = ps.executeQuery(); + Metadata metadata = new Metadata(); + while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2)); + rs.close(); + ps.close(); + return metadata; + } catch (SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public Collection<MessageStatus> getMessageStatus(Connection txn, ContactId c, GroupId g) throws DbException { PreparedStatement ps = null; diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java index 36f8357bafdfecf063c21a8f270d91271ad24b98..2574ee80da3fd3e05bc0669f30e456a784328643 100644 --- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java @@ -4,9 +4,11 @@ import org.briarproject.api.FormatException; import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.MetadataParser; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Transaction; +import org.briarproject.api.event.ForumPostReceivedEvent; import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.ForumFactory; import org.briarproject.api.forum.ForumManager; @@ -19,7 +21,9 @@ import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.Group; import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; +import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.util.StringUtils; import java.util.ArrayList; @@ -44,7 +48,7 @@ import static org.briarproject.api.forum.ForumConstants.KEY_READ; import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP; import static org.briarproject.api.identity.Author.Status.ANONYMOUS; -class ForumManagerImpl implements ForumManager { +class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { private static final Logger LOG = Logger.getLogger(ForumManagerImpl.class.getName()); @@ -55,21 +59,31 @@ class ForumManagerImpl implements ForumManager { private final DatabaseComponent db; private final IdentityManager identityManager; - private final ClientHelper clientHelper; private final ForumFactory forumFactory; private final List<RemoveForumHook> removeHooks; @Inject ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager, - ClientHelper clientHelper, ForumFactory forumFactory) { + ClientHelper clientHelper, MetadataParser metadataParser, + ForumFactory forumFactory) { + super(clientHelper, metadataParser); this.db = db; this.identityManager = identityManager; - this.clientHelper = clientHelper; this.forumFactory = forumFactory; removeHooks = new CopyOnWriteArrayList<RemoveForumHook>(); } + @Override + protected void incomingMessage(Transaction txn, Message m, BdfList body, + BdfDictionary meta) throws DbException, FormatException { + + ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta); + ForumPostReceivedEvent event = + new ForumPostReceivedEvent(post, m.getGroupId()); + txn.attach(event); + } + @Override public ClientId getClientId() { return CLIENT_ID; @@ -193,25 +207,7 @@ class ForumManagerImpl implements ForumManager { for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) { try { BdfDictionary meta = entry.getValue(); - long timestamp = meta.getLong(KEY_TIMESTAMP); - Author author = null; - Status authorStatus = ANONYMOUS; - MessageId parentId = null; - if (meta.containsKey(KEY_PARENT)) - parentId = new MessageId(meta.getRaw(KEY_PARENT)); - BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null); - if (d1 != null) { - AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID)); - String name = d1.getString(KEY_NAME); - byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME); - author = new Author(authorId, name, publicKey); - authorStatus = - identityManager.getAuthorStatus(author.getId()); - } - String contentType = meta.getString(KEY_CONTENT_TYPE); - boolean read = meta.getBoolean(KEY_READ); - headers.add(new ForumPostHeader(entry.getKey(), parentId, - timestamp, author, authorStatus, contentType, read)); + headers.add(getForumPostHeader(entry.getKey(), meta)); } catch (FormatException e) { throw new DbException(e); } @@ -242,4 +238,39 @@ class ForumManagerImpl implements ForumManager { return new Forum(g, forum.getString(0), forum.getRaw(1)); } + private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id, + BdfDictionary meta) throws DbException, FormatException { + + long timestamp = meta.getLong(KEY_TIMESTAMP); + Author author = null; + Status authorStatus = ANONYMOUS; + MessageId parentId = null; + if (meta.containsKey(KEY_PARENT)) + parentId = new MessageId(meta.getRaw(KEY_PARENT)); + BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null); + if (d1 != null) { + AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID)); + String name = d1.getString(KEY_NAME); + byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME); + author = new Author(authorId, name, publicKey); + if (txn == null) { + authorStatus = identityManager.getAuthorStatus(author.getId()); + } else { + authorStatus = + identityManager.getAuthorStatus(txn, author.getId()); + } + } + String contentType = meta.getString(KEY_CONTENT_TYPE); + boolean read = meta.getBoolean(KEY_READ); + + return new ForumPostHeader(id, parentId, timestamp, author, + authorStatus, contentType, read); + } + + private ForumPostHeader getForumPostHeader(MessageId id, + BdfDictionary meta) throws DbException, FormatException { + + return getForumPostHeader(null, id, meta); + } + } diff --git a/briar-core/src/org/briarproject/forum/ForumModule.java b/briar-core/src/org/briarproject/forum/ForumModule.java index 8c84509b143bf3c45a082b9829d3acdb9e016fa3..40bfd01381fe8b1972fb253ec389c7b18107e006 100644 --- a/briar-core/src/org/briarproject/forum/ForumModule.java +++ b/briar-core/src/org/briarproject/forum/ForumModule.java @@ -29,7 +29,13 @@ public class ForumModule { @Provides @Singleton - ForumManager provideForumManager(ForumManagerImpl forumManager) { + ForumManager provideForumManager(ForumManagerImpl forumManager, + ValidationManager validationManager) { + + validationManager + .registerIncomingMessageHook(forumManager.getClientId(), + forumManager); + return forumManager; } diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java index f6aa74c3ab07fcd7bbb0ba5f211f027270980329..7853f0497d18cbb5b253a3e25ec74921f8ecfd75 100644 --- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java +++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java @@ -188,7 +188,7 @@ class ValidationManagerImpl implements ValidationManager, Service, byte[] raw = db.getRawMessage(txn, id); m = parseMessage(id, raw); g = db.getGroup(txn, m.getGroupId()); - meta = db.getMessageMetadata(txn, id); + meta = db.getMessageMetadataForValidator(txn, id); txn.setComplete(); } finally { db.endTransaction(txn); diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java index dbf48a82fd5ef2c23b345c95c1b9e802481e7c83..f50dcb2bcb36aa3e61b9749128e2e3f61b6e1221 100644 --- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java +++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java @@ -1006,6 +1006,10 @@ public class H2DatabaseTest extends BriarTestCase { map = db.getMessageMetadata(txn, groupId); assertTrue(map.isEmpty()); + // validator gets also metadata for pending messages + retrieved = db.getMessageMetadataForValidator(txn, messageId); + assertFalse(retrieved.isEmpty()); + db.commitTransaction(txn); db.close(); }