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/02/14 00:12:11 UTC

[james-project] branch master updated: JAMES-3882 Move messages into DeletedMessageVault asynchronously for … (#1436)

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


The following commit(s) were added to refs/heads/master by this push:
     new b3a3c20249 JAMES-3882 Move messages into DeletedMessageVault asynchronously for … (#1436)
b3a3c20249 is described below

commit b3a3c20249fd7e6759ee9a7db823e01199bc12c1
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Tue Feb 14 07:12:05 2023 +0700

    JAMES-3882 Move messages into DeletedMessageVault asynchronously for … (#1436)
---
 .../james/mailbox/MetadataWithMailboxId.java       |  24 ++--
 .../apache/james/mailbox/MailboxManagerTest.java   |  12 +-
 .../CassandraMailboxSessionMapperFactory.java      |   3 +-
 .../mailbox/cassandra/DeleteMessageListener.java   |  27 +++--
 .../deleted-messages-vault-cassandra/pom.xml       |   4 +
 .../DeletedMessageVaultDeletionCallback.java       | 133 +++++++++++++++++++++
 .../james/vault/DeletedMessageVaultHook.java       |   2 +-
 .../james/mailbox/store/StoreMailboxManager.java   |   4 +-
 .../james/mailbox/store/StoreMessageIdManager.java |  20 ++--
 .../AbstractMessageIdManagerSideEffectTest.java    |   8 +-
 .../sample-configuration/listeners.xml             |   6 -
 .../modules/ROOT/pages/configure/listeners.adoc    |  26 ----
 .../docs/modules/ROOT/pages/configure/vault.adoc   |   5 +-
 .../sample-configuration/listeners.xml             |   7 --
 .../sample-configuration/listeners.xml             |   6 -
 .../CassandraDeletedMessageVaultModule.java        |   6 +
 .../modules/mailbox/CassandraMailboxModule.java    |   1 +
 ...RabbitMQDeletedMessageVaultIntegrationTest.java |   5 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |   3 +-
 .../vault/DeletedMessageVaultIntegrationTest.java  |   7 ++
 src/homepage/howTo/deleted-messages-vault.html     |   1 +
 src/site/xdoc/server/config-listeners.xml          |   1 +
 upgrade-instructions.md                            |  12 ++
 23 files changed, 229 insertions(+), 94 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MetadataWithMailboxId.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MetadataWithMailboxId.java
index a33345ee58..8fb98bfd3c 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MetadataWithMailboxId.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MetadataWithMailboxId.java
@@ -22,24 +22,31 @@ package org.apache.james.mailbox;
 import java.util.Objects;
 
 import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
 
 public class MetadataWithMailboxId {
 
     public static MetadataWithMailboxId from(MessageMetaData messageMetaData, MailboxId mailboxId) {
-        return new MetadataWithMailboxId(messageMetaData, mailboxId);
+        return new MetadataWithMailboxId(messageMetaData.getMessageId(), messageMetaData.getSize(), mailboxId);
     }
 
-    private final MessageMetaData messageMetaData;
+    private final MessageId messageId;
+    private final long size;
     private final MailboxId mailboxId;
 
-    private MetadataWithMailboxId(MessageMetaData messageMetaData, MailboxId mailboxId) {
-        this.messageMetaData = messageMetaData;
+    public MetadataWithMailboxId(MessageId messageId, long size, MailboxId mailboxId) {
+        this.messageId = messageId;
+        this.size = size;
         this.mailboxId = mailboxId;
     }
 
-    public MessageMetaData getMessageMetaData() {
-        return messageMetaData;
+    public MessageId getMessageId() {
+        return messageId;
+    }
+
+    public long getSize() {
+        return size;
     }
 
     public MailboxId getMailboxId() {
@@ -51,7 +58,8 @@ public class MetadataWithMailboxId {
         if (o instanceof MetadataWithMailboxId) {
             MetadataWithMailboxId that = (MetadataWithMailboxId) o;
 
-            return Objects.equals(this.messageMetaData, that.messageMetaData)
+            return Objects.equals(this.messageId, that.messageId)
+                && Objects.equals(this.size, that.size)
                 && Objects.equals(this.mailboxId, that.mailboxId);
         }
         return false;
@@ -59,6 +67,6 @@ public class MetadataWithMailboxId {
 
     @Override
     public final int hashCode() {
-        return Objects.hash(messageMetaData, mailboxId);
+        return Objects.hash(messageId, size, mailboxId);
     }
 }
\ No newline at end of file
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index 79e66ae419..5bd6502f16 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -2599,7 +2599,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList())
                     .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                         softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(inboxId);
-                        softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(composeId.getMessageId());
+                        softy.assertThat(deleteMetadata.getMessageId()).isEqualTo(composeId.getMessageId());
                     }));
             }
 
@@ -2620,7 +2620,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList())
                     .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                         softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(inboxId);
-                        softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(composeId.getMessageId());
+                        softy.assertThat(deleteMetadata.getMessageId()).isEqualTo(composeId.getMessageId());
                     }));
             }
 
@@ -2641,7 +2641,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList())
                     .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                         softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(inboxId);
-                        softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(composeId.getMessageId());
+                        softy.assertThat(deleteMetadata.getMessageId()).isEqualTo(composeId.getMessageId());
                     }));
             }
 
@@ -2667,7 +2667,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .hasSameElementsAs(preDeleteCaptor2.getAllValues())
                     .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList)
                     .allSatisfy(deleteMetadata -> assertThat(deleteMetadata.getMailboxId()).isEqualTo(inboxId))
-                    .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId())
+                    .extracting(deleteMetadata -> deleteMetadata.getMessageId())
                     .containsOnly(composeId1.getMessageId(), composeId2.getMessageId());
             }
 
@@ -2691,7 +2691,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList())
                     .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                         softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(inboxId);
-                        softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(composeId1.getMessageId());
+                        softy.assertThat(deleteMetadata.getMessageId()).isEqualTo(composeId1.getMessageId());
                     }));
             }
 
@@ -2726,7 +2726,7 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                 assertThat(preDeleteCaptor1.getAllValues())
                     .hasSameElementsAs(preDeleteCaptor2.getAllValues())
                     .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList)
-                    .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId())
+                    .extracting(deleteMetadata -> deleteMetadata.getMessageId())
                     .containsOnly(composeId1.getMessageId(), composeId2.getMessageId());
 
                 assertThat(preDeleteCaptor1.getAllValues())
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index 22e150af51..84d86b1882 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -64,6 +64,7 @@ import org.apache.james.mailbox.store.mail.UidProvider;
 import org.apache.james.mailbox.store.user.SubscriptionMapper;
 
 import com.datastax.oss.driver.api.core.CqlSession;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Cassandra implementation of {@link MailboxSessionMapperFactory}
@@ -233,6 +234,6 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     public DeleteMessageListener deleteMessageListener() {
         return new DeleteMessageListener(threadDAO, threadLookupDAO, imapUidDAO, messageIdDAO, messageDAO, messageDAOV3, attachmentDAOV2,
             attachmentMessageIdDAO, aclMapper, userMailboxRightsDAO, applicableFlagDAO, firstUnseenDAO, deletedMessageDAO,
-            mailboxCounterDAO, mailboxRecentsDAO, blobStore, cassandraConfiguration);
+            mailboxCounterDAO, mailboxRecentsDAO, blobStore, cassandraConfiguration, ImmutableSet.of());
     }
 }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java
index 7817f93f82..d24f781761 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java
@@ -25,6 +25,7 @@ import static org.apache.james.util.FunctionalUtils.negate;
 import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY;
 
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Predicate;
 
 import javax.inject.Inject;
@@ -32,6 +33,7 @@ import javax.inject.Inject;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.JamesExecutionProfiles;
 import org.apache.james.blob.api.BlobStore;
+import org.apache.james.core.Username;
 import org.apache.james.events.Event;
 import org.apache.james.events.EventListener;
 import org.apache.james.events.Group;
@@ -60,6 +62,8 @@ import org.apache.james.mailbox.events.MailboxEvents.Expunged;
 import org.apache.james.mailbox.events.MailboxEvents.MailboxDeletion;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MailboxACL;
+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.store.mail.MessageMapper;
@@ -87,6 +91,11 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
 
     }
 
+    @FunctionalInterface
+    public interface DeletionCallback {
+        Mono<Void> forMessage(MessageRepresentation message, MailboxId mailboxId, Username owner);
+    }
+
     private final CassandraThreadDAO threadDAO;
     private final CassandraThreadLookupDAO threadLookupDAO;
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
@@ -104,6 +113,7 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
     private final CassandraMailboxRecentsDAO recentsDAO;
     private final BlobStore blobStore;
     private final CassandraConfiguration cassandraConfiguration;
+    private final Set<DeletionCallback> deletionCallbackList;
 
     @Inject
     public DeleteMessageListener(CassandraThreadDAO threadDAO, CassandraThreadLookupDAO threadLookupDAO,
@@ -113,7 +123,7 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
                                  CassandraUserMailboxRightsDAO rightsDAO, CassandraApplicableFlagDAO applicableFlagDAO,
                                  CassandraFirstUnseenDAO firstUnseenDAO, CassandraDeletedMessageDAO deletedMessageDAO,
                                  CassandraMailboxCounterDAO counterDAO, CassandraMailboxRecentsDAO recentsDAO, BlobStore blobStore,
-                                 CassandraConfiguration cassandraConfiguration) {
+                                 CassandraConfiguration cassandraConfiguration, Set<DeletionCallback> deletionCallbackList) {
         this.threadDAO = threadDAO;
         this.threadLookupDAO = threadLookupDAO;
         this.imapUidDAO = imapUidDAO;
@@ -131,6 +141,7 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
         this.recentsDAO = recentsDAO;
         this.blobStore = blobStore;
         this.cassandraConfiguration = cassandraConfiguration;
+        this.deletionCallbackList = deletionCallbackList;
     }
 
     @Override
@@ -155,18 +166,18 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
 
             CassandraId mailboxId = (CassandraId) mailboxDeletion.getMailboxId();
 
-            return handleMailboxDeletion(mailboxId);
+            return handleMailboxDeletion(mailboxId, mailboxDeletion.getMailboxPath());
         }
         return Mono.empty();
     }
 
-    private Mono<Void> handleMailboxDeletion(CassandraId mailboxId) {
+    private Mono<Void> handleMailboxDeletion(CassandraId mailboxId, MailboxPath path) {
         int prefetch = 1;
         return Flux.mergeDelayError(prefetch,
                 messageIdDAO.retrieveMessages(mailboxId, MessageRange.all(), Limit.unlimited())
                     .map(CassandraMessageMetadata::getComposedMessageId)
                     .map(ComposedMessageIdWithMetaData::getComposedMessageId)
-                    .concatMap(metadata -> handleMessageDeletionAsPartOfMailboxDeletion((CassandraMessageId) metadata.getMessageId(), mailboxId)
+                    .concatMap(metadata -> handleMessageDeletionAsPartOfMailboxDeletion((CassandraMessageId) metadata.getMessageId(), mailboxId, path.getUser())
                         .then(imapUidDAO.delete((CassandraMessageId) metadata.getMessageId(), mailboxId))
                         .then(messageIdDAO.delete(mailboxId, metadata.getUid()))),
                 deleteAcl(mailboxId),
@@ -183,7 +194,7 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
             .values())
             .map(MessageMetaData::getMessageId)
             .map(CassandraMessageId.class::cast)
-            .concatMap(this::handleMessageDeletion)
+            .concatMap(messageId -> handleMessageDeletion(messageId, expunged.getMailboxId(), expunged.getMailboxPath().getUser()))
             .then();
     }
 
@@ -193,10 +204,11 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
                 .then(aclMapper.delete(mailboxId)));
     }
 
-    private Mono<Void> handleMessageDeletion(CassandraMessageId messageId) {
+    private Mono<Void> handleMessageDeletion(CassandraMessageId messageId, MailboxId mailboxId, Username owner) {
         return Mono.just(messageId)
             .filterWhen(this::isReferenced)
             .flatMap(id -> readMessage(id)
+                .flatMap(message -> Flux.fromIterable(deletionCallbackList).concatMap(callback -> callback.forMessage(message, mailboxId, owner)).then().thenReturn(message))
                 .flatMap(message -> deleteUnreferencedAttachments(message).thenReturn(message))
                 .flatMap(this::deleteMessageBlobs)
                 .flatMap(this::deleteAttachmentMessageIds)
@@ -208,10 +220,11 @@ public class DeleteMessageListener implements EventListener.ReactiveGroupEventLi
                 .then(threadLookupDAO.deleteOneRow(messageId)));
     }
 
-    private Mono<Void> handleMessageDeletionAsPartOfMailboxDeletion(CassandraMessageId messageId, CassandraId excludedId) {
+    private Mono<Void> handleMessageDeletionAsPartOfMailboxDeletion(CassandraMessageId messageId, CassandraId excludedId, Username owner) {
         return Mono.just(messageId)
             .filterWhen(id -> isReferenced(id, excludedId))
             .flatMap(id -> readMessage(id)
+                .flatMap(message -> Flux.fromIterable(deletionCallbackList).concatMap(callback -> callback.forMessage(message, excludedId, owner)).then().thenReturn(message))
                 .flatMap(message -> deleteUnreferencedAttachments(message).thenReturn(message))
                 .flatMap(this::deleteMessageBlobs)
                 .flatMap(this::deleteAttachmentMessageIds)
diff --git a/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml
index a56d98340d..f66d3bba84 100644
--- a/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml
+++ b/mailbox/plugin/deleted-messages-vault-cassandra/pom.xml
@@ -55,6 +55,10 @@
             <type>test-jar</type>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-cassandra</artifactId>
+        </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
diff --git a/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageVaultDeletionCallback.java b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageVaultDeletionCallback.java
new file mode 100644
index 0000000000..57746c6be7
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault-cassandra/src/main/java/org/apache/james/vault/metadata/DeletedMessageVaultDeletionCallback.java
@@ -0,0 +1,133 @@
+/****************************************************************
+ * 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.vault.metadata;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.apache.james.blob.api.BlobStore;
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.MaybeSender;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.cassandra.DeleteMessageListener;
+import org.apache.james.mailbox.cassandra.mail.MessageRepresentation;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mime4j.MimeIOException;
+import org.apache.james.mime4j.codec.DecodeMonitor;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.message.DefaultMessageBuilder;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.server.core.Envelope;
+import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageVault;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ImmutableSet;
+
+import reactor.core.publisher.Mono;
+
+public class DeletedMessageVaultDeletionCallback implements DeleteMessageListener.DeletionCallback {
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeletedMessageVaultDeletionCallback.class);
+
+
+    private final DeletedMessageVault deletedMessageVault;
+    private final BlobStore blobStore;
+    private final Clock clock;
+
+    @Inject
+    public DeletedMessageVaultDeletionCallback(DeletedMessageVault deletedMessageVault, BlobStore blobStore, Clock clock) {
+        this.deletedMessageVault = deletedMessageVault;
+        this.blobStore = blobStore;
+        this.clock = clock;
+    }
+
+    @Override
+    public Mono<Void> forMessage(MessageRepresentation message, MailboxId mailboxId, Username owner) {
+        return Mono.from(blobStore.readBytes(blobStore.getDefaultBucketName(), message.getHeaderId(), BlobStore.StoragePolicy.LOW_COST))
+            .flatMap(bytes -> {
+                Optional<Message> mimeMessage = parseMessage(new ByteArrayInputStream(bytes), message.getMessageId());
+                DeletedMessage deletedMessage = DeletedMessage.builder()
+                    .messageId(message.getMessageId())
+                    .originMailboxes(mailboxId)
+                    .user(owner)
+                    .deliveryDate(ZonedDateTime.ofInstant(message.getInternalDate().toInstant(), ZoneOffset.UTC))
+                    .deletionDate(ZonedDateTime.ofInstant(clock.instant(), ZoneOffset.UTC))
+                    .sender(retrieveSender(mimeMessage))
+                    .recipients(retrieveRecipients(mimeMessage))
+                    .hasAttachment(!message.getAttachments().isEmpty())
+                    .size(message.getSize())
+                    .subject(mimeMessage.map(Message::getSubject))
+                    .build();
+
+                return Mono.from(blobStore.readReactive(blobStore.getDefaultBucketName(), message.getBodyId(), BlobStore.StoragePolicy.LOW_COST))
+                    .map(bodyStream -> new SequenceInputStream(new ByteArrayInputStream(bytes), bodyStream))
+                    .flatMap(bodyStream -> Mono.from(deletedMessageVault.append(deletedMessage, bodyStream)));
+            });
+    }
+
+    private Optional<Message> parseMessage(InputStream inputStream, MessageId messageId) {
+        DefaultMessageBuilder messageBuilder = new DefaultMessageBuilder();
+        messageBuilder.setMimeEntityConfig(MimeConfig.PERMISSIVE);
+        messageBuilder.setDecodeMonitor(DecodeMonitor.SILENT);
+        try {
+            return Optional.ofNullable(messageBuilder.parseMessage(inputStream));
+        } catch (MimeIOException e) {
+            LOGGER.warn("Can not parse the message {}", messageId, e);
+            return Optional.empty();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    private ZonedDateTime retrieveDeliveryDate(Optional<Message> mimeMessage, org.apache.james.mailbox.store.mail.model.Message message) {
+        return mimeMessage.map(Message::getDate)
+            .map(Date::toInstant)
+            .map(instant -> ZonedDateTime.ofInstant(instant, ZoneOffset.UTC))
+            .orElse(ZonedDateTime.ofInstant(message.getInternalDate().toInstant(), ZoneOffset.UTC));
+    }
+
+    private MaybeSender retrieveSender(Optional<Message> mimeMessage) {
+        return mimeMessage
+            .map(Message::getSender)
+            .map(Mailbox::getAddress)
+            .map(MaybeSender::getMailSender)
+            .orElse(MaybeSender.nullSender());
+    }
+
+    private Set<MailAddress> retrieveRecipients(Optional<Message> maybeMessage) {
+        return maybeMessage.map(message -> Envelope.fromMime4JMessage(message, Envelope.ValidationPolicy.IGNORE))
+            .map(Envelope::getRecipients)
+            .orElse(ImmutableSet.of());
+    }
+}
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVaultHook.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVaultHook.java
index 0af3a67b56..3e5c4b5499 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVaultHook.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVaultHook.java
@@ -140,7 +140,7 @@ public class DeletedMessageVaultHook implements PreDeletionHook {
     private Flux<DeletedMessageMailboxContext> addOwnerToMetadata(GroupedFlux<MailboxId, MetadataWithMailboxId> groupedFlux) {
         return retrieveMailboxUser(groupedFlux.key())
             .flatMapMany(owner -> groupedFlux.map(metadata ->
-                new DeletedMessageMailboxContext(metadata.getMessageMetaData().getMessageId(), owner, ImmutableList.of(metadata.getMailboxId()))));
+                new DeletedMessageMailboxContext(metadata.getMessageId(), owner, ImmutableList.of(metadata.getMailboxId()))));
     }
 
     private Mono<Username> retrieveMailboxUser(MailboxId mailboxId) {
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index 0ac05f4934..aae28f62b9 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -67,7 +67,6 @@ import org.apache.james.mailbox.model.MailboxMetaData.Selectability;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageId.Factory;
-import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.QuotaRoot;
@@ -494,8 +493,7 @@ public class StoreMailboxManager implements MailboxManager {
             .collect(ImmutableList.toImmutableList())
             .flatMap(metadata -> {
                 long totalSize = metadata.stream()
-                    .map(MetadataWithMailboxId::getMessageMetaData)
-                    .mapToLong(MessageMetaData::getSize)
+                    .mapToLong(MetadataWithMailboxId::getSize)
                     .sum();
 
                 return preDeletionHooks.runHooks(PreDeletionHook.DeleteOperation.from(metadata))
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
index 99e3c0dda1..6ffa10f1ca 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
@@ -81,6 +81,7 @@ import com.github.fge.lambdas.Throwing;
 import com.github.fge.lambdas.functions.ThrowingFunction;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
@@ -264,15 +265,16 @@ public class StoreMessageIdManager implements MessageIdManager {
     }
 
     private Mono<Void> deleteWithPreHooks(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession) {
-        ImmutableList<MetadataWithMailboxId> metadataWithMailbox = messageList.stream()
-            .map(mailboxMessage -> MetadataWithMailboxId.from(mailboxMessage.metaData(), mailboxMessage.getMailboxId()))
-            .collect(ImmutableList.toImmutableList());
+        ImmutableMap<MetadataWithMailboxId, MessageMetaData> metadataWithMailbox = messageList.stream()
+            .collect(ImmutableMap.toImmutableMap(
+                message -> MetadataWithMailboxId.from(message.metaData(), message.getMailboxId()),
+                MailboxMessage::metaData));
 
-        return preDeletionHooks.runHooks(PreDeletionHook.DeleteOperation.from(metadataWithMailbox))
+        return preDeletionHooks.runHooks(PreDeletionHook.DeleteOperation.from(metadataWithMailbox.keySet().asList()))
             .then(delete(messageIdMapper, messageList, mailboxSession, metadataWithMailbox));
     }
 
-    private Mono<Void> delete(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession, ImmutableList<MetadataWithMailboxId> metadataWithMailbox) {
+    private Mono<Void> delete(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession, Map<MetadataWithMailboxId, MessageMetaData> metadataWithMailbox) {
         MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(mailboxSession);
 
         return messageIdMapper.deleteReactive(
@@ -281,15 +283,15 @@ public class StoreMessageIdManager implements MessageIdManager {
                     Message::getMessageId,
                     MailboxMessage::getMailboxId)))
             .then(
-                Flux.fromIterable(metadataWithMailbox)
-                    .flatMap(metadataWithMailboxId -> mailboxMapper.findMailboxById(metadataWithMailboxId.getMailboxId())
+                Flux.fromIterable(metadataWithMailbox.entrySet())
+                    .flatMap(metadataWithMailboxId -> mailboxMapper.findMailboxById(metadataWithMailboxId.getKey().getMailboxId())
                         .flatMap(mailbox -> eventBus.dispatch(EventFactory.expunged()
                                 .randomEventId()
                                 .mailboxSession(mailboxSession)
                                 .mailbox(mailbox)
-                                .addMetaData(metadataWithMailboxId.getMessageMetaData())
+                                .addMetaData(metadataWithMailboxId.getValue())
                                 .build(),
-                            new MailboxIdRegistrationKey(metadataWithMailboxId.getMailboxId()))), DEFAULT_CONCURRENCY)
+                            new MailboxIdRegistrationKey(metadataWithMailboxId.getKey().getMailboxId()))), DEFAULT_CONCURRENCY)
                     .then());
     }
 
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
index f3fa4a1bea..7823b7d401 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
@@ -217,8 +217,7 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
             .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList())
             .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                 softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(mailbox1.getMailboxId());
-                softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(messageId);
-                softy.assertThat(deleteMetadata.getMessageMetaData().getFlags()).isEqualTo(FLAGS);
+                softy.assertThat(deleteMetadata.getMessageId()).isEqualTo(messageId);
             }));
 
     }
@@ -243,9 +242,8 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
             .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList)
             .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> {
                 softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(mailbox1.getMailboxId());
-                softy.assertThat(deleteMetadata.getMessageMetaData().getFlags()).isEqualTo(FLAGS);
             }))
-            .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId())
+            .extracting(deleteMetadata -> deleteMetadata.getMessageId())
             .containsOnly(messageId1, messageId2);
 
     }
@@ -267,7 +265,7 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
         assertThat(preDeleteCaptor1.getAllValues())
             .hasSameElementsAs(preDeleteCaptor2.getAllValues())
             .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList)
-            .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId())
+            .extracting(deleteMetadata -> deleteMetadata.getMessageId())
             .containsOnly(messageId1, messageId2);
 
         assertThat(preDeleteCaptor1.getAllValues())
diff --git a/server/apps/cassandra-app/sample-configuration/listeners.xml b/server/apps/cassandra-app/sample-configuration/listeners.xml
index 9803da2286..d7c9ae12d3 100644
--- a/server/apps/cassandra-app/sample-configuration/listeners.xml
+++ b/server/apps/cassandra-app/sample-configuration/listeners.xml
@@ -24,12 +24,6 @@
   <listener>
     <class>org.apache.james.mailbox.cassandra.MailboxOperationLoggingListener</class>
   </listener>
-  <!-- Requires Deleted Message Vault to be activated. See deletedMessageVault.properties -->
-  <!--
-  <preDeletionHook>
-    <class>org.apache.james.vault.DeletedMessageVaultHook</class>
-  </preDeletionHook>
-  -->
 
   <!-- Enable to populate JMAP EmailQueryView -->
   <!--
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/listeners.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/listeners.adoc
index b9440fba89..1983741022 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/listeners.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/listeners.adoc
@@ -171,29 +171,3 @@ To mitigate this issue you might reach your administrator in order to increase y
 
 This MailboxListener is supported.
 
-== PreDeletionHook configuration
-
-Before deleting a message in James, this message and some related information about the deletion will be passed to a set of PreDeletionHook instances,
-This process is called notifying, and it acts sequentially. If the notifying process for all PreDeletionHooks finish successfully, then the message will be processed to be deleted.
-Otherwise, that message won't be deleted.
-
-Pre Deletion Hook configuration is under the XML element <preDeletionHook>
-
-Already provided additional pre deletion hooks includes:
-
-* `org.apache.james.vault.DeletedMessageVaultHook`: Storing messages about being deleted into
-`org.apache.james.vault.DeletedMessageVault`
-
-
-Example:
-
-....
-<listeners>
-  <!-- ... -->
-  <preDeletionHook>
-    <class>org.apache.james.vault.DeletedMessageVaultHook</class>
-  </preDeletionHook>
-</listeners>
-....
-
-Read also xref:configure/vault.adoc[this page] for extra configuration required to support this feature.
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/vault.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/vault.adoc
index 4a6ccae55b..75da84cc3a 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/vault.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/vault.adoc
@@ -9,10 +9,7 @@ xref:operate/webadmin.adoc#_deleted_messages_vault[WebAdmin endpoints].
 
 == Deleted Messages Vault Configuration
 
-To make James use DeletedMessageVault, you need to configure the PreDeletionHook for it.
-
-How to do this is documented in the xref:configure/listeners.adoc[*listeners.xml*]
-Mailbox Listener Configuration page.
+Once the vault is active, James will start moving deleted messages to it asynchronously.
 
 The Deleted Messages Vault also stores and manages deleted messages into a BlobStore. The BlobStore can be either
 based on an object storage or on Cassandra. For configuring the BlobStore the vault will use, you can look at
diff --git a/server/apps/distributed-app/sample-configuration/listeners.xml b/server/apps/distributed-app/sample-configuration/listeners.xml
index d90a373d6e..94eba430d4 100644
--- a/server/apps/distributed-app/sample-configuration/listeners.xml
+++ b/server/apps/distributed-app/sample-configuration/listeners.xml
@@ -26,13 +26,6 @@
     <class>org.apache.james.mailbox.cassandra.MailboxOperationLoggingListener</class>
   </listener>
 
-  <!-- Requires Deleted Message Vault to be activated. See deletedMessageVault.properties -->
-  <!--
-  <preDeletionHook>
-    <class>org.apache.james.vault.DeletedMessageVaultHook</class>
-  </preDeletionHook>
-  -->
-
   <!-- Enable to populate JMAP EmailQueryView -->
   <!--
   <listener>
diff --git a/server/apps/distributed-pop3-app/sample-configuration/listeners.xml b/server/apps/distributed-pop3-app/sample-configuration/listeners.xml
index b8e256b794..94eba430d4 100644
--- a/server/apps/distributed-pop3-app/sample-configuration/listeners.xml
+++ b/server/apps/distributed-pop3-app/sample-configuration/listeners.xml
@@ -25,12 +25,6 @@
   <listener>
     <class>org.apache.james.mailbox.cassandra.MailboxOperationLoggingListener</class>
   </listener>
-  <!-- Requires Deleted Message Vault to be activated. See deletedMessageVault.properties -->
-  <!--
-  <preDeletionHook>
-    <class>org.apache.james.vault.DeletedMessageVaultHook</class>
-  </preDeletionHook>
-  -->
 
   <!-- Enable to populate JMAP EmailQueryView -->
   <!--
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraDeletedMessageVaultModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraDeletedMessageVaultModule.java
index 5063a6f237..f8befa6e8f 100644
--- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraDeletedMessageVaultModule.java
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraDeletedMessageVaultModule.java
@@ -20,6 +20,7 @@
 package org.apache.james.modules.mailbox;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.mailbox.cassandra.DeleteMessageListener;
 import org.apache.james.modules.vault.DeletedMessageVaultModule;
 import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.blob.BlobStoreDeletedMessageVault;
@@ -28,6 +29,7 @@ import org.apache.james.vault.dto.DeletedMessageWithStorageInformationConverter;
 import org.apache.james.vault.metadata.CassandraDeletedMessageMetadataVault;
 import org.apache.james.vault.metadata.DeletedMessageMetadataModule;
 import org.apache.james.vault.metadata.DeletedMessageMetadataVault;
+import org.apache.james.vault.metadata.DeletedMessageVaultDeletionCallback;
 import org.apache.james.vault.metadata.MetadataDAO;
 import org.apache.james.vault.metadata.StorageInformationDAO;
 import org.apache.james.vault.metadata.UserPerBucketDAO;
@@ -60,5 +62,9 @@ public class CassandraDeletedMessageVaultModule extends AbstractModule {
         bind(BlobStoreDeletedMessageVault.class).in(Scopes.SINGLETON);
         bind(DeletedMessageVault.class)
             .to(BlobStoreDeletedMessageVault.class);
+
+        Multibinder.newSetBinder(binder(), DeleteMessageListener.DeletionCallback.class)
+            .addBinding()
+            .to(DeletedMessageVaultDeletionCallback.class);
     }
 }
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 72397e9793..00d6ddb446 100644
--- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -229,6 +229,7 @@ public class CassandraMailboxModule extends AbstractModule {
             .addBinding().to(MailboxAnnotationListener.class);
         Multibinder.newSetBinder(binder(), EventListener.ReactiveGroupEventListener.class)
             .addBinding().to(DeleteMessageListener.class);
+        Multibinder.newSetBinder(binder(), DeleteMessageListener.DeletionCallback.class);
 
         bind(MailboxManager.class).annotatedWith(Names.named(MAILBOXMANAGER_NAME)).to(MailboxManager.class);
 
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
index ae708fb09c..78b8216247 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
@@ -32,7 +32,6 @@ import org.apache.james.modules.AwsS3BlobStoreExtension;
 import org.apache.james.modules.RabbitMQExtension;
 import org.apache.james.modules.TestJMAPServerModule;
 import org.apache.james.modules.blobstore.BlobStoreConfiguration;
-import org.apache.james.modules.vault.TestDeleteMessageVaultPreDeletionHookModule;
 import org.apache.james.vault.VaultConfiguration;
 import org.apache.james.webadmin.integration.vault.DeletedMessageVaultIntegrationTest;
 import org.junit.jupiter.api.Disabled;
@@ -63,8 +62,7 @@ class RabbitMQDeletedMessageVaultIntegrationTest extends DeletedMessageVaultInte
         .extension(new RabbitMQExtension())
         .extension(new ClockExtension())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
-            .overrideWith(new TestJMAPServerModule())
-            .overrideWith(new TestDeleteMessageVaultPreDeletionHookModule()))
+            .overrideWith(new TestJMAPServerModule()))
         .build();
 
     @Override
@@ -79,4 +77,5 @@ class RabbitMQDeletedMessageVaultIntegrationTest extends DeletedMessageVaultInte
     public void vaultExportShouldExportZipContainsVaultMessagesToShareeWhenImapDeletedMailbox(GuiceJamesServer jmapServer) {
 
     }
+
 }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
index 83b3652011..d1cc97fbbb 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
@@ -60,7 +60,6 @@ class RabbitMQLinshareBlobExportMechanismIntegrationTest extends LinshareBlobExp
         .extension(new LinshareGuiceExtension())
         .server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
             .overrideWith(new TestJMAPServerModule())
-            .overrideWith(new TestRabbitMQModule(DockerRabbitMQSingleton.SINGLETON))
-            .overrideWith(new TestDeleteMessageVaultPreDeletionHookModule()))
+            .overrideWith(new TestRabbitMQModule(DockerRabbitMQSingleton.SINGLETON)))
         .build();
 }
diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/DeletedMessageVaultIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/DeletedMessageVaultIntegrationTest.java
index b7f899510c..466fb8e05b 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/DeletedMessageVaultIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/vault/DeletedMessageVaultIntegrationTest.java
@@ -253,6 +253,7 @@ public abstract class DeletedMessageVaultIntegrationTest {
         testIMAPClient.delete(MAILBOX_NAME);
 
         WAIT_TWO_MINUTES.untilAsserted(() -> assertThat(listMessageIdsForAccount(homerAccessToken)).hasSize(0));
+        Thread.sleep(1000); // Wait for messages to be moved to the vault
 
         restoreAllMessagesOfHomer();
         WAIT_TWO_MINUTES.untilAsserted(() -> assertThat(listMessageIdsForAccount(homerAccessToken)).hasSize(1));
@@ -929,6 +930,12 @@ public abstract class DeletedMessageVaultIntegrationTest {
 
     private void homerDeletesMessages(List<String> idsToDestroy) {
         deleteMessages(homerAccessToken, idsToDestroy);
+        // Grace period for the vault
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     private void bartDeletesMessages(List<String> idsToDestroy) {
diff --git a/src/homepage/howTo/deleted-messages-vault.html b/src/homepage/howTo/deleted-messages-vault.html
index f122c4ac73..f51ccb2a4d 100644
--- a/src/homepage/howTo/deleted-messages-vault.html
+++ b/src/homepage/howTo/deleted-messages-vault.html
@@ -132,6 +132,7 @@ layout: howTo
                     Before deleting a mail in James, PreDeletionHooks will be triggered to execute all declared hooks. If all hook executions success, then James will process to delete that mail.
                     There is already a DeletedMessageVaultHook in James, its job is to store deleted mails into Deleted Messages Vault. Thus, you need to configure this hook in listeners.xml configuration file.
                 </p>
+                <p><b>NOTE:</b> From James 3.8.0 onward the DeletedMessageVaultHook should no longer be specified for Cassandra based products.</p>
 
                 <p>
                     Sample DeletedMessageVaultHook configuration:
diff --git a/src/site/xdoc/server/config-listeners.xml b/src/site/xdoc/server/config-listeners.xml
index b8a018d650..f00736b03f 100644
--- a/src/site/xdoc/server/config-listeners.xml
+++ b/src/site/xdoc/server/config-listeners.xml
@@ -88,6 +88,7 @@
                     Storing messages about being deleted into <code>org.apache.james.vault.DeletedMessageVault</code>
                 </li>
             </ul>
+            <p><b>NOTE:</b> From James 3.8.0 onward the DeletedMessageVaultHook should no longer be specified for Cassandra based products.</p>
         </section>
 
         <section name="Add custom Guice injections for extensions">
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index 2e464ac34d..adfc5bf7fa 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -26,6 +26,18 @@ Change list:
 - [Adding saveDate column to messageIdTable and imapUidTable](#adding-savedate-column-to-messageidtable-and-imapuidtable)
 - [Adding the saveDate to the OpenSearch index](#adding-the-savedate-to-the-opensearch-index)
 - [Adding delegatedUser column to user_table](#adding-delegatedusers-column-to-user-table)
+- [DeletedMessageVaultHook should not be used on Cassandra based products](#deleted-message-vault-is-now-deactivated-by-default)
+
+### DeletedMessageVaultHook should not be used on Cassandra based products
+
+Date: 10/02/2023
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3882
+
+Concerned products: Distributed James, Cassandra James Server
+
+When the deleted message vault is enabled in Cassandra products, now the deleted messages are directly copied
+into the vault, asynchronously upon deletions. Configuring DeletedMessageVaultHook is thus no longer needed.
 
 ### Adding delegated_users column to user table
 


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