You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2023/03/16 15:23:51 UTC
[james-project] 01/02: JAMES-3440 Fixbug: Data race issue with JMAP email query view
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 29a253f596aa3693e2f9b0b46fe51c3a68bc178c
Author: Tung Van TRAN <vt...@linagora.com>
AuthorDate: Sat Mar 11 09:50:56 2023 +0700
JAMES-3440 Fixbug: Data race issue with JMAP email query view
---
.../jmap/event/PopulateEmailQueryViewListener.java | 17 ++++++-
.../event/PopulateEmailQueryViewListenerTest.java | 54 +++++++++++++++++++++-
2 files changed, 68 insertions(+), 3 deletions(-)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
index 9df59c69f4..de07c18370 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
@@ -36,6 +36,7 @@ import org.apache.james.events.Group;
import org.apache.james.jmap.api.projections.EmailQueryView;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageIdManager;
+import org.apache.james.mailbox.Role;
import org.apache.james.mailbox.SessionProvider;
import org.apache.james.mailbox.events.MailboxEvents.Added;
import org.apache.james.mailbox.events.MailboxEvents.Expunged;
@@ -54,6 +55,7 @@ import org.apache.james.mime4j.dom.field.DateTimeField;
import org.apache.james.mime4j.field.DateTimeFieldLenientImpl;
import org.apache.james.mime4j.message.DefaultMessageBuilder;
import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.util.FunctionalUtils;
import org.reactivestreams.Publisher;
import com.google.common.collect.ImmutableList;
@@ -156,11 +158,24 @@ public class PopulateEmailQueryViewListener implements ReactiveGroupEventListene
private Mono<Void> handleAdded(Added added, MessageMetaData messageMetaData, MailboxSession session) {
MessageId messageId = messageMetaData.getMessageId();
+ MailboxId mailboxId = added.getMailboxId();
- return Flux.from(messageIdManager.getMessagesReactive(ImmutableList.of(messageId), FetchGroup.HEADERS, session))
+ Mono<Void> doHandleAdded = Flux.from(messageIdManager.getMessagesReactive(ImmutableList.of(messageId), FetchGroup.HEADERS, session))
.next()
.filter(message -> !message.getFlags().contains(DELETED))
.flatMap(messageResult -> handleAdded(added.getMailboxId(), messageResult));
+ if (Role.from(added.getMailboxPath().getName()).equals(Optional.of(Role.OUTBOX))) {
+ return checkMessageStillInOriginMailbox(messageId, session, mailboxId)
+ .filter(FunctionalUtils.identityPredicate())
+ .flatMap(stillInOriginMailbox -> doHandleAdded);
+ }
+ return doHandleAdded;
+ }
+
+ private Mono<Boolean> checkMessageStillInOriginMailbox(MessageId messageId, MailboxSession session, MailboxId targetMailboxId) {
+ return Flux.from(messageIdManager.messageMetadata(messageId, session))
+ .filter(composedMessageIdWithMetaData -> composedMessageIdWithMetaData.getComposedMessageId().getMailboxId().equals(targetMailboxId))
+ .hasElements();
}
public Mono<Void> handleAdded(MailboxId mailboxId, MessageResult messageResult) {
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
index 2f62f4414a..66de6a59ad 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
@@ -20,16 +20,19 @@
package org.apache.james.jmap.event;
import static javax.mail.Flags.Flag.DELETED;
+import static org.apache.james.mailbox.events.MailboxEvents.Added.IS_DELIVERY;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Date;
+import java.util.Optional;
import javax.mail.Flags;
import org.apache.james.core.Username;
+import org.apache.james.events.Event;
import org.apache.james.events.Group;
import org.apache.james.events.InVMEventBus;
import org.apache.james.events.MemoryEventDeadLetters;
@@ -40,11 +43,16 @@ import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MailboxSessionUtil;
import org.apache.james.mailbox.MessageIdManager;
import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.events.MailboxEvents;
import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.ThreadId;
import org.apache.james.mailbox.store.FakeAuthenticator;
import org.apache.james.mailbox.store.FakeAuthorizator;
import org.apache.james.mailbox.store.SessionProviderImpl;
@@ -56,6 +64,9 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+
+import reactor.core.publisher.Mono;
public class PopulateEmailQueryViewListenerTest {
private static final Username BOB = Username.of("bob");
@@ -69,6 +80,7 @@ public class PopulateEmailQueryViewListenerTest {
MessageManager otherBoxMessageManager;
PopulateEmailQueryViewListener listener;
MessageIdManager messageIdManager;
+ SessionProviderImpl sessionProvider;
private MemoryEmailQueryView view;
private MailboxId inboxId;
@@ -97,7 +109,7 @@ public class PopulateEmailQueryViewListenerTest {
FakeAuthenticator authenticator = new FakeAuthenticator();
authenticator.addUser(BOB, "12345");
- SessionProviderImpl sessionProvider = new SessionProviderImpl(authenticator, FakeAuthorizator.defaultReject());
+ sessionProvider = new SessionProviderImpl(authenticator, FakeAuthorizator.defaultReject());
view = new MemoryEmailQueryView();
listener = new PopulateEmailQueryViewListener(messageIdManager, view, sessionProvider);
@@ -133,7 +145,7 @@ public class PopulateEmailQueryViewListenerTest {
}
@Test
- void appendingADeletedMessageSHouldNotAddItToTheView() throws Exception {
+ void appendingADeletedMessageShouldNotAddItToTheView() throws Exception {
inboxMessageManager.appendMessage(
MessageManager.AppendCommand.builder()
.withInternalDate(Date.from(ZonedDateTime.parse("2014-10-30T15:12:00Z").toInstant()))
@@ -145,6 +157,44 @@ public class PopulateEmailQueryViewListenerTest {
.isEmpty();
}
+ @Test
+ void appendingAOutdatedMessageInOutBoxShouldNotAddItToTheView() throws Exception {
+ MemoryEmailQueryView emailQueryView = new MemoryEmailQueryView();
+ PopulateEmailQueryViewListener queryViewListener = new PopulateEmailQueryViewListener(messageIdManager, emailQueryView, sessionProvider);
+ MailboxPath outboxPath = MailboxPath.forUser(BOB, "Outbox");
+ MailboxId outboxId = mailboxManager.createMailbox(outboxPath, mailboxSession).orElseThrow();
+
+ // given: save a message in Inbox
+ ComposedMessageId composedId = inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .withInternalDate(Date.from(ZonedDateTime.parse("2014-10-30T15:12:00Z").toInstant()))
+ .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))),
+ mailboxSession).getId();
+
+ // mock an outDated message by assigning above message in OutBox
+ MessageMetaData outdatedMessageMetaData = new MessageMetaData(MessageUid.of(1),
+ ModSeq.of(35), new Flags(), 12,
+ new Date(),
+ Optional.empty(),
+ composedId.getMessageId(),
+ ThreadId.fromBaseMessageId(composedId.getMessageId()));
+
+ // the latest mailboxId should be `composedId.getMailboxId()`, not `outboxId`
+ MailboxEvents.Added addedOutDatedEvent = new MailboxEvents.Added(MailboxSession.SessionId.of(42),
+ BOB,
+ outboxPath,
+ outboxId,
+ ImmutableSortedMap.of(MessageUid.of(1), outdatedMessageMetaData),
+ Event.EventId.random(),
+ !IS_DELIVERY);
+
+ Mono.from(queryViewListener.reactiveEvent(addedOutDatedEvent)).block();
+
+ assertThat(emailQueryView.listMailboxContentSortedBySentAt(outboxId, Limit.limit(12)).collectList().block())
+ .isEmpty();
+ }
+
+
@Test
void removingDeletedFlagsShouldAddItToTheView() throws Exception {
ComposedMessageId composedId = inboxMessageManager.appendMessage(
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org