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/03/04 09:22:57 UTC

[james-project] branch master updated (20ca7e1 -> 961ee9b)

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 20ca7e1  MAILBOX-383 Manage dependencies between all startable components
     new d42533e  JAMES-2629 remove duplicate reactor dependency
     new 9a434fe  JAMES-2663 Vault restore all user message routes
     new 0c02cc5  MAILBOX-381 Improve MemoryMailRepositoryStore code style
     new 961ee9b  MAILBOX-381 MemoryMailRepositoryStore configuration should be final

The 4 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:
 backends-common/cassandra/pom.xml                  |   4 -
 .../vault/memory/MemoryDeletedMessagesVault.java   |   2 +-
 .../modules/server/MailStoreRepositoryModule.java  |  18 +-
 .../memory/MemoryMailRepositoryStore.java          |  84 ++---
 .../memory/MemoryMailRepositoryStoreTest.java      |  20 +-
 .../mailets/ToSenderDomainRepositoryTest.java      |   7 +-
 .../MailRepositoryDeletedMessageVaultTest.java     |  13 +-
 .../apache/james/smtpserver/SMTPServerTest.java    |   7 +-
 server/protocols/webadmin/pom.xml                  |   1 +
 .../pom.xml                                        |  57 +--
 .../routes/DeletedMessagesVaultRestoreTask.java    | 123 +++++++
 .../vault/routes/DeletedMessagesVaultRoutes.java}  |  90 ++---
 .../webadmin/vault/routes/RestoreService.java      | 107 ++++++
 .../routes/DeletedMessagesVaultRoutesTest.java     | 387 +++++++++++++++++++++
 .../routes/MailRepositoriesRoutesTest.java         |   6 +-
 .../webadmin/service/ReprocessingServiceTest.java  |   7 +-
 src/site/markdown/server/manage-webadmin.md        |  45 +++
 17 files changed, 816 insertions(+), 162 deletions(-)
 copy server/protocols/webadmin/{webadmin-mailrepository => webadmin-mailbox-deleted-message-vault}/pom.xml (80%)
 create mode 100644 server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRestoreTask.java
 copy server/protocols/webadmin/{webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MessageIdReindexingRoutes.java => webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java} (56%)
 create mode 100644 server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
 create mode 100644 server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java


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


[james-project] 04/04: MAILBOX-381 MemoryMailRepositoryStore configuration should be final

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 961ee9b70d905bc54d53710fcaf1fd566a046353
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 28 11:44:02 2019 +0700

    MAILBOX-381 MemoryMailRepositoryStore configuration should be final
    
    Also the POJO should be injected to allow easy overloads
---
 .../modules/server/MailStoreRepositoryModule.java    | 18 ++++++++++++------
 .../memory/MemoryMailRepositoryStore.java            | 17 +++++------------
 .../memory/MemoryMailRepositoryStoreTest.java        | 20 +++++++++++++-------
 .../mailets/ToSenderDomainRepositoryTest.java        |  7 ++++---
 .../vault/MailRepositoryDeletedMessageVaultTest.java | 13 ++++++++-----
 .../org/apache/james/smtpserver/SMTPServerTest.java  |  7 +++++--
 .../webadmin/routes/MailRepositoriesRoutesTest.java  |  6 ++++--
 .../webadmin/service/ReprocessingServiceTest.java    |  7 ++++---
 8 files changed, 55 insertions(+), 40 deletions(-)

diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
index d48c619..66bc204 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/modules/server/MailStoreRepositoryModule.java
@@ -21,10 +21,13 @@ package org.apache.james.modules.server;
 
 import java.util.List;
 
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.lifecycle.api.Startable;
 import org.apache.james.mailrepository.api.MailRepositoryProvider;
 import org.apache.james.mailrepository.api.MailRepositoryStore;
 import org.apache.james.mailrepository.file.FileMailRepositoryProvider;
+import org.apache.james.mailrepository.memory.MailRepositoryStoreConfiguration;
 import org.apache.james.mailrepository.memory.MemoryMailRepositoryStore;
 import org.apache.james.server.core.configuration.ConfigurationProvider;
 import org.apache.james.utils.ConfigurationPerformer;
@@ -34,6 +37,7 @@ import org.apache.james.utils.MailRepositoryProbeImpl;
 import com.google.common.collect.ImmutableList;
 import com.google.inject.AbstractModule;
 import com.google.inject.Inject;
+import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
@@ -51,23 +55,25 @@ public class MailStoreRepositoryModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), GuiceProbe.class).addBinding().to(MailRepositoryProbeImpl.class);
     }
 
+    @Provides
     @Singleton
-    public static class MailRepositoryStoreModuleConfigurationPerformer implements ConfigurationPerformer {
+    MailRepositoryStoreConfiguration provideConfiguration(ConfigurationProvider configurationProvider) throws ConfigurationException {
+        HierarchicalConfiguration configuration = configurationProvider.getConfiguration("mailrepositorystore");
+        return MailRepositoryStoreConfiguration.parse(configuration);
+    }
 
-        private final ConfigurationProvider configurationProvider;
+    @Singleton
+    public static class MailRepositoryStoreModuleConfigurationPerformer implements ConfigurationPerformer {
         private final MemoryMailRepositoryStore javaMailRepositoryStore;
 
         @Inject
-        public MailRepositoryStoreModuleConfigurationPerformer(ConfigurationProvider configurationProvider,
-                                                               MemoryMailRepositoryStore javaMailRepositoryStore) {
-            this.configurationProvider = configurationProvider;
+        public MailRepositoryStoreModuleConfigurationPerformer(MemoryMailRepositoryStore javaMailRepositoryStore) {
             this.javaMailRepositoryStore = javaMailRepositoryStore;
         }
 
         @Override
         public void initModule() {
             try {
-                javaMailRepositoryStore.configure(configurationProvider.getConfiguration("mailrepositorystore"));
                 javaMailRepositoryStore.init();
             } catch (Exception e) {
                 throw new RuntimeException(e);
diff --git a/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java b/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
index 63d8d97..80c77e8 100644
--- a/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
+++ b/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
@@ -34,6 +34,7 @@ import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.DefaultConfigurationBuilder;
 import org.apache.commons.configuration.HierarchicalConfiguration;
 import org.apache.james.lifecycle.api.Configurable;
+import org.apache.james.lifecycle.api.Startable;
 import org.apache.james.mailrepository.api.MailRepository;
 import org.apache.james.mailrepository.api.MailRepositoryPath;
 import org.apache.james.mailrepository.api.MailRepositoryProvider;
@@ -47,7 +48,7 @@ import org.slf4j.LoggerFactory;
 
 import com.github.fge.lambdas.Throwing;
 
-public class MemoryMailRepositoryStore implements MailRepositoryStore, Configurable {
+public class MemoryMailRepositoryStore implements MailRepositoryStore, Startable {
     private static final Logger LOGGER = LoggerFactory.getLogger(MemoryMailRepositoryStore.class);
 
     private final MailRepositoryUrlStore urlStore;
@@ -55,26 +56,18 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
     private final ConcurrentMap<MailRepositoryUrl, MailRepository> destinationToRepositoryAssociations;
     private final Map<Protocol, MailRepositoryProvider> protocolToRepositoryProvider;
     private final Map<Protocol, HierarchicalConfiguration> perProtocolMailRepositoryDefaultConfiguration;
-    private MailRepositoryStoreConfiguration configuration;
+    private final MailRepositoryStoreConfiguration configuration;
 
     @Inject
-    public MemoryMailRepositoryStore(MailRepositoryUrlStore urlStore, Set<MailRepositoryProvider> mailRepositories) {
+    public MemoryMailRepositoryStore(MailRepositoryUrlStore urlStore, Set<MailRepositoryProvider> mailRepositories, MailRepositoryStoreConfiguration configuration) {
         this.urlStore = urlStore;
         this.mailRepositories = mailRepositories;
+        this.configuration = configuration;
         this.destinationToRepositoryAssociations = new ConcurrentHashMap<>();
         this.protocolToRepositoryProvider = new HashMap<>();
         this.perProtocolMailRepositoryDefaultConfiguration = new HashMap<>();
     }
 
-    @Override
-    public void configure(HierarchicalConfiguration configuration) {
-        configure(MailRepositoryStoreConfiguration.parse(configuration));
-    }
-
-    public void configure(MailRepositoryStoreConfiguration configuration) {
-        this.configuration = configuration;
-    }
-
     public void init() throws Exception {
         LOGGER.info("JamesMailStore init... {}", this);
 
diff --git a/server/data/data-memory/src/test/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStoreTest.java b/server/data/data-memory/src/test/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStoreTest.java
index f92c93e..13aa40b 100644
--- a/server/data/data-memory/src/test/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStoreTest.java
+++ b/server/data/data-memory/src/test/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStoreTest.java
@@ -61,11 +61,14 @@ public class MemoryMailRepositoryStoreTest {
             .build();
         fileSystem = new FileSystemImpl(configuration.directories());
         urlStore = new MemoryMailRepositoryUrlStore();
+
+        MailRepositoryStoreConfiguration storeConfiguration = MailRepositoryStoreConfiguration.parse(
+            new FileConfigurationProvider(fileSystem, configuration).getConfiguration("mailrepositorystore"));
+
         repositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(
                 new MemoryMailRepositoryProvider(),
-                new MemoryMailRepositoryProvider()));
-        repositoryStore.configure(new FileConfigurationProvider(fileSystem, configuration)
-            .getConfiguration("mailrepositorystore"));
+                new MemoryMailRepositoryProvider()),
+            storeConfiguration);
         repositoryStore.init();
     }
 
@@ -94,9 +97,11 @@ public class MemoryMailRepositoryStoreTest {
 
     @Test
     public void configureShouldThrowWhenNonValidClassesAreProvided() throws Exception {
+        MailRepositoryStoreConfiguration storeConfiguration = MailRepositoryStoreConfiguration.parse(
+            new FileConfigurationProvider(fileSystem, configuration).getConfiguration("fakemailrepositorystore"));
+
         repositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(
-            new MemoryMailRepositoryProvider()));
-        repositoryStore.configure(new FileConfigurationProvider(fileSystem, configuration).getConfiguration("fakemailrepositorystore"));
+            new MemoryMailRepositoryProvider()), storeConfiguration);
 
         assertThatThrownBy(() -> repositoryStore.init())
             .isInstanceOf(ConfigurationException.class);
@@ -104,9 +109,10 @@ public class MemoryMailRepositoryStoreTest {
 
     @Test
     public void configureShouldNotThrowOnEmptyConfiguration() throws Exception {
+        MailRepositoryStoreConfiguration configuration = MailRepositoryStoreConfiguration.parse(new HierarchicalConfiguration());
+
         repositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(
-            new MemoryMailRepositoryProvider()));
-        repositoryStore.configure(new HierarchicalConfiguration());
+            new MemoryMailRepositoryProvider()), configuration);
 
         repositoryStore.init();
     }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
index b630449..5f0cd05 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/ToSenderDomainRepositoryTest.java
@@ -69,12 +69,13 @@ class ToSenderDomainRepositoryTest {
 
     private void createMailRepositoryStore() throws Exception {
         MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
-        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()));
-        mailRepositoryStore.configure(new MailRepositoryStoreConfiguration(
+        MailRepositoryStoreConfiguration configuration = new MailRepositoryStoreConfiguration(
             ImmutableList.of(new MailRepositoryStoreConfiguration.Item(
                 ImmutableList.of(new Protocol("memory")),
                 MemoryMailRepository.class.getName(),
-                new HierarchicalConfiguration()))));
+                new HierarchicalConfiguration())));
+
+        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()), configuration);
         mailRepositoryStore.init();
     }
 
diff --git a/server/mailrepository/deleted-messages-vault-repository/src/test/java/org/apache/james/vault/MailRepositoryDeletedMessageVaultTest.java b/server/mailrepository/deleted-messages-vault-repository/src/test/java/org/apache/james/vault/MailRepositoryDeletedMessageVaultTest.java
index 1b83686..9220f45 100644
--- a/server/mailrepository/deleted-messages-vault-repository/src/test/java/org/apache/james/vault/MailRepositoryDeletedMessageVaultTest.java
+++ b/server/mailrepository/deleted-messages-vault-repository/src/test/java/org/apache/james/vault/MailRepositoryDeletedMessageVaultTest.java
@@ -40,14 +40,17 @@ public class MailRepositoryDeletedMessageVaultTest implements DeletedMessageVaul
 
     @BeforeEach
     void setUp() throws Exception {
-        MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
-        MemoryMailRepositoryStore mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()));
-
-        mailRepositoryStore.configure(new MailRepositoryStoreConfiguration(
+        MailRepositoryStoreConfiguration configuration = new MailRepositoryStoreConfiguration(
             ImmutableList.of(new MailRepositoryStoreConfiguration.Item(
                 ImmutableList.of(new Protocol("memory")),
                 MemoryMailRepository.class.getName(),
-                new HierarchicalConfiguration()))));
+                new HierarchicalConfiguration())));
+        
+        MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
+        MemoryMailRepositoryStore mailRepositoryStore = new MemoryMailRepositoryStore(urlStore,
+            Sets.newHashSet(new MemoryMailRepositoryProvider()),
+            configuration);
+
         mailRepositoryStore.init();
 
         testee = new MailRepositoryDeletedMessageVault(
diff --git a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
index 8281678..8c0e472 100644
--- a/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
+++ b/server/protocols/protocols-smtp/src/test/java/org/apache/james/smtpserver/SMTPServerTest.java
@@ -203,11 +203,14 @@ public class SMTPServerTest {
                 .build();
         fileSystem = new FileSystemImpl(configuration.directories());
         MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
-        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()));        mailRepositoryStore.configure(new MailRepositoryStoreConfiguration(
+
+        MailRepositoryStoreConfiguration configuration = new MailRepositoryStoreConfiguration(
             ImmutableList.of(new MailRepositoryStoreConfiguration.Item(
                 ImmutableList.of(new Protocol("memory")),
                 MemoryMailRepository.class.getName(),
-                new HierarchicalConfiguration()))));
+                new HierarchicalConfiguration())));
+
+        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()), configuration);
         mailRepositoryStore.init();
     }
 
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
index cb6555f..2c20f27 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/routes/MailRepositoriesRoutesTest.java
@@ -1754,7 +1754,7 @@ public class MailRepositoriesRoutesTest {
 
     private void createMailRepositoryStore() throws Exception {
         MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
-        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()));        mailRepositoryStore.configure(new MailRepositoryStoreConfiguration(
+        MailRepositoryStoreConfiguration configuration = new MailRepositoryStoreConfiguration(
             ImmutableList.of(new MailRepositoryStoreConfiguration.Item(
                     ImmutableList.of(new Protocol("memory")),
                     MemoryMailRepository.class.getName(),
@@ -1762,7 +1762,9 @@ public class MailRepositoriesRoutesTest {
                 new MailRepositoryStoreConfiguration.Item(
                     ImmutableList.of(new Protocol("other")),
                     MemoryMailRepository.class.getName(),
-                    new HierarchicalConfiguration()))));
+                    new HierarchicalConfiguration())));
+        mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()), configuration);
+
         mailRepositoryStore.init();
     }
 }
diff --git a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
index 5ca274a..4ffbe90 100644
--- a/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
+++ b/server/protocols/webadmin/webadmin-mailrepository/src/test/java/org/apache/james/webadmin/service/ReprocessingServiceTest.java
@@ -179,12 +179,13 @@ public class ReprocessingServiceTest {
 
     private MemoryMailRepositoryStore createMemoryMailRepositoryStore() throws Exception {
         MemoryMailRepositoryUrlStore urlStore = new MemoryMailRepositoryUrlStore();
-        MemoryMailRepositoryStore mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()));
-        mailRepositoryStore.configure(new MailRepositoryStoreConfiguration(
+        MailRepositoryStoreConfiguration configuration = new MailRepositoryStoreConfiguration(
             ImmutableList.of(new MailRepositoryStoreConfiguration.Item(
                 ImmutableList.of(new Protocol("memory")),
                 MemoryMailRepository.class.getName(),
-                new HierarchicalConfiguration()))));
+                new HierarchicalConfiguration())));
+
+        MemoryMailRepositoryStore mailRepositoryStore = new MemoryMailRepositoryStore(urlStore, Sets.newHashSet(new MemoryMailRepositoryProvider()), configuration);
         mailRepositoryStore.init();
         return mailRepositoryStore;
     }


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


[james-project] 03/04: MAILBOX-381 Improve MemoryMailRepositoryStore code style

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 0c02cc50b70b6ba517868810ec73fd80135d64f5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Feb 28 11:30:54 2019 +0700

    MAILBOX-381 Improve MemoryMailRepositoryStore code style
    
     - Use Map::computeIfAbsent
     - Use more functional programming style
     - Avoid variable re-allocation
     - Group public methods together
---
 .../memory/MemoryMailRepositoryStore.java          | 69 ++++++++++------------
 1 file changed, 31 insertions(+), 38 deletions(-)

diff --git a/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java b/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
index a7b39bd..63d8d97 100644
--- a/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
+++ b/server/data/data-memory/src/main/java/org/apache/james/mailrepository/memory/MemoryMailRepositoryStore.java
@@ -67,13 +67,8 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
     }
 
     @Override
-    public Stream<MailRepositoryUrl> getUrls() {
-        return urlStore.listDistinct();
-    }
-
-    @Override
     public void configure(HierarchicalConfiguration configuration) {
-        this.configuration = MailRepositoryStoreConfiguration.parse(configuration);
+        configure(MailRepositoryStoreConfiguration.parse(configuration));
     }
 
     public void configure(MailRepositoryStoreConfiguration configuration) {
@@ -88,13 +83,30 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
         }
     }
 
+    private void initEntry(MailRepositoryStoreConfiguration.Item item) throws ConfigurationException {
+        String className = item.getClassFqdn();
+
+        MailRepositoryProvider usedMailRepository = mailRepositories.stream()
+            .filter(mailRepositoryProvider -> mailRepositoryProvider.canonicalName().equals(className))
+            .findAny()
+            .orElseThrow(() -> new ConfigurationException("MailRepository " + className + " has not been registered"));
+
+        for (Protocol protocol : item.getProtocols()) {
+            protocolToRepositoryProvider.put(protocol, usedMailRepository);
+            perProtocolMailRepositoryDefaultConfiguration.put(protocol, item.getConfiguration());
+        }
+    }
+
+    @Override
+    public Stream<MailRepositoryUrl> getUrls() {
+        return urlStore.listDistinct();
+    }
 
     @Override
     public Optional<MailRepository> get(MailRepositoryUrl url) {
-        if (urlStore.contains(url)) {
-            return Optional.of(select(url));
-        }
-        return Optional.empty();
+        return Optional.of(url)
+            .filter(urlStore::contains)
+            .map(this::select);
     }
 
     @Override
@@ -106,48 +118,31 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
 
     @Override
     public MailRepository select(MailRepositoryUrl mailRepositoryUrl) {
-        return Optional.ofNullable(destinationToRepositoryAssociations.get(mailRepositoryUrl))
-            .orElseGet(Throwing.supplier(
-                () -> createNewMailRepository(mailRepositoryUrl))
-                .sneakyThrow());
+        return destinationToRepositoryAssociations.computeIfAbsent(mailRepositoryUrl,
+            Throwing.function(this::createNewMailRepository).sneakyThrow());
     }
 
     private MailRepository createNewMailRepository(MailRepositoryUrl mailRepositoryUrl) throws MailRepositoryStoreException {
         MailRepository newMailRepository = retrieveMailRepository(mailRepositoryUrl);
+        initializeNewRepository(newMailRepository, createRepositoryCombinedConfig(mailRepositoryUrl));
         urlStore.add(mailRepositoryUrl);
-        newMailRepository = initializeNewRepository(newMailRepository, createRepositoryCombinedConfig(mailRepositoryUrl));
-        MailRepository previousRepository = destinationToRepositoryAssociations.putIfAbsent(mailRepositoryUrl, newMailRepository);
-        return Optional.ofNullable(previousRepository)
-            .orElse(newMailRepository);
-    }
 
-    private void initEntry(MailRepositoryStoreConfiguration.Item item) throws ConfigurationException {
-        String className = item.getClassFqdn();
-
-        MailRepositoryProvider usedMailRepository = mailRepositories.stream()
-            .filter(mailRepositoryProvider -> mailRepositoryProvider.canonicalName().equals(className))
-            .findAny()
-            .orElseThrow(() -> new ConfigurationException("MailRepository " + className + " has not been registered"));
-
-        for (Protocol protocol : item.getProtocols()) {
-            protocolToRepositoryProvider.put(protocol, usedMailRepository);
-            perProtocolMailRepositoryDefaultConfiguration.put(protocol, item.getConfiguration());
-        }
+        return newMailRepository;
     }
 
     private CombinedConfiguration createRepositoryCombinedConfig(MailRepositoryUrl mailRepositoryUrl) {
         CombinedConfiguration config = new CombinedConfiguration();
-        HierarchicalConfiguration defaultProtocolConfig = perProtocolMailRepositoryDefaultConfiguration.get(mailRepositoryUrl.getProtocol());
-        if (defaultProtocolConfig != null) {
-            config.addConfiguration(defaultProtocolConfig);
-        }
+
+        Optional.ofNullable(perProtocolMailRepositoryDefaultConfiguration.get(mailRepositoryUrl.getProtocol()))
+            .ifPresent(config::addConfiguration);
+
         DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
         builder.addProperty("[@destinationURL]", mailRepositoryUrl.asString());
         config.addConfiguration(builder);
         return config;
     }
 
-    private MailRepository initializeNewRepository(MailRepository mailRepository, CombinedConfiguration config) throws MailRepositoryStoreException {
+    private void initializeNewRepository(MailRepository mailRepository, CombinedConfiguration config) throws MailRepositoryStoreException {
         try {
             if (mailRepository instanceof Configurable) {
                 ((Configurable) mailRepository).configure(config);
@@ -155,7 +150,6 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
             if (mailRepository instanceof Initializable) {
                 ((Initializable) mailRepository).init();
             }
-            return mailRepository;
         } catch (Exception e) {
             throw new MailRepositoryStoreException("Cannot init mail repository", e);
         }
@@ -167,5 +161,4 @@ public class MemoryMailRepositoryStore implements MailRepositoryStore, Configura
             .orElseThrow(() -> new MailRepositoryStoreException("No Mail Repository associated with " + protocol.getValue()))
             .provide(mailRepositoryUrl);
     }
-
 }


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


[james-project] 01/04: JAMES-2629 remove duplicate reactor dependency

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 d42533e88c7b34e40fe37f63be7d8ec8ea481ed3
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Tue Feb 26 15:18:37 2019 +0700

    JAMES-2629 remove duplicate reactor dependency
---
 backends-common/cassandra/pom.xml | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/backends-common/cassandra/pom.xml b/backends-common/cassandra/pom.xml
index 90e8b0d..ddd1c78 100644
--- a/backends-common/cassandra/pom.xml
+++ b/backends-common/cassandra/pom.xml
@@ -85,10 +85,6 @@
             <artifactId>reactor-core</artifactId>
         </dependency>
         <dependency>
-            <groupId>io.projectreactor</groupId>
-            <artifactId>reactor-core</artifactId>
-        </dependency>
-        <dependency>
             <groupId>javax.inject</groupId>
             <artifactId>javax.inject</artifactId>
         </dependency>


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


[james-project] 02/04: JAMES-2663 Vault restore all user message routes

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 9a434fe2e12e929d86176038136c4a7d3e490034
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Tue Feb 26 14:32:33 2019 +0700

    JAMES-2663 Vault restore all user message routes
---
 .../vault/memory/MemoryDeletedMessagesVault.java   |   2 +-
 server/protocols/webadmin/pom.xml                  |   1 +
 .../webadmin-mailbox-deleted-message-vault/pom.xml | 177 ++++++++++
 .../routes/DeletedMessagesVaultRestoreTask.java    | 123 +++++++
 .../vault/routes/DeletedMessagesVaultRoutes.java   | 116 ++++++
 .../webadmin/vault/routes/RestoreService.java      | 107 ++++++
 .../routes/DeletedMessagesVaultRoutesTest.java     | 387 +++++++++++++++++++++
 src/site/markdown/server/manage-webadmin.md        |  45 +++
 8 files changed, 957 insertions(+), 1 deletion(-)

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 e23d87a..7e9faa3 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
@@ -43,7 +43,7 @@ import reactor.core.publisher.Mono;
 public class MemoryDeletedMessagesVault implements DeletedMessageVault {
     private final Table<User, MessageId, Pair<DeletedMessage, byte[]>> table;
 
-    MemoryDeletedMessagesVault() {
+    public MemoryDeletedMessagesVault() {
         table = HashBasedTable.create();
     }
 
diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/pom.xml
index 38d0fe3..65c60f9 100644
--- a/server/protocols/webadmin/pom.xml
+++ b/server/protocols/webadmin/pom.xml
@@ -38,6 +38,7 @@
         <module>webadmin-core</module>
         <module>webadmin-data</module>
         <module>webadmin-mailbox</module>
+        <module>webadmin-mailbox-deleted-message-vault</module>
         <module>webadmin-mailqueue</module>
         <module>webadmin-mailrepository</module>
         <module>webadmin-swagger</module>
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml
new file mode 100644
index 0000000..aa6847d
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/pom.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>james-server</artifactId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>james-server-webadmin-mailbox-deleted-message-vault</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Apache James :: Server :: Web Admin :: Mailbox :: Deleted Message Vault</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-deleted-messages-vault</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>apache-mailet-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-core</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>metrics-logger</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.platform</groupId>
+            <artifactId>junit-platform-launcher</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>java-hamcrest</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.github.kongchen</groupId>
+                <artifactId>swagger-maven-plugin</artifactId>
+                <configuration>
+                    <apiSources>
+                        <apiSource>
+                            <springmvc>false</springmvc>
+                            <locations>org.apache.james.webadmin</locations>
+                            <info>
+                                <title>Swagger Maven Plugin</title>
+                                <version>v1</version>
+                            </info>
+                            <swaggerDirectory>${project.build.directory}</swaggerDirectory>
+                            <swaggerFileName>webadmin-mailbox-deleted-message-vault</swaggerFileName>
+                        </apiSource>
+                    </apiSources>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>generate</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <reuseForks>true</reuseForks>
+                    <forkCount>1C</forkCount>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRestoreTask.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRestoreTask.java
new file mode 100644
index 0000000..b6aa3e5
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRestoreTask.java
@@ -0,0 +1,123 @@
+/****************************************************************
+ * 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.webadmin.vault.routes;
+
+import static org.apache.james.webadmin.vault.routes.RestoreService.RestoreResult.RESTORE_SUCCEED;
+
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskExecutionDetails;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class DeletedMessagesVaultRestoreTask implements Task {
+
+    public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
+        private final User user;
+        private final AtomicLong successfulRestoreCount;
+        private final AtomicLong errorRestoreCount;
+
+        AdditionalInformation(User user) {
+            this.user = user;
+            this.successfulRestoreCount = new AtomicLong();
+            this.errorRestoreCount = new AtomicLong();
+        }
+
+        public long getSuccessfulRestoreCount() {
+            return successfulRestoreCount.get();
+        }
+
+        public long getErrorRestoreCount() {
+            return errorRestoreCount.get();
+        }
+
+        public String getUser() {
+            return user.asString();
+        }
+
+        void incrementSuccessfulRestoreCount() {
+            successfulRestoreCount.incrementAndGet();
+        }
+
+        void incrementErrorRestoreCount() {
+            errorRestoreCount.incrementAndGet();
+        }
+    }
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DeletedMessagesVaultRestoreTask.class);
+
+    static final String TYPE = "deletedMessages/restore";
+
+    private final User userToRestore;
+    private final RestoreService vaultRestore;
+    private final AdditionalInformation additionalInformation;
+
+    DeletedMessagesVaultRestoreTask(User userToRestore, RestoreService vaultRestore) {
+        this.userToRestore = userToRestore;
+        this.vaultRestore = vaultRestore;
+        this.additionalInformation = new AdditionalInformation(userToRestore);
+    }
+
+    @Override
+    public Result run() {
+        try {
+            return vaultRestore.restore(userToRestore).toStream()
+                .peek(this::updateInformation)
+                .map(this::restoreResultToTaskResult)
+                .reduce(Task::combine)
+                .orElse(Result.COMPLETED);
+        } catch (MailboxException e) {
+            LOGGER.error("Error happens while restoring user {}", userToRestore.asString(), e);
+            return Result.PARTIAL;
+        }
+    }
+
+    private Task.Result restoreResultToTaskResult(RestoreService.RestoreResult restoreResult) {
+        if (restoreResult.equals(RESTORE_SUCCEED)) {
+            return Result.COMPLETED;
+        }
+        return Result.PARTIAL;
+    }
+
+    private void updateInformation(RestoreService.RestoreResult restoreResult) {
+        switch (restoreResult) {
+            case RESTORE_FAILED:
+                additionalInformation.incrementErrorRestoreCount();
+                break;
+            case RESTORE_SUCCEED:
+                additionalInformation.incrementSuccessfulRestoreCount();
+                break;
+        }
+    }
+
+    @Override
+    public String type() {
+        return TYPE;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        return Optional.of(additionalInformation);
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
new file mode 100644
index 0000000..f47a331
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutes.java
@@ -0,0 +1,116 @@
+/****************************************************************
+ * 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.webadmin.vault.routes;
+
+import static org.apache.james.webadmin.Constants.SEPARATOR;
+
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+
+import org.apache.james.core.User;
+import org.apache.james.task.TaskId;
+import org.apache.james.task.TaskManager;
+import org.apache.james.webadmin.Constants;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.dto.TaskIdDto;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import spark.Request;
+import spark.Response;
+import spark.Service;
+
+@Api(tags = "Deleted Messages Vault")
+@Path(DeletedMessagesVaultRoutes.ROOT_PATH)
+@Produces(Constants.JSON_CONTENT_TYPE)
+public class DeletedMessagesVaultRoutes implements Routes {
+
+    static final String ROOT_PATH = "deletedMessages/user";
+    private static final String USER_PATH_PARAM = "user";
+    private static final String RESTORE_PATH = ROOT_PATH + SEPARATOR + ":" + USER_PATH_PARAM;
+
+    private final RestoreService vaultRestore;
+    private final JsonTransformer jsonTransformer;
+    private final TaskManager taskManager;
+
+    @VisibleForTesting
+    DeletedMessagesVaultRoutes(RestoreService vaultRestore, JsonTransformer jsonTransformer,
+                               TaskManager taskManager) {
+        this.vaultRestore = vaultRestore;
+        this.jsonTransformer = jsonTransformer;
+        this.taskManager = taskManager;
+    }
+
+    @Override
+    public String getBasePath() {
+        return ROOT_PATH;
+    }
+
+    @Override
+    public void define(Service service) {
+        service.post(RESTORE_PATH, this::restore, jsonTransformer);
+    }
+
+    @POST
+    @Path(ROOT_PATH)
+    @ApiOperation(value = "Restore deleted emails from a specified user to his new restore mailbox")
+    @ApiImplicitParams({
+        @ApiImplicitParam(
+            required = true,
+            name = "user",
+            paramType = "path parameter",
+            dataType = "String",
+            defaultValue = "none",
+            example = "user@james.org",
+            value = "Compulsory. Needs to be a valid username represent for an user had requested to restore deleted emails")
+    })
+    @ApiResponses(value = {
+        @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response = TaskIdDto.class),
+        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - user param is invalid"),
+        @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side.")
+    })
+    private TaskIdDto restore(Request request, Response response) {
+        User userToRestore = extractUser(request);
+        TaskId taskId = taskManager.submit(new DeletedMessagesVaultRestoreTask(userToRestore, vaultRestore));
+        return TaskIdDto.respond(response, taskId);
+    }
+
+    private User extractUser(Request request) {
+        try {
+            return User.fromUsername(request.params(USER_PATH_PARAM));
+        } catch (IllegalArgumentException e) {
+            throw ErrorResponder.builder()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                .message(e.getMessage())
+                .haltError();
+        }
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
new file mode 100644
index 0000000..392b1fa
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/main/java/org/apache/james/webadmin/vault/routes/RestoreService.java
@@ -0,0 +1,107 @@
+/****************************************************************
+ * 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.webadmin.vault.routes;
+
+import static org.apache.james.mailbox.MessageManager.AppendCommand;
+import static org.apache.james.webadmin.vault.routes.RestoreService.RestoreResult.RESTORE_FAILED;
+import static org.apache.james.webadmin.vault.routes.RestoreService.RestoreResult.RESTORE_SUCCEED;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.vault.DeletedMessage;
+import org.apache.james.vault.DeletedMessageVault;
+import org.apache.james.vault.search.Query;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+class RestoreService {
+
+    enum RestoreResult {
+        RESTORE_SUCCEED,
+        RESTORE_FAILED
+    }
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RestoreService.class);
+
+    static final String RESTORE_MAILBOX_NAME = "Restored-Messages";
+
+    private final DeletedMessageVault deletedMessageVault;
+    private final MailboxManager mailboxManager;
+
+    RestoreService(DeletedMessageVault deletedMessageVault, MailboxManager mailboxManager) {
+        this.deletedMessageVault = deletedMessageVault;
+        this.mailboxManager = mailboxManager;
+    }
+
+    Flux<RestoreResult> restore(User userToRestore) throws MailboxException {
+        MailboxSession session = mailboxManager.createSystemSession(userToRestore.asString());
+        MessageManager restoreMessageManager = restoreMailboxManager(session);
+
+        return Flux.from(deletedMessageVault.search(userToRestore, Query.ALL))
+            .flatMap(deletedMessage -> appendToMailbox(restoreMessageManager, deletedMessage, session));
+    }
+
+    private Mono<RestoreResult> appendToMailbox(MessageManager restoreMailboxManager, DeletedMessage deletedMessage, MailboxSession session) {
+        return appendCommand(deletedMessage)
+            .map(Throwing.<AppendCommand, ComposedMessageId>function(
+                appendCommand -> restoreMailboxManager.appendMessage(appendCommand, session)).sneakyThrow())
+            .map(any -> RESTORE_SUCCEED)
+            .onErrorResume(throwable -> {
+                LOGGER.error("append message {} to restore mailbox of user {} didn't success",
+                    deletedMessage.getMessageId().serialize(), deletedMessage.getOwner().asString(), throwable);
+                return Mono.just(RESTORE_FAILED);
+            });
+    }
+
+    private Mono<AppendCommand> appendCommand(DeletedMessage deletedMessage) {
+        return Mono.from(deletedMessageVault.loadMimeMessage(deletedMessage.getOwner(), deletedMessage.getMessageId()))
+            .map(messageContentStream -> AppendCommand.builder()
+                .build(messageContentStream));
+    }
+
+    private MessageManager restoreMailboxManager(MailboxSession session) throws MailboxException {
+        MailboxPath restoreMailbox = MailboxPath.forUser(session.getUser().asString(), RESTORE_MAILBOX_NAME);
+        try {
+            return mailboxManager.getMailbox(restoreMailbox, session);
+        } catch (MailboxNotFoundException e) {
+            LOGGER.debug("mailbox {} doesn't exist, create a new one", restoreMailbox);
+            return createRestoreMailbox(session, restoreMailbox);
+        }
+    }
+
+    private MessageManager createRestoreMailbox(MailboxSession session, MailboxPath restoreMailbox) throws MailboxException {
+        return mailboxManager.createMailbox(restoreMailbox, session)
+            .map(Throwing.<MailboxId, MessageManager>function(mailboxId -> mailboxManager.getMailbox(mailboxId, session)).sneakyThrow())
+            .orElseThrow(() -> new RuntimeException("createMailbox " + restoreMailbox.asString() + " returns an empty mailboxId"));
+    }
+
+}
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
new file mode 100644
index 0000000..7349d5a
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox-deleted-message-vault/src/test/java/org/apache/james/webadmin/vault/routes/DeletedMessagesVaultRoutesTest.java
@@ -0,0 +1,387 @@
+/****************************************************************
+ * 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.webadmin.vault.routes;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
+import static io.restassured.RestAssured.with;
+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.USER;
+import static org.apache.james.vault.DeletedMessageFixture.USER_2;
+import static org.apache.james.webadmin.WebAdminServer.NO_CONFIGURATION;
+import static org.apache.james.webadmin.vault.routes.RestoreService.RESTORE_MAILBOX_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.james.core.User;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.FetchGroupImpl;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.MessageResult;
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
+import org.apache.james.mailbox.model.SearchQuery;
+import org.apache.james.metrics.logger.DefaultMetricFactory;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.vault.memory.MemoryDeletedMessagesVault;
+import org.apache.james.vault.search.Query;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.routes.TasksRoutes;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import com.google.common.collect.ImmutableList;
+
+import io.restassured.RestAssured;
+import io.restassured.filter.log.LogDetail;
+import reactor.core.publisher.Flux;
+
+class DeletedMessagesVaultRoutesTest {
+
+    private WebAdminServer webAdminServer;
+    private MemoryDeletedMessagesVault vault;
+    private InMemoryMailboxManager mailboxManager;
+
+    @BeforeEach
+    void beforeEach() throws Exception {
+        vault = spy(new MemoryDeletedMessagesVault());
+        InMemoryIntegrationResources inMemoryIntegrationResources = new InMemoryIntegrationResources();
+        InMemoryIntegrationResources.Resources inMemoryResource = inMemoryIntegrationResources.createResources(new SimpleGroupMembershipResolver());
+        mailboxManager = spy(inMemoryResource.getMailboxManager());
+
+        MemoryTaskManager taskManager = new MemoryTaskManager();
+        JsonTransformer jsonTransformer = new JsonTransformer();
+
+        RestoreService vaultRestore = new RestoreService(vault, mailboxManager);
+        webAdminServer = WebAdminUtils.createWebAdminServer(
+            new DefaultMetricFactory(),
+            new TasksRoutes(taskManager, jsonTransformer),
+            new DeletedMessagesVaultRoutes(vaultRestore, jsonTransformer, taskManager));
+
+        webAdminServer.configure(NO_CONFIGURATION);
+        webAdminServer.await();
+        RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer)
+            .setBasePath(DeletedMessagesVaultRoutes.ROOT_PATH)
+            .log(LogDetail.METHOD)
+            .build();
+    }
+
+    @AfterEach
+    void afterEach() {
+        webAdminServer.destroy();
+    }
+
+    @Nested
+    class ValidationTest {
+
+        @Test
+        void restoreShouldReturnInvalidWhenUserIsInvalid() {
+            when()
+                .post("not@valid@user.com")
+            .then()
+                .statusCode(HttpStatus.BAD_REQUEST_400)
+                .body("statusCode", is(400))
+                .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+                .body("message", is("The username should not contain multiple domain delimiter."));
+        }
+
+        @Test
+        void postShouldReturnNotFoundWhenNoUserPathParameter() {
+            when()
+                .post()
+            .then()
+                .statusCode(HttpStatus.NOT_FOUND_404)
+                .body("statusCode", is(404))
+                .body("type", is(ErrorResponder.ErrorType.NOT_FOUND.getType()))
+                .body("message", is("POST /deletedMessages/user can not be found"));
+        }
+    }
+
+    @Nested
+    class FailingRestoreTest {
+
+        @Test
+        void restoreShouldProduceFailedTaskWhenTheVaultGetsError() {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            doThrow(new RuntimeException("mock exception"))
+                .when(vault)
+                .search(any(), any());
+
+            String taskId = with()
+                .post(USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("status", is("failed"))
+                .body("taskId", is(taskId))
+                .body("type", is(DeletedMessagesVaultRestoreTask.TYPE))
+                .body("additionalInformation.successfulRestoreCount", is(0))
+                .body("additionalInformation.errorRestoreCount", is(0))
+                .body("additionalInformation.user", is(USER.asString()))
+                .body("startedDate", is(notNullValue()))
+                .body("submitDate", is(notNullValue()));
+        }
+
+        @Test
+        void restoreShouldProduceFailedTaskWithErrorRestoreCountWhenMessageAppendGetsError() throws Exception {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            MessageManager mockMessageManager = Mockito.mock(MessageManager.class);
+            doReturn(mockMessageManager)
+                .when(mailboxManager)
+                .getMailbox(any(MailboxId.class), any(MailboxSession.class));
+
+            doThrow(new MailboxException("mock exception"))
+                .when(mockMessageManager)
+                .appendMessage(any(), any());
+
+            String taskId = with()
+                .post(USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("status", is("failed"))
+                .body("taskId", is(taskId))
+                .body("type", is(DeletedMessagesVaultRestoreTask.TYPE))
+                .body("additionalInformation.successfulRestoreCount", is(0))
+                .body("additionalInformation.errorRestoreCount", is(2))
+                .body("additionalInformation.user", is(USER.asString()))
+                .body("startedDate", is(notNullValue()))
+                .body("submitDate", is(notNullValue()));
+        }
+
+        @Test
+        void restoreShouldProduceFailedTaskWhenMailboxMangerGetsError() throws Exception {
+            vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+            vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+            doThrow(new RuntimeException("mock exception"))
+                .when(mailboxManager)
+                .createMailbox(any(MailboxPath.class), any(MailboxSession.class));
+
+            String taskId = with()
+                .post(USER.asString())
+                .jsonPath()
+                .get("taskId");
+
+            given()
+                .basePath(TasksRoutes.BASE)
+            .when()
+                .get(taskId + "/await")
+            .then()
+                .body("status", is("failed"))
+                .body("taskId", is(taskId))
+                .body("type", is(DeletedMessagesVaultRestoreTask.TYPE))
+                .body("additionalInformation.successfulRestoreCount", is(0))
+                .body("additionalInformation.errorRestoreCount", is(0))
+                .body("additionalInformation.user", is(USER.asString()))
+                .body("startedDate", is(notNullValue()))
+                .body("submitDate", is(notNullValue()));
+        }
+    }
+
+    @Test
+    void restoreShouldReturnATaskCreated() {
+        when()
+            .post(USER.asString())
+        .then()
+            .statusCode(HttpStatus.CREATED_201)
+            .body("taskId", notNullValue());
+    }
+
+    @Test
+    void restoreShouldProduceASuccessfulTaskWithAdditionalInformation() {
+        vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+        vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+        String taskId = with()
+            .post(USER.asString())
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is(DeletedMessagesVaultRestoreTask.TYPE))
+            .body("additionalInformation.successfulRestoreCount", is(2))
+            .body("additionalInformation.errorRestoreCount", is(0))
+            .body("additionalInformation.user", is(USER.asString()))
+            .body("startedDate", is(notNullValue()))
+            .body("submitDate", is(notNullValue()))
+            .body("completedDate", is(notNullValue()));
+    }
+
+    @Test
+    void restoreShouldKeepAllMessagesInTheVaultOfCorrespondingUser() {
+        vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+        vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+        String taskId = with()
+            .post(USER.asString())
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"));
+
+        assertThat(Flux.from(vault.search(USER, Query.ALL)).toStream())
+            .containsOnly(DELETED_MESSAGE, DELETED_MESSAGE_2);
+    }
+
+    @Test
+    void restoreShouldNotDeleteExistingMessagesInTheUserMailbox() throws Exception {
+        MailboxSession session = mailboxManager.createSystemSession(USER.asString());
+        MailboxPath restoreMailboxPath = MailboxPath.forUser(USER.asString(), RESTORE_MAILBOX_NAME);
+        mailboxManager.createMailbox(restoreMailboxPath, session);
+        MessageManager messageManager = mailboxManager.getMailbox(restoreMailboxPath, session);
+        messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(new ByteArrayInputStream(CONTENT)),
+            session);
+
+        vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+        vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+        String taskId = with()
+            .post(USER.asString())
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"));
+
+        assertThat(restoreMailboxMessages(USER))
+            .hasSize(3);
+    }
+
+    @Test
+    void restoreShouldAppendAllMessageFromVaultToRestoreMailboxOfCorrespondingUser() throws Exception {
+        vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+        vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+        String taskId = with()
+            .post(USER.asString())
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"));
+
+        assertThat(restoreMailboxMessages(USER))
+            .hasSize(2)
+            .anySatisfy(messageResult -> assertThat(fullContent(messageResult)).hasSameContentAs(new ByteArrayInputStream(CONTENT)))
+            .anySatisfy(messageResult -> assertThat(fullContent(messageResult)).hasSameContentAs(new ByteArrayInputStream(CONTENT)));
+    }
+
+    @Test
+    void restoreShouldNotAppendMessagesToAnOtherUserMailbox() throws Exception {
+        vault.append(USER, DELETED_MESSAGE, new ByteArrayInputStream(CONTENT)).block();
+        vault.append(USER, DELETED_MESSAGE_2, new ByteArrayInputStream(CONTENT)).block();
+
+        String taskId = with()
+            .post(USER.asString())
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"));
+
+        assertThat(hasAnyMail(USER_2))
+            .isFalse();
+    }
+
+    private boolean hasAnyMail(User user) throws MailboxException {
+        MailboxSession session = mailboxManager.createSystemSession(user.asString());
+        int limitToOneMessage = 1;
+
+        return !mailboxManager.search(MultimailboxesSearchQuery.from(new SearchQuery()).build(), session, limitToOneMessage)
+            .isEmpty();
+    }
+
+    private InputStream fullContent(MessageResult messageResult) {
+        try {
+            return messageResult.getFullContent().getInputStream();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private List<MessageResult> restoreMailboxMessages(User user) throws Exception {
+        MailboxSession session = mailboxManager.createSystemSession(user.asString());
+        MessageManager messageManager = mailboxManager.getMailbox(MailboxPath.forUser(user.asString(), RESTORE_MAILBOX_NAME), session);
+        return ImmutableList.copyOf(messageManager.getMessages(MessageRange.all(), FetchGroupImpl.MINIMAL, session));
+    }
+}
\ No newline at end of file
diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 8709d9f..678a69a 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -44,6 +44,7 @@ as exposed above). To avoid information duplication, this is ommited on endpoint
  - [Administrating Sieve quotas](#Administrating_Sieve_quotas)
  - [ReIndexing](#ReIndexing)
  - [Event Dead Letter](#Event_Dead_Letter)
+ - [Deleted Messages Vault](#Deleted_Messages_Vault)
  - [Task management](#Task_management)
  - [Cassandra extra operations](#Cassandra_extra_operations)
 
@@ -2496,6 +2497,50 @@ Response codes:
 
 Not implemented yet.
 
+## Deleted Messages Vault
+
+The 'Deleted Message Vault plugin' allows you to keep users deleted messages during a given retention time. This set of routes allow you to *restore* users deleted messages or export them in an archive (not implemented yet).
+
+To move deleted messages in the vault, you need to specifically configure the DeletedMessageVault PreDeletionHook.
+
+Here are the following actions available on the 'Deleted Messages Vault'
+
+ - [Restore Deleted Messages](#Restore_deleted_messages)
+
+ Note that the 'Deleted Messages Vault' feature is only supported on top of Cassandra-Guice.
+
+### Restore Deleted Messages
+
+Deleted messages of a specific user can be restored by calling the following endpoint:
+
+```
+curl -XPOST http://ip:port/deletedMessages/user/userToRestore@domain.ext
+```
+
+**All** messages in the Deleted Messages Vault of an specified user will be appended to his 'Restored-Messages' mailbox, which will be created if needed.
+
+**Note**: Restoring matched messages by queries is not supported yet 
+
+Response code:
+
+ - 201: Task for restoring deleted has been created
+ - 400: Bad request, user parameter is invalid
+
+The scheduled task will have the following type `deletedMessages/restore` and the following `additionalInformation`:
+
+```
+{
+  "successfulRestoreCount": 47,
+  "errorRestoreCount": 0
+  "user": "userToRestore@domain.ext"
+}
+```
+
+while:
+ - successfulRestoreCount: number of restored messages
+ - errorRestoreCount: number of messages that failed to restore
+ - user: owner of deleted messages need to restore
+
 ## Task management
 
 Some webadmin features schedules tasks. The task management API allow to monitor and manage the execution of the following tasks.


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