You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2019/07/09 07:25:06 UTC

[james-project] branch master updated (6a87529 -> 225103b)

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 6a87529  Merge branch 'pr-2507'
     new f5644bd  JAMES-2809 Implement a BlobStore based version of the DeletedMessageVault
     new f35e241  JAMES-2809 Remove DeletedMessageVault::userWithVault
     new 225103b  JAMES-2809 DeletedMessageVault contract did not include search test filtering

The 3 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:
 mailbox/plugin/deleted-messages-vault/pom.xml      |   5 +
 .../apache/james/vault/DeletedMessageVault.java    |   2 -
 .../vault/blob/BlobStoreDeletedMessageVault.java   |  94 +++++++++++++++
 .../james/vault/blob/BucketNameGenerator.java      |  23 ++--
 .../vault/memory/MemoryDeletedMessagesVault.java   |  13 ++-
 .../james/vault/utils/DeleteByQueryExecutor.java   |   9 +-
 .../james/vault/DeletedMessageVaultContract.java   |  45 +++++---
 .../blob/BlobStoreDeletedMessageVaultTest.java     | 128 +++++++++++++++++++++
 .../james/vault/blob/BucketNameGeneratorTest.java  |  35 +++---
 .../vault/utils/DeleteByQueryExecutorTest.java     |   6 +-
 .../vault/MailRepositoryDeletedMessageVault.java   |  12 +-
 .../routes/DeletedMessagesVaultRoutesTest.java     |   4 +-
 12 files changed, 316 insertions(+), 60 deletions(-)
 create mode 100644 mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
 copy backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/utils/LightweightTransactionException.java => mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BucketNameGenerator.java (71%)
 create mode 100644 mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
 copy server/data/data-api/src/test/java/org/apache/james/rrt/lib/DomainRewriterTest.java => mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BucketNameGeneratorTest.java (59%)


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


[james-project] 03/03: JAMES-2809 DeletedMessageVault contract did not include search test filtering

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 225103b0b12242ef82825436e65ef32e4a5f951a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Jul 5 14:07:00 2019 +0200

    JAMES-2809 DeletedMessageVault contract did not include search test filtering
---
 .../apache/james/vault/DeletedMessageVaultContract.java  | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
index ef872b0..52c8862 100644
--- a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
@@ -23,9 +23,11 @@ import static org.apache.james.vault.DeletedMessageFixture.CONTENT;
 import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE;
 import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE_2;
 import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE_GENERATOR;
+import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE_WITH_SUBJECT;
 import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID;
 import static org.apache.james.vault.DeletedMessageFixture.NOW;
 import static org.apache.james.vault.DeletedMessageFixture.OLD_DELETED_MESSAGE;
+import static org.apache.james.vault.DeletedMessageFixture.SUBJECT;
 import static org.apache.james.vault.DeletedMessageFixture.USER;
 import static org.apache.james.vault.DeletedMessageFixture.USER_2;
 import static org.apache.james.vault.search.Query.ALL;
@@ -39,6 +41,8 @@ import java.time.Duration;
 import org.apache.james.mailbox.inmemory.InMemoryMessageId;
 import org.apache.james.task.Task;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
+import org.apache.james.vault.search.CriterionFactory;
+import org.apache.james.vault.search.Query;
 import org.junit.jupiter.api.Test;
 
 import reactor.core.publisher.Flux;
@@ -127,6 +131,18 @@ public interface DeletedMessageVaultContract {
     }
 
     @Test
+    default void searchShouldReturnMatchingItems() {
+        Mono.from(getVault().append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT))).block();
+        Mono.from(getVault().append(USER, DELETED_MESSAGE_WITH_SUBJECT, new ByteArrayInputStream(CONTENT))).block();
+
+        assertThat(
+            Flux.from(getVault().search(USER,
+                Query.of(CriterionFactory.subject().containsIgnoreCase(SUBJECT))))
+                .collectList().block())
+            .containsOnly(DELETED_MESSAGE_WITH_SUBJECT);
+    }
+
+    @Test
     default void vaultShouldBePartitionnedByUser() {
         Mono.from(getVault().append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT))).block();
 


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


[james-project] 01/03: JAMES-2809 Implement a BlobStore based version of the DeletedMessageVault

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 f5644bd231ea1c83b2b1d2522951443803a775d3
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Jul 4 15:16:45 2019 +0200

    JAMES-2809 Implement a BlobStore based version of the DeletedMessageVault
    
    Not supported operations:
     - retention: will be implemented in JAMES-2811
     - delete: will be implemented later
     - userWithVault: this not generic enough operation will be refactored
     out of the contract
---
 mailbox/plugin/deleted-messages-vault/pom.xml      |   5 +
 .../vault/blob/BlobStoreDeletedMessageVault.java   |  99 +++++++++++++++
 .../james/vault/blob/BucketNameGenerator.java      |  40 ++++++
 .../blob/BlobStoreDeletedMessageVaultTest.java     | 134 +++++++++++++++++++++
 .../james/vault/blob/BucketNameGeneratorTest.java  |  48 ++++++++
 5 files changed, 326 insertions(+)

diff --git a/mailbox/plugin/deleted-messages-vault/pom.xml b/mailbox/plugin/deleted-messages-vault/pom.xml
index f7bff9c..8d973c0 100644
--- a/mailbox/plugin/deleted-messages-vault/pom.xml
+++ b/mailbox/plugin/deleted-messages-vault/pom.xml
@@ -82,6 +82,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>blob-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-core</artifactId>
         </dependency>
         <dependency>
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
new file mode 100644
index 0000000..4f5aa16
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
@@ -0,0 +1,99 @@
+/****************************************************************
+ * 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.blob;
+
+import java.io.InputStream;
+
+import org.apache.commons.lang3.NotImplementedException;
+import org.apache.james.blob.api.BlobStore;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.task.Task;
+import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageVault;
+import org.apache.james.vault.metadata.DeletedMessageMetadataVault;
+import org.apache.james.vault.metadata.DeletedMessageWithStorageInformation;
+import org.apache.james.vault.metadata.StorageInformation;
+import org.apache.james.vault.search.Query;
+import org.reactivestreams.Publisher;
+
+import com.google.common.base.Preconditions;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
+    private final DeletedMessageMetadataVault messageMetadataVault;
+    private final BlobStore blobStore;
+    private final BucketNameGenerator nameGenerator;
+
+    public BlobStoreDeletedMessageVault(DeletedMessageMetadataVault messageMetadataVault, BlobStore blobStore, BucketNameGenerator nameGenerator) {
+        this.messageMetadataVault = messageMetadataVault;
+        this.blobStore = blobStore;
+        this.nameGenerator = nameGenerator;
+    }
+
+    @Override
+    public Publisher<Void> append(User user, DeletedMessage deletedMessage, InputStream mimeMessage) {
+        Preconditions.checkNotNull(user);
+        Preconditions.checkNotNull(deletedMessage);
+        Preconditions.checkNotNull(mimeMessage);
+        BucketName bucketName = nameGenerator.currentBucket();
+        return blobStore.save(bucketName, mimeMessage)
+            .map(blobId -> new StorageInformation(bucketName, blobId))
+            .map(storageInformation -> new DeletedMessageWithStorageInformation(deletedMessage, storageInformation))
+            .flatMap(message -> Mono.from(messageMetadataVault.store(message)))
+            .then();
+    }
+
+    @Override
+    public Publisher<InputStream> loadMimeMessage(User user, MessageId messageId) {
+        Preconditions.checkNotNull(user);
+        Preconditions.checkNotNull(messageId);
+        return Mono.from(messageMetadataVault.retrieveStorageInformation(user, messageId))
+            .map(storageInformation -> blobStore.read(storageInformation.getBucketName(), storageInformation.getBlobId()));
+    }
+
+    @Override
+    public Publisher<DeletedMessage> search(User user, Query query) {
+        Preconditions.checkNotNull(user);
+        Preconditions.checkNotNull(query);
+        return Flux.from(messageMetadataVault.listRelatedBuckets())
+            .concatMap(bucketName -> Flux.from(messageMetadataVault.listMessages(bucketName, user)))
+            .map(DeletedMessageWithStorageInformation::getDeletedMessage)
+            .filter(query.toPredicate());
+    }
+
+    @Override
+    public Publisher<Void> delete(User user, MessageId messageId) {
+        throw new NotImplementedException("Will be implemented later");
+    }
+
+    @Override
+    public Publisher<User> usersWithVault() {
+        throw new NotImplementedException("Will be implemented later");
+    }
+
+    @Override
+    public Task deleteExpiredMessagesTask() {
+        throw new NotImplementedException("Will be implemented later");
+    }
+}
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BucketNameGenerator.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BucketNameGenerator.java
new file mode 100644
index 0000000..37931a2
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BucketNameGenerator.java
@@ -0,0 +1,40 @@
+/****************************************************************
+ * 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.blob;
+
+import java.time.Clock;
+import java.time.ZonedDateTime;
+
+import org.apache.james.blob.api.BucketName;
+
+public class BucketNameGenerator {
+    private final Clock clock;
+
+    public BucketNameGenerator(Clock clock) {
+        this.clock = clock;
+    }
+
+    public BucketName currentBucket() {
+        ZonedDateTime now = ZonedDateTime.now(clock);
+        int month = now.getMonthValue();
+        int year = now.getYear();
+        return BucketName.of(String.format("deletedMessages-%d-%02d-01", year, month));
+    }
+}
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
new file mode 100644
index 0000000..c4e18f9
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
@@ -0,0 +1,134 @@
+/****************************************************************
+ * 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.blob;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+import org.apache.james.blob.api.HashBlobId;
+import org.apache.james.blob.memory.MemoryBlobStore;
+import org.apache.james.vault.DeletedMessageVault;
+import org.apache.james.vault.DeletedMessageVaultContract;
+import org.apache.james.vault.memory.metadata.MemoryDeletedMessageMetadataVault;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+
+class BlobStoreDeletedMessageVaultTest implements DeletedMessageVaultContract {
+    private static final Instant NOW = Instant.parse("2007-12-03T10:15:30.00Z");
+    private static final Clock CLOCK = Clock.fixed(NOW, ZoneId.of("UTC"));
+
+    private BlobStoreDeletedMessageVault messageVault;
+
+    @BeforeEach
+    void setUp() {
+        messageVault = new BlobStoreDeletedMessageVault(new MemoryDeletedMessageMetadataVault(),
+            new MemoryBlobStore(new HashBlobId.Factory()),
+            new BucketNameGenerator(CLOCK));
+    }
+
+    @Override
+    public DeletedMessageVault getVault() {
+        return messageVault;
+    }
+
+
+    @Disabled("Will be implemented later")
+    public void deleteShouldThrowOnNullMessageId() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    public void deleteShouldThrowOnNullUser() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldCompleteWhenNoMail() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldCompleteWhenAllMailsDeleted() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldCompleteWhenOnlyRecentMails() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldDeleteOldMails() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldNotDeleteRecentMails() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldDoNothingWhenEmpty() {
+
+    }
+
+    @Disabled("Will be implemented in JAMES-2811")
+    @Override
+    public void deleteExpiredMessagesTaskShouldCompleteWhenOnlyOldMails() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    @Override
+    public void deleteShouldRunSuccessfullyInAConcurrentContext() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    @Override
+    public void usersWithVaultShouldReturnEmptyWhenNoItem() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    @Override
+    public void usersWithVaultShouldReturnAllUsers() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    @Override
+    public void searchAllShouldNotReturnDeletedItems() {
+
+    }
+
+    @Disabled("Will be implemented later")
+    @Override
+    public void loadMimeMessageShouldReturnEmptyWhenDeleted() {
+
+    }
+}
\ No newline at end of file
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BucketNameGeneratorTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BucketNameGeneratorTest.java
new file mode 100644
index 0000000..b2a2c6a
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BucketNameGeneratorTest.java
@@ -0,0 +1,48 @@
+/****************************************************************
+ * 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.blob;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+
+import org.apache.james.blob.api.BucketName;
+import org.junit.jupiter.api.Test;
+
+class BucketNameGeneratorTest {
+    private static final Instant NOW = Instant.parse("2007-12-03T10:15:30.00Z");
+    private static final Instant DATE_2 = Instant.parse("2007-07-03T10:15:30.00Z");
+    private static final Clock CLOCK = Clock.fixed(NOW, ZoneId.of("UTC"));
+    private static final Clock CLOCK_2 = Clock.fixed(DATE_2, ZoneId.of("UTC"));
+
+    @Test
+    void currentBucketShouldReturnBucketFormattedOnFirstDayOfMonth() {
+        assertThat(new BucketNameGenerator(CLOCK).currentBucket())
+            .isEqualTo(BucketName.of("deletedMessages-2007-12-01"));
+    }
+
+    @Test
+    void monthShouldBeFormattedWithTwoDigits() {
+        assertThat(new BucketNameGenerator(CLOCK_2).currentBucket())
+            .isEqualTo(BucketName.of("deletedMessages-2007-07-01"));
+    }
+}
\ No newline at end of file


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


[james-project] 02/03: JAMES-2809 Remove DeletedMessageVault::userWithVault

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 f35e24117360cde59859c2fef2f8276c77386f55
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Jul 4 15:26:34 2019 +0200

    JAMES-2809 Remove DeletedMessageVault::userWithVault
    
    Tis not generic enough operation is refactored  out of the contract (this
    was used internally to implement retention but is not required with the
    new DeletedMessageVault implementation)
---
 .../apache/james/vault/DeletedMessageVault.java    |  2 --
 .../vault/blob/BlobStoreDeletedMessageVault.java   |  5 ----
 .../vault/memory/MemoryDeletedMessagesVault.java   | 13 ++++++----
 .../james/vault/utils/DeleteByQueryExecutor.java   |  9 +++++--
 .../james/vault/DeletedMessageVaultContract.java   | 29 +++++++++++-----------
 .../blob/BlobStoreDeletedMessageVaultTest.java     | 12 +++------
 .../vault/utils/DeleteByQueryExecutorTest.java     |  6 ++---
 .../vault/MailRepositoryDeletedMessageVault.java   | 12 ++++-----
 .../routes/DeletedMessagesVaultRoutesTest.java     |  4 +--
 9 files changed, 43 insertions(+), 49 deletions(-)

diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVault.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVault.java
index 420c7cd..dcee403 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVault.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageVault.java
@@ -36,7 +36,5 @@ public interface DeletedMessageVault {
 
     Publisher<DeletedMessage> search(User user, Query query);
 
-    Publisher<User> usersWithVault();
-
     Task deleteExpiredMessagesTask();
 }
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
index 4f5aa16..ed56d3a 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVault.java
@@ -88,11 +88,6 @@ public class BlobStoreDeletedMessageVault implements DeletedMessageVault {
     }
 
     @Override
-    public Publisher<User> usersWithVault() {
-        throw new NotImplementedException("Will be implemented later");
-    }
-
-    @Override
     public Task deleteExpiredMessagesTask() {
         throw new NotImplementedException("Will be implemented later");
     }
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/memory/MemoryDeletedMessagesVault.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/memory/MemoryDeletedMessagesVault.java
index ecee56e..a887576 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/memory/MemoryDeletedMessagesVault.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/memory/MemoryDeletedMessagesVault.java
@@ -56,7 +56,7 @@ public class MemoryDeletedMessagesVault implements DeletedMessageVault {
     private DeleteByQueryExecutor deleteByQueryExecutor;
 
     public MemoryDeletedMessagesVault(RetentionConfiguration retentionConfiguration, Clock clock) {
-        this.deleteByQueryExecutor = new DeleteByQueryExecutor(this);
+        this.deleteByQueryExecutor = new DeleteByQueryExecutor(this, this::usersWithVault);
         this.retentionConfiguration = retentionConfiguration;
         this.clock = clock;
         this.table = HashBasedTable.create();
@@ -111,11 +111,14 @@ public class MemoryDeletedMessagesVault implements DeletedMessageVault {
             .filter(query.toPredicate());
     }
 
-    @Override
+    @VisibleForTesting
     public Publisher<User> usersWithVault() {
-        synchronized (table) {
-            return Flux.fromIterable(ImmutableList.copyOf(table.rowKeySet()));
-        }
+        return Flux.defer(
+            () -> {
+                synchronized (table) {
+                    return Flux.fromIterable(ImmutableList.copyOf(table.rowKeySet()));
+                }
+            });
     }
 
     public Task deleteExpiredMessagesTask() {
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/utils/DeleteByQueryExecutor.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/utils/DeleteByQueryExecutor.java
index 97fe2b1..6226474 100644
--- a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/utils/DeleteByQueryExecutor.java
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/utils/DeleteByQueryExecutor.java
@@ -19,12 +19,15 @@
 
 package org.apache.james.vault.utils;
 
+import java.util.function.Supplier;
+
 import org.apache.james.core.User;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.task.Task;
 import org.apache.james.util.FunctionalUtils;
 import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.search.Query;
+import org.reactivestreams.Publisher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,13 +57,15 @@ public class DeleteByQueryExecutor {
     private static final Logger LOGGER = LoggerFactory.getLogger(DeleteByQueryExecutor.class);
 
     private final DeletedMessageVault deletedMessageVault;
+    private final Supplier<Publisher<User>> userWithVaults;
 
-    public DeleteByQueryExecutor(DeletedMessageVault deletedMessageVault) {
+    public DeleteByQueryExecutor(DeletedMessageVault deletedMessageVault, Supplier<Publisher<User>> userWithVaults) {
         this.deletedMessageVault = deletedMessageVault;
+        this.userWithVaults = userWithVaults;
     }
 
     public Task.Result deleteByQuery(Query query, Notifiers notifiers) {
-        return Flux.from(deletedMessageVault.usersWithVault())
+        return Flux.from(userWithVaults.get())
             .flatMap(user -> deleteByQueryForUser(query, user, notifiers))
             .reduce(Task::combine)
             .onErrorResume(e -> {
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
index 2abaea5..ef872b0 100644
--- a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageVaultContract.java
@@ -170,21 +170,6 @@ public interface DeletedMessageVaultContract {
     }
 
     @Test
-    default void usersWithVaultShouldReturnEmptyWhenNoItem() {
-        assertThat(Flux.from(getVault().usersWithVault()).collectList().block())
-            .isEmpty();
-    }
-
-    @Test
-    default void usersWithVaultShouldReturnAllUsers() {
-        Mono.from(getVault().append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT))).block();
-        Mono.from(getVault().append(USER_2, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT))).block();
-
-        assertThat(Flux.from(getVault().usersWithVault()).collectList().block())
-            .containsOnly(USER, USER_2);
-    }
-
-    @Test
     default void appendShouldRunSuccessfullyInAConcurrentContext() throws Exception {
         int operationCount = 10;
         int threadCount = 10;
@@ -282,4 +267,18 @@ public interface DeletedMessageVaultContract {
         assertThat(Flux.from(getVault().search(USER, ALL)).collectList().block())
             .isEmpty();
     }
+
+    @Test
+    default void deleteExpiredMessagesTaskShouldDeleteOldMailsWhenRunSeveralTime() throws InterruptedException {
+        Mono.from(getVault().append(USER, OLD_DELETED_MESSAGE, new ByteArrayInputStream(CONTENT))).block();
+        getVault().deleteExpiredMessagesTask().run();
+
+        Mono.from(getVault().append(USER_2, OLD_DELETED_MESSAGE, new ByteArrayInputStream(CONTENT))).block();
+        getVault().deleteExpiredMessagesTask().run();
+
+        assertThat(Flux.from(getVault().search(USER, ALL)).collectList().block())
+            .isEmpty();
+        assertThat(Flux.from(getVault().search(USER_2, ALL)).collectList().block())
+            .isEmpty();
+    }
 }
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
index c4e18f9..04eae2d 100644
--- a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/blob/BlobStoreDeletedMessageVaultTest.java
@@ -110,25 +110,19 @@ class BlobStoreDeletedMessageVaultTest implements DeletedMessageVaultContract {
 
     @Disabled("Will be implemented later")
     @Override
-    public void usersWithVaultShouldReturnEmptyWhenNoItem() {
-
-    }
-
-    @Disabled("Will be implemented later")
-    @Override
-    public void usersWithVaultShouldReturnAllUsers() {
+    public void searchAllShouldNotReturnDeletedItems() {
 
     }
 
     @Disabled("Will be implemented later")
     @Override
-    public void searchAllShouldNotReturnDeletedItems() {
+    public void loadMimeMessageShouldReturnEmptyWhenDeleted() {
 
     }
 
     @Disabled("Will be implemented later")
     @Override
-    public void loadMimeMessageShouldReturnEmptyWhenDeleted() {
+    public void deleteExpiredMessagesTaskShouldDeleteOldMailsWhenRunSeveralTime() {
 
     }
 }
\ No newline at end of file
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/utils/DeleteByQueryExecutorTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/utils/DeleteByQueryExecutorTest.java
index 452772a..45c4af9 100644
--- a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/utils/DeleteByQueryExecutorTest.java
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/utils/DeleteByQueryExecutorTest.java
@@ -36,7 +36,6 @@ import static org.mockito.Mockito.when;
 import java.io.ByteArrayInputStream;
 
 import org.apache.james.task.Task;
-import org.apache.james.vault.DeletedMessageVault;
 import org.apache.james.vault.RetentionConfiguration;
 import org.apache.james.vault.memory.MemoryDeletedMessagesVault;
 import org.apache.james.vault.search.Query;
@@ -48,7 +47,7 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 class DeleteByQueryExecutorTest {
-    private DeletedMessageVault vault;
+    private MemoryDeletedMessagesVault vault;
     private DeleteByQueryExecutor testee;
     private DeleteByQueryExecutor.Notifiers notifiers;
     private DeleteByQueryExecutor.Notifier userHandledNotifier;
@@ -59,7 +58,7 @@ class DeleteByQueryExecutorTest {
     @BeforeEach
     void setUp() {
         vault = Mockito.spy(new MemoryDeletedMessagesVault(RetentionConfiguration.DEFAULT, CLOCK));
-        testee = new DeleteByQueryExecutor(vault);
+        testee = new DeleteByQueryExecutor(vault, vault::usersWithVault);
 
         userHandledNotifier = mock(DeleteByQueryExecutor.Notifier.class);
         searchErrorNotifier = mock(DeleteByQueryExecutor.Notifier.class);
@@ -75,6 +74,7 @@ class DeleteByQueryExecutorTest {
     @Test
     void deleteByQueryShouldReturnPartialWhenListingUserFailed() {
         when(vault.usersWithVault()).thenReturn(Mono.error(new RuntimeException()));
+        testee = new DeleteByQueryExecutor(vault, vault::usersWithVault);
 
         assertThat(testee.deleteByQuery(Query.ALL, notifiers)).isEqualTo(Task.Result.PARTIAL);
     }
diff --git a/server/mailrepository/deleted-messages-vault-repository/src/main/java/org/apache/james/vault/MailRepositoryDeletedMessageVault.java b/server/mailrepository/deleted-messages-vault-repository/src/main/java/org/apache/james/vault/MailRepositoryDeletedMessageVault.java
index 912fddb..945a486 100644
--- a/server/mailrepository/deleted-messages-vault-repository/src/main/java/org/apache/james/vault/MailRepositoryDeletedMessageVault.java
+++ b/server/mailrepository/deleted-messages-vault-repository/src/main/java/org/apache/james/vault/MailRepositoryDeletedMessageVault.java
@@ -75,7 +75,7 @@ public class MailRepositoryDeletedMessageVault implements DeletedMessageVault {
     MailRepositoryDeletedMessageVault(MailRepositoryStore mailRepositoryStore, RetentionConfiguration retentionConfiguration, Configuration configuration, MailConverter mailConverter, Clock clock) {
         this.retentionConfiguration = retentionConfiguration;
         this.clock = clock;
-        this.deleteByQueryExecutor = new DeleteByQueryExecutor(this);
+        this.deleteByQueryExecutor = new DeleteByQueryExecutor(this, this::usersWithVault);
         this.mailRepositoryStore = mailRepositoryStore;
         this.configuration = configuration;
         this.mailConverter = mailConverter;
@@ -139,11 +139,11 @@ public class MailRepositoryDeletedMessageVault implements DeletedMessageVault {
         }
     }
 
-    @Override
-    public Publisher<User> usersWithVault() {
-        return Flux.fromStream(mailRepositoryStore.getUrls()
-            .filter(this::isVault)
-            .map(this::userForRepository));
+    private Publisher<User> usersWithVault() {
+        return Flux.defer(() ->
+            Flux.fromStream(mailRepositoryStore.getUrls()
+                .filter(this::isVault)
+                .map(this::userForRepository)));
     }
 
     private boolean isVault(MailRepositoryUrl url) {
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
index 0441abb..944595e 100644
--- a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
@@ -2022,7 +2022,7 @@ class DeletedMessagesVaultRoutesTest {
                 vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
                 vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
 
-                doReturn(new DeleteByQueryExecutor(vault)).when(vault).getDeleteByQueryExecutor();
+                doReturn(new DeleteByQueryExecutor(vault, vault::usersWithVault)).when(vault).getDeleteByQueryExecutor();
                 doReturn(Flux.error(new RuntimeException("mock exception")))
                     .when(vault)
                     .search(any(), any());
@@ -2057,7 +2057,7 @@ class DeletedMessagesVaultRoutesTest {
                 vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
                 vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
 
-                doReturn(new DeleteByQueryExecutor(vault)).when(vault).getDeleteByQueryExecutor();
+                doReturn(new DeleteByQueryExecutor(vault, vault::usersWithVault)).when(vault).getDeleteByQueryExecutor();
                 doReturn(Mono.error(new RuntimeException("mock exception")))
                     .when(vault)
                     .delete(any(), any());


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