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 2021/06/05 02:57:45 UTC

[james-project] branch master updated (9f7425e -> a8d1382)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 9f7425e  [REFACTORING] Add a memory hepa size limit on ES docker container
     new 21b6468  JAMES-3516 Exposing AppendResult::getThreadId allow advertising the threadId as part of Email/set and Email/Import results
     new a8d1382  JAMES-3516 Add threadId property to MessageMetaData POJO

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../listeners/SetCustomFlagOnBigMessagesTest.java  |  4 +-
 .../org/apache/james/mailbox/MessageManager.java   | 15 ++++-
 .../james/mailbox/model/MessageMetaData.java       |  8 ++-
 .../org/apache/james/mailbox/model/ThreadId.java   |  4 ++
 ...ithMailboxIdTest.java => AppendResultTest.java} | 43 ++++++-------
 .../java/org/apache/james/mailbox/EventTest.java   |  9 +--
 .../apache/james/mailbox/MailboxListenerTest.java  |  3 +-
 .../scala/org/apache/james/event/json/DTOs.scala   | 10 +--
 .../james/event/json/MailboxEventSerializer.scala  |  7 +-
 .../james/event/json/AddedSerializationTest.java   | 75 +++++++++++++++++++++-
 .../event/json/ExpungedSerializationTest.java      | 75 +++++++++++++++++++++-
 .../james/mailbox/store/StoreMessageManager.java   |  2 +-
 .../mailbox/store/mail/model/MailboxMessage.java   |  2 +-
 .../james/mailbox/store/PreDeletionHooksTest.java  |  3 +-
 .../store/mail/model/MetadataMapAssertTest.java    |  9 +--
 .../quota/ListeningCurrentQuotaUpdaterTest.java    |  9 +--
 .../processor/base/MailboxEventAnalyserTest.java   |  3 +-
 .../processor/base/SelectedMailboxImplTest.java    |  3 +-
 .../methods/SetMessagesCreationProcessorTest.java  |  3 +-
 .../methods/SetMessagesUpdateProcessorTest.java    |  3 +-
 .../james/jmap/json/EmailSetSerializer.scala       |  4 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  6 +-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  2 +-
 .../james/jmap/method/EmailImportMethod.scala      |  5 +-
 .../jmap/method/EmailSetCreatePerformer.scala      |  5 +-
 25 files changed, 247 insertions(+), 65 deletions(-)
 copy mailbox/api/src/test/java/org/apache/james/mailbox/{MetadataWithMailboxIdTest.java => AppendResultTest.java} (84%)

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/02: JAMES-3516 Exposing AppendResult::getThreadId allow advertising the threadId as part of Email/set and Email/Import results

Posted by bt...@apache.org.
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 21b64687dda36b15e800d3a125f8d9075cbcdbab
Author: quanth <hq...@linagora.com>
AuthorDate: Fri May 28 13:34:57 2021 +0700

    JAMES-3516 Exposing AppendResult::getThreadId allow advertising the threadId as part of Email/set and Email/Import results
---
 .../org/apache/james/mailbox/MessageManager.java   | 15 ++++++++--
 .../org/apache/james/mailbox/AppendResultTest.java | 32 ++++++++++++++++++++++
 .../james/mailbox/store/StoreMessageManager.java   |  2 +-
 .../james/jmap/json/EmailSetSerializer.scala       |  4 +--
 .../scala/org/apache/james/jmap/mail/Email.scala   |  6 +++-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  2 +-
 .../james/jmap/method/EmailImportMethod.scala      |  5 ++--
 .../jmap/method/EmailSetCreatePerformer.scala      |  5 ++--
 8 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
index 6e23e26..e214865 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
@@ -53,6 +53,7 @@ import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MessageResultIterator;
 import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.mime4j.message.DefaultMessageWriter;
@@ -152,11 +153,13 @@ public interface MessageManager {
         private final ComposedMessageId id;
         private final Long size;
         private final Optional<List<MessageAttachmentMetadata>> messageAttachments;
+        private final ThreadId threadId;
 
-        public AppendResult(ComposedMessageId id, Long size, Optional<List<MessageAttachmentMetadata>> messageAttachments) {
+        public AppendResult(ComposedMessageId id, Long size, Optional<List<MessageAttachmentMetadata>> messageAttachments, ThreadId threadId) {
             this.id = id;
             this.size = size;
             this.messageAttachments = messageAttachments;
+            this.threadId = threadId;
         }
 
         public ComposedMessageId getId() {
@@ -172,20 +175,26 @@ public interface MessageManager {
             return messageAttachments.get();
         }
 
+        public ThreadId getThreadId() {
+            return threadId;
+        }
+
         @Override
         public final boolean equals(Object o) {
             if (o instanceof AppendResult) {
                 AppendResult that = (AppendResult) o;
 
                 return Objects.equals(this.id, that.id)
-                    && Objects.equals(this.messageAttachments, that.messageAttachments);
+                    && Objects.equals(this.messageAttachments, that.messageAttachments)
+                    && Objects.equals(this.size, that.size)
+                    && Objects.equals(this.threadId, that.threadId);
             }
             return false;
         }
 
         @Override
         public final int hashCode() {
-            return Objects.hash(id, messageAttachments);
+            return Objects.hash(id, messageAttachments, size, threadId);
         }
     }
 
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/AppendResultTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/AppendResultTest.java
new file mode 100644
index 0000000..be1d939
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/AppendResultTest.java
@@ -0,0 +1,32 @@
+/******************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one     *
+ * or more contributor license agreements.  See the NOTICE file   *
+ * distributed with this work for additional information          *
+ * regarding copyright ownership.  The ASF licenses this file     *
+ * to you under the Apache License, Version 2.0 (the              *
+ * "License"); you may not use this file except in compliance     *
+ * with the License.  You may obtain a copy of the License at     *
+ *                                                                *
+ * http://www.apache.org/licenses/LICENSE-2.0                     *
+ *                                                                *
+ * Unless required by applicable law or agreed to in writing,     *
+ * software distributed under the License is distributed on an    *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY         *
+ * KIND, either express or implied.  See the License for the      *
+ * specific language governing permissions and limitations        *
+ * under the License.                                             *
+ ******************************************************************/
+
+package org.apache.james.mailbox;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class AppendResultTest {
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(MessageManager.AppendResult.class)
+            .verify();
+    }
+}
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
index ab62237..60d7a5a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
@@ -511,7 +511,7 @@ public class StoreMessageManager implements MessageManager {
     private AppendResult computeAppendResult(Pair<MessageMetaData, Optional<List<MessageAttachmentMetadata>>> data, Mailbox mailbox) {
         MessageMetaData messageMetaData = data.getLeft();
         ComposedMessageId ids = new ComposedMessageId(mailbox.getMailboxId(), messageMetaData.getMessageId(), messageMetaData.getUid());
-        return new AppendResult(ids, messageMetaData.getSize(), data.getRight());
+        return new AppendResult(ids, messageMetaData.getSize(), data.getRight(), messageMetaData.getThreadId());
     }
 
     private PropertyBuilder getPropertyBuilder(MaximalBodyDescriptor descriptor, String mediaType, String subType) {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
index 41c206e..fcd8ab6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
@@ -28,8 +28,7 @@ import javax.inject.Inject
 import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{Id, SetError, UTCDate, UuidState}
 import org.apache.james.jmap.mail.KeywordsFactory.STRICT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, Attachment, BlobId, Charset, ClientBody, ClientCid, ClientEmailBodyValue, ClientPartId, DateHeaderValue, DestroyIds, Disposition, EmailAddress, EmailAddressGroup, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, GroupName, GroupedAd [...]
-import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, Attachment, BlobId, Charset, ClientBody, ClientCid, ClientEmailBodyValue, ClientPartId, DateHeaderValue, DestroyIds, Disposition, EmailAddress, EmailAddressGroup, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailImport, EmailImportRequest, EmailImportResponse, EmailSetRequest, EmailSetResponse, EmailSetUpda [...]
+import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, Attachment, BlobId, Charset, ClientBody, ClientCid, ClientEmailBodyValue, ClientPartId, DateHeaderValue, DestroyIds, Disposition, EmailAddress, EmailAddressGroup, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailImport, EmailImportRequest, EmailImportResponse, EmailSetRequest, EmailSetRespo [...]
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json.{Format, JsArray, JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
 
@@ -229,6 +228,7 @@ class EmailSetSerializer @Inject()(messageIdFactory: MessageId.Factory, mailboxI
   private implicit val destroyIdsReads: Reads[DestroyIds] = Json.valueFormat[DestroyIds]
   private implicit val destroyIdsWrites: Writes[DestroyIds] = Json.valueWrites[DestroyIds]
   private implicit val emailRequestSetReads: Reads[EmailSetRequest] = Json.reads[EmailSetRequest]
+  private implicit val threadIdWrites: Writes[ThreadId] = Json.valueWrites[ThreadId]
   private implicit val emailCreationResponseWrites: Writes[EmailCreationResponse] = Json.writes[EmailCreationResponse]
   private implicit val createsMapWrites: Writes[Map[EmailCreationId, EmailCreationResponse]] =
     mapWrites[EmailCreationId, EmailCreationResponse](_.id.value, emailCreationResponseWrites)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 6699876..db66bfa 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -36,7 +36,7 @@ import org.apache.james.jmap.mail.EmailHeaderName.{ADDRESSES_NAMES, DATE, MESSAG
 import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.method.ZoneIdProvider
 import org.apache.james.mailbox.model.FetchGroup.{FULL_CONTENT, HEADERS, MINIMAL}
-import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageId, MessageResult}
+import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageId, MessageResult, ThreadId => JavaThreadId}
 import org.apache.james.mailbox.{MailboxSession, MessageIdManager}
 import org.apache.james.mime4j.codec.DecodeMonitor
 import org.apache.james.mime4j.dom.field.{AddressListField, DateTimeField, MailboxField, MailboxListField}
@@ -233,6 +233,10 @@ case class MailboxIds(value: List[MailboxId]) {
   def --(ids: MailboxIds) = MailboxIds((value.toSet -- ids.value.toSet).toList)
 }
 
+object ThreadId {
+  def fromJava(threadId: JavaThreadId): ThreadId = ThreadId(threadId.serialize)
+}
+
 case class ThreadId(value: String) extends AnyVal
 
 case class HasAttachment(value: Boolean) extends AnyVal
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
index d6a88e4..ef69a2c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
@@ -398,5 +398,5 @@ case class InvalidEmailPropertyException(property: String, cause: String) extend
 
 case class InvalidEmailUpdateException(property: String, cause: String) extends EmailUpdateValidationException
 
-case class EmailCreationResponse(id: MessageId, blobId: Option[BlobId], threadId: Option[BlobId], size: Size)
+case class EmailCreationResponse(id: MessageId, blobId: Option[BlobId], threadId: ThreadId, size: Size)
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
index 8fa4ea2..5598083 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailImportMethod.scala
@@ -30,7 +30,7 @@ import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{ClientId, Id, Invocation, ServerId, SetError, UuidState}
 import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationId, EmailCreationResponse, EmailImport, EmailImportRequest, EmailImportResponse, ValidatedEmailImport}
+import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationId, EmailCreationResponse, EmailImport, EmailImportRequest, EmailImportResponse, ThreadId, ValidatedEmailImport}
 import org.apache.james.jmap.method.EmailImportMethod.{ImportFailure, ImportResult, ImportResults, ImportSuccess, ImportWithBlob}
 import org.apache.james.jmap.routes.{Blob, BlobNotFoundException, BlobResolvers, ProcessingContext, SessionSupplier}
 import org.apache.james.mailbox.MessageManager.AppendCommand
@@ -172,7 +172,8 @@ class EmailImportMethod @Inject() (val metricFactory: MetricFactory,
 
   private def asEmailCreationResponse(appendResult: MessageManager.AppendResult): EmailCreationResponse = {
     val blobId: Option[BlobId] = BlobId.of(appendResult.getId.getMessageId).toOption
-    EmailCreationResponse(appendResult.getId.getMessageId, blobId, blobId, Email.sanitizeSize(appendResult.getSize))
+    val threadId: ThreadId = ThreadId.fromJava(appendResult.getThreadId)
+    EmailCreationResponse(appendResult.getId.getMessageId, blobId, threadId, Email.sanitizeSize(appendResult.getSize))
   }
 
   private def retrieveState(capabilities: Set[CapabilityIdentifier], mailboxSession: MailboxSession): SMono[UuidState] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
index bd490e7..bc108af 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
@@ -28,7 +28,7 @@ import javax.mail.Flags
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{Properties, SetError, UTCDate}
 import org.apache.james.jmap.json.EmailSetSerializer
-import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailSetRequest}
+import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, ThreadId}
 import org.apache.james.jmap.method.EmailSetCreatePerformer.{CreationFailure, CreationResult, CreationResults, CreationSuccess}
 import org.apache.james.jmap.routes.{BlobNotFoundException, BlobResolvers}
 import org.apache.james.mailbox.MessageManager.AppendCommand
@@ -103,7 +103,8 @@ class EmailSetCreatePerformer @Inject()(serializer: EmailSetSerializer,
       appendResult <- SMono(mailbox.appendMessageReactive(appendCommand, mailboxSession))
     } yield {
       val blobId: Option[BlobId] = BlobId.of(appendResult.getId.getMessageId).toOption
-      CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId, blobId, blobId, Email.sanitizeSize(appendResult.getSize)))
+      val threadId: ThreadId = ThreadId.fromJava(appendResult.getThreadId)
+      CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId, blobId, threadId, Email.sanitizeSize(appendResult.getSize)))
     }
   }
 

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/02: JAMES-3516 Add threadId property to MessageMetaData POJO

Posted by bt...@apache.org.
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 a8d13822e6fe1a3fa6dc9fba73a8befb477e8ae2
Author: quanth <hq...@linagora.com>
AuthorDate: Fri May 28 18:08:39 2021 +0700

    JAMES-3516 Add threadId property to MessageMetaData POJO
---
 .../listeners/SetCustomFlagOnBigMessagesTest.java  |  4 +-
 .../james/mailbox/model/MessageMetaData.java       |  8 ++-
 .../org/apache/james/mailbox/model/ThreadId.java   |  4 ++
 .../java/org/apache/james/mailbox/EventTest.java   |  9 +--
 .../apache/james/mailbox/MailboxListenerTest.java  |  3 +-
 .../scala/org/apache/james/event/json/DTOs.scala   | 10 +--
 .../james/event/json/MailboxEventSerializer.scala  |  7 +-
 .../james/event/json/AddedSerializationTest.java   | 75 +++++++++++++++++++++-
 .../event/json/ExpungedSerializationTest.java      | 75 +++++++++++++++++++++-
 .../mailbox/store/mail/model/MailboxMessage.java   |  2 +-
 .../james/mailbox/store/PreDeletionHooksTest.java  |  3 +-
 .../store/mail/model/MetadataMapAssertTest.java    |  9 +--
 .../quota/ListeningCurrentQuotaUpdaterTest.java    |  9 +--
 .../processor/base/MailboxEventAnalyserTest.java   |  3 +-
 .../processor/base/SelectedMailboxImplTest.java    |  3 +-
 .../methods/SetMessagesCreationProcessorTest.java  |  3 +-
 .../methods/SetMessagesUpdateProcessorTest.java    |  3 +-
 17 files changed, 199 insertions(+), 31 deletions(-)

diff --git a/examples/custom-listeners/src/test/java/org/apache/james/examples/custom/listeners/SetCustomFlagOnBigMessagesTest.java b/examples/custom-listeners/src/test/java/org/apache/james/examples/custom/listeners/SetCustomFlagOnBigMessagesTest.java
index 0ccbeca..1c5bd5a 100644
--- a/examples/custom-listeners/src/test/java/org/apache/james/examples/custom/listeners/SetCustomFlagOnBigMessagesTest.java
+++ b/examples/custom-listeners/src/test/java/org/apache/james/examples/custom/listeners/SetCustomFlagOnBigMessagesTest.java
@@ -29,11 +29,11 @@ import java.util.stream.Stream;
 import javax.mail.Flags;
 
 import org.apache.james.core.Username;
+import org.apache.james.events.Event;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MailboxSessionUtil;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageUid;
-import org.apache.james.events.Event;
 import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
 import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
 import org.apache.james.mailbox.model.ComposedMessageId;
@@ -128,7 +128,7 @@ class SetCustomFlagOnBigMessagesTest {
             .getMessages(MessageRange.one(composedIdOfSmallMessage.getUid()), FetchGroup.MINIMAL, mailboxSession)
             .next();
         MessageMetaData oneMBMetaData = new MessageMetaData(addedMessage.getUid(), addedMessage.getModSeq(),
-            addedMessage.getFlags(), ONE_MB, addedMessage.getInternalDate(), addedMessage.getMessageId());
+            addedMessage.getFlags(), ONE_MB, addedMessage.getInternalDate(), addedMessage.getMessageId(), addedMessage.getThreadId());
 
         Event eventWithAFakeMessageSize = EventFactory.added()
             .eventId(RANDOM_EVENT_ID)
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMetaData.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMetaData.java
index 1a6dfa2..de6fe30 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMetaData.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MessageMetaData.java
@@ -34,14 +34,16 @@ public class MessageMetaData {
     private final Date internalDate;
     private final ModSeq modSeq;
     private final MessageId messageId;
+    private final ThreadId threadId;
 
-    public MessageMetaData(MessageUid uid, ModSeq modSeq, Flags flags, long size, Date internalDate, MessageId messageId) {
+    public MessageMetaData(MessageUid uid, ModSeq modSeq, Flags flags, long size, Date internalDate, MessageId messageId, ThreadId threadId) {
         this.uid = uid;
         this.flags = flags;
         this.size = size;
         this.modSeq = modSeq;
         this.internalDate = internalDate;
         this.messageId = messageId;
+        this.threadId = threadId;
     }
 
     public Flags getFlags() {
@@ -73,6 +75,10 @@ public class MessageMetaData {
         return messageId;
     }
 
+    public ThreadId getThreadId() {
+        return threadId;
+    }
+
     /**
      * Return the modify-sequence number of the message. This is kind of optional and the mailbox
      * implementation may not support this. If so it will return -1
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
index 1c689f7..d556ef7 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ThreadId.java
@@ -25,6 +25,10 @@ import com.google.common.base.MoreObjects;
 
 
 public class ThreadId {
+    public static ThreadId fromBaseMessageId(MessageId baseMessageId) {
+        return new ThreadId(baseMessageId);
+    }
+
     private final MessageId baseMessageId;
 
     public ThreadId(MessageId baseMessageId) {
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/EventTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/EventTest.java
index c25f7df..8ea9603 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/EventTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/EventTest.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.junit.jupiter.api.Test;
 
 import com.google.common.collect.ImmutableSortedMap;
@@ -67,8 +68,8 @@ class EventTest {
         MessageUid uid2 = MessageUid.of(37);
         TestMessageId messageId1 = TestMessageId.of(45);
         TestMessageId messageId2 = TestMessageId.of(46);
-        MessageMetaData metaData1 = new MessageMetaData(uid1, ModSeq.of(85), new Flags(), 36, new Date(), messageId1);
-        MessageMetaData metaData2 = new MessageMetaData(uid2, ModSeq.of(85), new Flags(), 36, new Date(), messageId2);
+        MessageMetaData metaData1 = new MessageMetaData(uid1, ModSeq.of(85), new Flags(), 36, new Date(), messageId1, ThreadId.fromBaseMessageId(messageId1));
+        MessageMetaData metaData2 = new MessageMetaData(uid2, ModSeq.of(85), new Flags(), 36, new Date(), messageId2, ThreadId.fromBaseMessageId(messageId2));
 
         Added added = new Added(MailboxSession.SessionId.of(36), BOB, MailboxPath.inbox(BOB), TestId.of(48),
             ImmutableSortedMap.of(
@@ -84,8 +85,8 @@ class EventTest {
         MessageUid uid1 = MessageUid.of(36);
         MessageUid uid2 = MessageUid.of(37);
         TestMessageId messageId = TestMessageId.of(45);
-        MessageMetaData metaData1 = new MessageMetaData(uid1, ModSeq.of(85), new Flags(), 36, new Date(), messageId);
-        MessageMetaData metaData2 = new MessageMetaData(uid2, ModSeq.of(85), new Flags(), 36, new Date(), messageId);
+        MessageMetaData metaData1 = new MessageMetaData(uid1, ModSeq.of(85), new Flags(), 36, new Date(), messageId, ThreadId.fromBaseMessageId(messageId));
+        MessageMetaData metaData2 = new MessageMetaData(uid2, ModSeq.of(85), new Flags(), 36, new Date(), messageId, ThreadId.fromBaseMessageId(messageId));
 
         Added added = new Added(MailboxSession.SessionId.of(36), BOB, MailboxPath.inbox(BOB), TestId.of(48),
             ImmutableSortedMap.of(
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
index 13fa417..82b41b9 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxListenerTest.java
@@ -50,6 +50,7 @@ import org.apache.james.mailbox.model.Quota;
 import org.apache.james.mailbox.model.QuotaRoot;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.junit.jupiter.api.Test;
 
@@ -72,7 +73,7 @@ class MailboxListenerTest {
     private static final MailboxACL ACL_2 = new MailboxACL(
         Pair.of(MailboxACL.EntryKey.createUserEntryKey(Username.of("Bob")), new MailboxACL.Rfc4314Rights(MailboxACL.Right.Read)));
     private static final MessageUid UID = MessageUid.of(85);
-    private static final MessageMetaData META_DATA = new MessageMetaData(UID, ModSeq.of(45), new Flags(), 45, new Date(), TestMessageId.of(75));
+    private static final MessageMetaData META_DATA = new MessageMetaData(UID, ModSeq.of(45), new Flags(), 45, new Date(), TestMessageId.of(75), ThreadId.fromBaseMessageId(TestMessageId.of(75)));
 
     @Test
     void mailboxAddedShouldMatchBeanContract() {
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
index f51f100..f63c48b 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/DTOs.scala
@@ -28,7 +28,7 @@ import org.apache.james.core.Username
 import org.apache.james.core.quota.{QuotaLimitValue, QuotaUsageValue}
 import org.apache.james.event.json.DTOs.SystemFlag.SystemFlag
 import org.apache.james.mailbox.acl.{ACLDiff => JavaACLDiff}
-import org.apache.james.mailbox.model.{MessageId, MailboxACL => JavaMailboxACL, MailboxPath => JavaMailboxPath, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota, UpdatedFlags => JavaUpdatedFlags}
+import org.apache.james.mailbox.model.{MessageId, ThreadId, MailboxACL => JavaMailboxACL, MailboxPath => JavaMailboxPath, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota, UpdatedFlags => JavaUpdatedFlags}
 import org.apache.james.mailbox.{FlagsBuilder, MessageUid, ModSeq}
 
 import scala.jdk.CollectionConverters._
@@ -90,11 +90,13 @@ object DTOs {
       Flags.fromJavaFlags(javaMessageMetaData.getFlags),
       javaMessageMetaData.getSize,
       javaMessageMetaData.getInternalDate.toInstant,
-      javaMessageMetaData.getMessageId)
+      javaMessageMetaData.getMessageId,
+      Option(javaMessageMetaData.getThreadId))
   }
 
-  case class MessageMetaData(uid: MessageUid, modSeq: ModSeq, flags: Flags, size: Long, internalDate: Instant, messageId: MessageId) {
-    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, Flags.toJavaFlags(flags), size, Date.from(internalDate), messageId)
+  case class MessageMetaData(uid: MessageUid, modSeq: ModSeq, flags: Flags, size: Long, internalDate: Instant, messageId: MessageId, threadId: Option[ThreadId]) {
+    def toJava: JavaMessageMetaData = new JavaMessageMetaData(uid, modSeq, Flags.toJavaFlags(flags), size, Date.from(internalDate), messageId, retrieveThreadId)
+    def retrieveThreadId: ThreadId = threadId.getOrElse(ThreadId.fromBaseMessageId(messageId))
   }
 
   case class UserFlag(value: String) extends AnyVal
diff --git a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
index 0b07edc..b446678 100644
--- a/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
+++ b/mailbox/event/json/src/main/scala/org/apache/james/event/json/MailboxEventSerializer.scala
@@ -33,7 +33,7 @@ import org.apache.james.events.{EventSerializer, Event => JavaEvent}
 import org.apache.james.mailbox.MailboxSession.SessionId
 import org.apache.james.mailbox.events.MailboxEvents.{Added => JavaAdded, Expunged => JavaExpunged, FlagsUpdated => JavaFlagsUpdated, MailboxACLUpdated => JavaMailboxACLUpdated, MailboxAdded => JavaMailboxAdded, MailboxDeletion => JavaMailboxDeletion, MailboxRenamed => JavaMailboxRenamed, QuotaUsageUpdatedEvent => JavaQuotaUsageUpdatedEvent}
 import org.apache.james.mailbox.events.{MessageMoveEvent => JavaMessageMoveEvent}
-import org.apache.james.mailbox.model.{MailboxId, MessageId, MessageMoves, QuotaRoot, MailboxACL => JavaMailboxACL, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, MessageMoves, QuotaRoot, ThreadId, MailboxACL => JavaMailboxACL, MessageMetaData => JavaMessageMetaData, Quota => JavaQuota}
 import org.apache.james.mailbox.quota.QuotaRootDeserializer
 import org.apache.james.mailbox.{MessageUid, ModSeq}
 import play.api.libs.json._
@@ -221,6 +221,7 @@ class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactory: Messa
   implicit val aclRightsWrites: Writes[JavaMailboxACL.Rfc4314Rights] = value => JsString(value.serialize())
   implicit val mailboxACLWrites: Writes[MailboxACL] = Json.writes[MailboxACL]
   implicit val aclDiffWrites: Writes[ACLDiff] = Json.writes[ACLDiff]
+  implicit val threadIdWrites: Writes[ThreadId] = value => JsString(value.serialize())
   implicit val messageIdWrites: Writes[MessageId] = value => JsString(value.serialize())
   implicit val messageUidWrites: Writes[MessageUid] = value => JsNumber(value.asLong())
   implicit val modSeqWrites: Writes[ModSeq] = value => JsNumber(value.asLong())
@@ -279,6 +280,10 @@ class JsonSerialize(mailboxIdFactory: MailboxId.Factory, messageIdFactory: Messa
     case JsString(userAsString) => JsSuccess(Username.of(userAsString))
     case _ => JsError()
   }
+  implicit val threadIdReads: Reads[ThreadId] = {
+    case JsString(value) => JsSuccess(ThreadId.fromBaseMessageId(messageIdFactory.fromString(value)))
+    case _ => JsError()
+  }
   implicit val messageIdReads: Reads[MessageId] = {
     case JsString(value) => JsSuccess(messageIdFactory.fromString(value))
     case _ => JsError()
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
index 7477cad..4acc893 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/AddedSerializationTest.java
@@ -44,6 +44,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -66,9 +67,13 @@ class AddedSerializationTest {
         .add("User Custom Flag")
         .build();
     private static final SortedMap<MessageUid, MessageMetaData> ADDED = ImmutableSortedMap.of(
-        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
+    private static final SortedMap<MessageUid, MessageMetaData> ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID = ImmutableSortedMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID, ThreadId.fromBaseMessageId(TestMessageId.of(100))));
 
     private static final Added DEFAULT_ADDED_EVENT = new Added(SESSION_ID, USERNAME, MAILBOX_PATH, MAILBOX_ID, ADDED, EVENT_ID);
+    private static final Added ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT = new Added(
+        SESSION_ID, USERNAME, MAILBOX_PATH, MAILBOX_ID, ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID, EVENT_ID);
     private static final String DEFAULT_ADDED_EVENT_JSON = 
         "{" +
         "  \"Added\": {" +
@@ -88,13 +93,67 @@ class AddedSerializationTest {
         "          \"userFlags\":[\"User Custom Flag\"]}," +
         "        \"size\": 45,  " +
         "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
-        "        \"messageId\": \"42\"" +
+        "        \"messageId\": \"42\"," +
+        "        \"threadId\":\"42\""   +
         "      }" +
         "    }," +
         "    \"sessionId\": 42," +
         "    \"user\": \"user\"" +
         "  }" +
         "}";
+    private static final String ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON =
+        "{" +
+            "  \"Added\": {" +
+            "    \"eventId\":\"6e0dd59d-660e-4d9b-b22f-0354479f47b4\"," +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": {" +
+            "          \"systemFlags\":[\"Answered\",\"Draft\"], " +
+            "          \"userFlags\":[\"User Custom Flag\"]}," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"," +
+            "        \"threadId\":\"100\""   +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+    private static final String DEFAULT_BACKWARD_ADDED_EVENT_JSON =
+        "{" +
+            "  \"Added\": {" +
+            "    \"eventId\":\"6e0dd59d-660e-4d9b-b22f-0354479f47b4\"," +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"added\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": {" +
+            "          \"systemFlags\":[\"Answered\",\"Draft\"], " +
+            "          \"userFlags\":[\"User Custom Flag\"]}," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
 
     @Test
     void addedShouldBeWellSerialized() {
@@ -103,11 +162,23 @@ class AddedSerializationTest {
     }
 
     @Test
+    void addedWithDistinctMessageIdAndThreadIdShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT))
+            .isEqualTo(ADDED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON);
+    }
+
+    @Test
     void addedShouldBeWellDeSerialized() {
         assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_ADDED_EVENT_JSON).get())
             .isEqualTo(DEFAULT_ADDED_EVENT);
     }
 
+    @Test
+    void previousAddedFormatShouldBeWellDeserialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_BACKWARD_ADDED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_ADDED_EVENT);
+    }
+
     @Nested
     class WithEmptyAddedMap {
 
diff --git a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
index 866f241..36c0688 100644
--- a/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
+++ b/mailbox/event/json/src/test/java/org/apache/james/event/json/ExpungedSerializationTest.java
@@ -44,6 +44,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
@@ -66,10 +67,14 @@ class ExpungedSerializationTest {
         .add("User Custom Flag")
         .build();
     private static final Map<MessageUid, MessageMetaData> EXPUNGED = ImmutableMap.of(
-        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID));
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
+    private static final Map<MessageUid, MessageMetaData> EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID = ImmutableMap.of(
+        MESSAGE_UID, new MessageMetaData(MESSAGE_UID, MOD_SEQ, FLAGS, SIZE, Date.from(INSTANT), MESSAGE_ID, ThreadId.fromBaseMessageId(TestMessageId.of(100))));
 
     private static final Expunged DEFAULT_EXPUNGED_EVENT = new Expunged(SESSION_ID, USERNAME,
         MAILBOX_PATH, MAILBOX_ID, EXPUNGED, EVENT_ID);
+    private static final Expunged EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT = new Expunged(
+        SESSION_ID, USERNAME, MAILBOX_PATH, MAILBOX_ID, EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID, EVENT_ID);
     private static final String DEFAULT_EXPUNGED_EVENT_JSON =
         "{" +
         "  \"Expunged\": {" +
@@ -89,13 +94,67 @@ class ExpungedSerializationTest {
         "          \"userFlags\":[\"User Custom Flag\"]}," +
         "        \"size\": 45,  " +
         "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
-        "        \"messageId\": \"42\"" +
+        "        \"messageId\": \"42\"," +
+        "        \"threadId\": \"42\"" +
         "      }" +
         "    }," +
         "    \"sessionId\": 42," +
         "    \"user\": \"user\"" +
         "  }" +
         "}";
+    private static final String EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON =
+        "{" +
+            "  \"Expunged\": {" +
+            "    \"eventId\":\"6e0dd59d-660e-4d9b-b22f-0354479f47b4\"," +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": {" +
+            "          \"systemFlags\":[\"Answered\",\"Draft\"], " +
+            "          \"userFlags\":[\"User Custom Flag\"]}," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"," +
+            "        \"threadId\": \"100\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
+    private static final String DEFAULT_BACKWARD_EXPUNGED_EVENT_JSON =
+        "{" +
+            "  \"Expunged\": {" +
+            "    \"eventId\":\"6e0dd59d-660e-4d9b-b22f-0354479f47b4\"," +
+            "    \"path\": {" +
+            "      \"namespace\": \"#private\"," +
+            "      \"user\": \"user\"," +
+            "      \"name\": \"mailboxName\"" +
+            "    }," +
+            "    \"mailboxId\": \"18\"," +
+            "    \"expunged\": {" +
+            "      \"123456\": {" +
+            "        \"uid\": 123456," +
+            "        \"modSeq\": 35," +
+            "        \"flags\": {" +
+            "          \"systemFlags\":[\"Answered\",\"Draft\"], " +
+            "          \"userFlags\":[\"User Custom Flag\"]}," +
+            "        \"size\": 45,  " +
+            "        \"internalDate\": \"2018-12-14T09:41:51.541Z\"," +
+            "        \"messageId\": \"42\"" +
+            "      }" +
+            "    }," +
+            "    \"sessionId\": 42," +
+            "    \"user\": \"user\"" +
+            "  }" +
+            "}";
 
     @Test
     void expungedShouldBeWellSerialized() {
@@ -104,11 +163,23 @@ class ExpungedSerializationTest {
     }
 
     @Test
+    void expungedWithDistinctMessageIdAndThreadIdShouldBeWellSerialized() {
+        assertThatJson(EVENT_SERIALIZER.toJson(EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT))
+            .isEqualTo(EXPUNGED_WITH_DISTINCT_MESSAGE_ID_AND_THREAD_ID_EVENT_JSON);
+    }
+
+    @Test
     void expungedShouldBeWellDeSerialized() {
         assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_EXPUNGED_EVENT_JSON).get())
             .isEqualTo(DEFAULT_EXPUNGED_EVENT);
     }
 
+    @Test
+    void previousExpungedFormatShouldBeWellDeserialized() {
+        assertThat(EVENT_SERIALIZER.fromJson(DEFAULT_BACKWARD_EXPUNGED_EVENT_JSON).get())
+            .isEqualTo(DEFAULT_EXPUNGED_EVENT);
+    }
+
     @Nested
     class WithEmptyExpungedMap {
 
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java
index b92930c..b34bc84 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java
@@ -110,7 +110,7 @@ public interface MailboxMessage extends Message, Comparable<MailboxMessage> {
     Flags createFlags();
 
     default MessageMetaData metaData() {
-        return new MessageMetaData(getUid(), getModSeq(), createFlags(), getFullContentOctets(), getInternalDate(), getMessageId());
+        return new MessageMetaData(getUid(), getModSeq(), createFlags(), getFullContentOctets(), getInternalDate(), getMessageId(), getThreadId());
     }
 
     default int compareTo(MailboxMessage other) {
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/PreDeletionHooksTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/PreDeletionHooksTest.java
index bcfc430..785aba4 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/PreDeletionHooksTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/PreDeletionHooksTest.java
@@ -43,6 +43,7 @@ import org.apache.james.mailbox.extension.PreDeletionHook;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.TestId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -59,7 +60,7 @@ class PreDeletionHooksTest {
     private static final TestId MAILBOX_ID = TestId.of(45);
     private static final ModSeq MOD_SEQ = ModSeq.of(18);
     private static final int SIZE = 12;
-    private static final MessageMetaData MESSAGE_META_DATA = new MessageMetaData(MessageUid.of(1), MOD_SEQ, new Flags(), SIZE, new Date(), TestMessageId.of(42));
+    private static final MessageMetaData MESSAGE_META_DATA = new MessageMetaData(MessageUid.of(1), MOD_SEQ, new Flags(), SIZE, new Date(), TestMessageId.of(42), ThreadId.fromBaseMessageId(TestMessageId.of(42)));
     private static final PreDeletionHook.DeleteOperation DELETE_OPERATION = PreDeletionHook.DeleteOperation.from(ImmutableList.of(MetadataWithMailboxId.from(
         MESSAGE_META_DATA,
         MAILBOX_ID)));
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MetadataMapAssertTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MetadataMapAssertTest.java
index 28e95ec..ef4796c 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MetadataMapAssertTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MetadataMapAssertTest.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.model.ByteContent;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
 import org.junit.jupiter.api.BeforeEach;
@@ -61,7 +62,7 @@ class MetadataMapAssertTest {
     @Test
     void metadataMapAssertShouldSucceedWhenContainingRightMetadata() {
         Map<MessageUid, MessageMetaData> metaDataMap = new HashMap<>();
-        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), DATE, MESSAGE_ID));
+        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), DATE, MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
         
         MetadataMapAssert.assertThat(metaDataMap).containsMetadataForMessages(message1);
     }
@@ -69,7 +70,7 @@ class MetadataMapAssertTest {
     @Test
     void metadataMapAssertShouldFailWhenUidMismatch() {
         Map<MessageUid, MessageMetaData> metaDataMap = new HashMap<>();
-        metaDataMap.put(UID, new MessageMetaData(UID.next(), MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), DATE, MESSAGE_ID));
+        metaDataMap.put(UID, new MessageMetaData(UID.next(), MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), DATE, MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
         
         assertThatThrownBy(() -> MetadataMapAssert.assertThat(metaDataMap).containsMetadataForMessages(message1))
             .isInstanceOf(AssertionError.class);
@@ -80,7 +81,7 @@ class MetadataMapAssertTest {
         Map<MessageUid, MessageMetaData> metaDataMap = new HashMap<>();
         Date date = new Date();
         date.setTime(DATE.getTime() + 100L);
-        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), date, MESSAGE_ID));
+        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length(), date, MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
 
         assertThatThrownBy(() -> MetadataMapAssert.assertThat(metaDataMap).containsMetadataForMessages(message1))
             .isInstanceOf(AssertionError.class);
@@ -89,7 +90,7 @@ class MetadataMapAssertTest {
     @Test
     void metadataMapAssertShouldFailWhenSizeMismatch() {
         Map<MessageUid, MessageMetaData> metaDataMap = new HashMap<>();
-        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length() + 1, DATE, MESSAGE_ID));
+        metaDataMap.put(UID, new MessageMetaData(UID, MODSEQ, new Flags(), HEADER_STRING.length() + BODY_STRING.length() + 1, DATE, MESSAGE_ID, ThreadId.fromBaseMessageId(MESSAGE_ID)));
 
         assertThatThrownBy(() -> MetadataMapAssert.assertThat(metaDataMap).containsMetadataForMessages(message1))
             .isInstanceOf(AssertionError.class);
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/ListeningCurrentQuotaUpdaterTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/ListeningCurrentQuotaUpdaterTest.java
index f71cba2..9099ede 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/ListeningCurrentQuotaUpdaterTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/ListeningCurrentQuotaUpdaterTest.java
@@ -51,6 +51,7 @@ import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.QuotaOperation;
 import org.apache.james.mailbox.model.QuotaRoot;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.quota.CurrentQuotaManager;
 import org.apache.james.mailbox.quota.QuotaManager;
 import org.apache.james.mailbox.quota.QuotaRootResolver;
@@ -99,8 +100,8 @@ class ListeningCurrentQuotaUpdaterTest {
         Added added = mock(Added.class);
         when(added.getMailboxId()).thenReturn(MAILBOX_ID);
         when(added.getMailboxPath()).thenReturn(MAILBOX_PATH);
-        when(added.getMetaData(MessageUid.of(36))).thenReturn(new MessageMetaData(MessageUid.of(36), ModSeq.first(),new Flags(), SIZE, new Date(), new DefaultMessageId()));
-        when(added.getMetaData(MessageUid.of(38))).thenReturn(new MessageMetaData(MessageUid.of(38), ModSeq.first(),new Flags(), SIZE, new Date(), new DefaultMessageId()));
+        when(added.getMetaData(MessageUid.of(36))).thenReturn(new MessageMetaData(MessageUid.of(36), ModSeq.first(),new Flags(), SIZE, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())));
+        when(added.getMetaData(MessageUid.of(38))).thenReturn(new MessageMetaData(MessageUid.of(38), ModSeq.first(),new Flags(), SIZE, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())));
         when(added.getUids()).thenReturn(Lists.newArrayList(MessageUid.of(36), MessageUid.of(38)));
         when(added.getUsername()).thenReturn(USERNAME_BENWA);
         when(mockedQuotaRootResolver.getQuotaRootReactive(eq(MAILBOX_ID))).thenReturn(Mono.just(QUOTA_ROOT));
@@ -115,8 +116,8 @@ class ListeningCurrentQuotaUpdaterTest {
     @Test
     void expungedEventShouldDecreaseCurrentQuotaValues() throws Exception {
         Expunged expunged = mock(Expunged.class);
-        when(expunged.getMetaData(MessageUid.of(36))).thenReturn(new MessageMetaData(MessageUid.of(36), ModSeq.first(), new Flags(), SIZE, new Date(), new DefaultMessageId()));
-        when(expunged.getMetaData(MessageUid.of(38))).thenReturn(new MessageMetaData(MessageUid.of(38), ModSeq.first(), new Flags(), SIZE, new Date(), new DefaultMessageId()));
+        when(expunged.getMetaData(MessageUid.of(36))).thenReturn(new MessageMetaData(MessageUid.of(36), ModSeq.first(), new Flags(), SIZE, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())));
+        when(expunged.getMetaData(MessageUid.of(38))).thenReturn(new MessageMetaData(MessageUid.of(38), ModSeq.first(), new Flags(), SIZE, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())));
         when(expunged.getUids()).thenReturn(Lists.newArrayList(MessageUid.of(36), MessageUid.of(38)));
         when(expunged.getMailboxId()).thenReturn(MAILBOX_ID);
         when(expunged.getUsername()).thenReturn(USERNAME_BENWA);
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
index f069286..0029a36 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
@@ -54,6 +54,7 @@ import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MessageResultIterator;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.event.EventFactory;
@@ -129,7 +130,7 @@ class MailboxEventAnalyserTest {
         .randomEventId()
         .mailboxSession(MAILBOX_SESSION)
         .mailbox(DEFAULT_MAILBOX)
-        .addMetaData(new MessageMetaData(MessageUid.of(11), ModSeq.first(), new Flags(), 45, new Date(), new DefaultMessageId()))
+        .addMetaData(new MessageMetaData(MessageUid.of(11), ModSeq.first(), new Flags(), 45, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())))
         .build();
 
     private SelectedMailboxImpl testee;
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
index a38aa36..353001f 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
@@ -61,6 +61,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.event.EventFactory;
 import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
@@ -218,7 +219,7 @@ class SelectedMailboxImplTest {
             .randomEventId()
             .mailboxSession(MailboxSessionUtil.create(Username.of("user")))
             .mailbox(mailbox)
-            .addMetaData(new MessageMetaData(EMITTED_EVENT_UID, MOD_SEQ, new Flags(), SIZE, new Date(), new DefaultMessageId()))
+            .addMetaData(new MessageMetaData(EMITTED_EVENT_UID, MOD_SEQ, new Flags(), SIZE, new Date(), new DefaultMessageId(), ThreadId.fromBaseMessageId(new DefaultMessageId())))
             .build();
     }
 
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
index bc5effd..0314b81 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesCreationProcessorTest.java
@@ -71,6 +71,7 @@ import org.apache.james.mailbox.model.MailboxId.Factory;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.rrt.api.AliasReverseResolver;
 import org.apache.james.rrt.api.CanSendFrom;
@@ -200,7 +201,7 @@ public class SetMessagesCreationProcessorTest {
         
         when(outbox.appendMessageReactive(any(MessageManager.AppendCommand.class), any(MailboxSession.class)))
             .thenReturn(Mono.just(new MessageManager.AppendResult(new ComposedMessageId(OUTBOX_ID, TestMessageId.of(23), MessageUid.of(1)), TEST_MESSAGE_SIZE,
-                Optional.of(ImmutableList.of()))));
+                Optional.of(ImmutableList.of()), ThreadId.fromBaseMessageId(TestMessageId.of(23)))));
 
         drafts = mock(MessageManager.class);
         when(drafts.getId()).thenReturn(DRAFTS_ID);
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
index 9c643cd..5cd6514 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/methods/SetMessagesUpdateProcessorTest.java
@@ -62,6 +62,7 @@ import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.rrt.api.AliasReverseResolver;
 import org.apache.james.rrt.api.CanSendFrom;
@@ -202,7 +203,7 @@ public class SetMessagesUpdateProcessorTest {
         when(outbox.appendMessage(any(MessageManager.AppendCommand.class), any(MailboxSession.class)))
             .thenReturn(new MessageManager.AppendResult(
                 new ComposedMessageId(OUTBOX_ID, TestMessageId.of(23), MessageUid.of(1)), TEST_MESSAGE_SIZE,
-                Optional.empty()));
+                Optional.empty(), ThreadId.fromBaseMessageId(TestMessageId.of(23))));
 
         drafts = mock(MessageManager.class);
         when(drafts.getId()).thenReturn(DRAFTS_ID);

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org