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 f29f69a60227268bc5817dedd7cbf1ac6d6eebfc Author: tran tien duc <[email protected]> AuthorDate: Wed Feb 20 15:20:19 2019 +0700 MAILBOX-379 Plug PreDeletionHooks to StoreMessageIdManager --- .../james/mailbox/extension/PreDeletionHook.java | 5 + .../CassandraCombinationManagerTestSystem.java | 3 +- .../CassandraMessageIdManagerSideEffectTest.java | 7 +- .../CassandraMessageIdManagerStorageTest.java | 3 +- .../CassandraMessageIdManagerTestSystem.java | 11 +- .../cassandra/CassandraTestSystemFixture.java | 9 +- .../ElasticSearchIntegrationTest.java | 4 +- .../search/LuceneMessageSearchIndexTest.java | 4 +- .../manager/InMemoryIntegrationResources.java | 4 +- .../InMemoryMessageIdManagerSideEffectTest.java | 8 +- .../store/search/SimpleMessageSearchIndexTest.java | 4 +- .../james/mailbox/store/StoreMessageIdManager.java | 24 +++- .../AbstractMessageIdManagerSideEffectTest.java | 150 ++++++++++++++++++++- .../org/apache/james/modules/MailboxModule.java | 3 + 14 files changed, 217 insertions(+), 22 deletions(-) diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/extension/PreDeletionHook.java b/mailbox/api/src/main/java/org/apache/james/mailbox/extension/PreDeletionHook.java index 41df20d..022e511 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/extension/PreDeletionHook.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/extension/PreDeletionHook.java @@ -21,11 +21,14 @@ package org.apache.james.mailbox.extension; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.UUID; import org.apache.james.mailbox.MetadataWithMailboxId; import org.reactivestreams.Publisher; +import com.google.common.collect.ImmutableSet; + public interface PreDeletionHook { class DeletionId { @@ -102,5 +105,7 @@ public interface PreDeletionHook { } } + Set<PreDeletionHook> NO_PRE_DELETION_HOOK = ImmutableSet.of(); + Publisher<Void> notifyDelete(DeleteOperation deleteOperation); } diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraCombinationManagerTestSystem.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraCombinationManagerTestSystem.java index 4878a9b..31e3c87 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraCombinationManagerTestSystem.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraCombinationManagerTestSystem.java @@ -26,6 +26,7 @@ import org.apache.james.mailbox.MessageIdManager; import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.quota.QuotaManager; import org.apache.james.mailbox.store.CombinationManagerTestSystem; @@ -39,7 +40,7 @@ public class CassandraCombinationManagerTestSystem extends CombinationManagerTes public static CombinationManagerTestSystem createTestingData(CassandraCluster cassandra, QuotaManager quotaManager, EventBus eventBus) { CassandraMailboxSessionMapperFactory mapperFactory = CassandraTestSystemFixture.createMapperFactory(cassandra); - return new CassandraCombinationManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, eventBus), + return new CassandraCombinationManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, eventBus, PreDeletionHook.NO_PRE_DELETION_HOOK), mapperFactory, CassandraTestSystemFixture.createMailboxManager(mapperFactory)); } diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerSideEffectTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerSideEffectTest.java index 3b70449..2d76328 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerSideEffectTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerSideEffectTest.java @@ -19,10 +19,13 @@ package org.apache.james.mailbox.cassandra; +import java.util.Set; + import org.apache.james.backends.cassandra.CassandraCluster; import org.apache.james.backends.cassandra.DockerCassandraRule; import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule; import org.apache.james.mailbox.events.EventBus; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.quota.QuotaManager; import org.apache.james.mailbox.store.AbstractMessageIdManagerSideEffectTest; import org.apache.james.mailbox.store.MessageIdManagerTestSystem; @@ -60,7 +63,7 @@ public class CassandraMessageIdManagerSideEffectTest extends AbstractMessageIdMa } @Override - protected MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus) { - return CassandraMessageIdManagerTestSystem.createTestingData(cassandra, quotaManager, eventBus); + protected MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus, Set<PreDeletionHook> preDeletionHooks) { + return CassandraMessageIdManagerTestSystem.createTestingData(cassandra, quotaManager, eventBus, preDeletionHooks); } } diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerStorageTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerStorageTest.java index 0902dce..99a9093 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerStorageTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerStorageTest.java @@ -24,6 +24,7 @@ import org.apache.james.backends.cassandra.DockerCassandraRule; import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.store.AbstractMessageIdManagerStorageTest; import org.apache.james.mailbox.store.MessageIdManagerTestSystem; import org.apache.james.mailbox.store.quota.NoQuotaManager; @@ -64,6 +65,6 @@ public class CassandraMessageIdManagerStorageTest extends AbstractMessageIdManag @Override protected MessageIdManagerTestSystem createTestingData() { InVMEventBus eventBus = new InVMEventBus(new InVmEventDelivery(new NoopMetricFactory())); - return CassandraMessageIdManagerTestSystem.createTestingData(cassandra, new NoQuotaManager(), eventBus); + return CassandraMessageIdManagerTestSystem.createTestingData(cassandra, new NoQuotaManager(), eventBus, PreDeletionHook.NO_PRE_DELETION_HOOK); } } diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerTestSystem.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerTestSystem.java index 0bdeb45..c0dd20d 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerTestSystem.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMessageIdManagerTestSystem.java @@ -19,9 +19,12 @@ package org.apache.james.mailbox.cassandra; +import java.util.Set; + import org.apache.james.backends.cassandra.CassandraCluster; import org.apache.james.mailbox.cassandra.ids.CassandraMessageId; import org.apache.james.mailbox.events.EventBus; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.quota.CurrentQuotaManager; import org.apache.james.mailbox.quota.QuotaManager; import org.apache.james.mailbox.store.MessageIdManagerTestSystem; @@ -30,10 +33,11 @@ import org.apache.james.mailbox.store.quota.StoreCurrentQuotaManager; class CassandraMessageIdManagerTestSystem { - static MessageIdManagerTestSystem createTestingData(CassandraCluster cassandra, QuotaManager quotaManager, EventBus eventBus) { + static MessageIdManagerTestSystem createTestingData(CassandraCluster cassandra, QuotaManager quotaManager, EventBus eventBus, + Set<PreDeletionHook> preDeletionHooks) { CassandraMailboxSessionMapperFactory mapperFactory = CassandraTestSystemFixture.createMapperFactory(cassandra); - return new MessageIdManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, eventBus), + return new MessageIdManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, eventBus, preDeletionHooks), new CassandraMessageId.Factory(), mapperFactory, CassandraTestSystemFixture.createMailboxManager(mapperFactory)) { @@ -48,7 +52,8 @@ class CassandraMessageIdManagerTestSystem { (StoreCurrentQuotaManager) currentQuotaManager, mailboxManager.getQuotaComponents().getQuotaRootResolver(), mailboxManager.getEventBus(), quotaManager); mailboxManager.getEventBus().register(listeningCurrentQuotaUpdater); - return new MessageIdManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, mailboxManager.getEventBus()), + return new MessageIdManagerTestSystem(CassandraTestSystemFixture.createMessageIdManager(mapperFactory, quotaManager, mailboxManager.getEventBus(), + PreDeletionHook.NO_PRE_DELETION_HOOK), new CassandraMessageId.Factory(), mapperFactory, mailboxManager); diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java index b1bef0a..4c32722 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java @@ -21,6 +21,8 @@ package org.apache.james.mailbox.cassandra; import static org.mockito.Mockito.mock; +import java.util.Set; + import org.apache.james.backends.cassandra.CassandraCluster; import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver; import org.apache.james.mailbox.acl.UnionMailboxACLResolver; @@ -33,6 +35,7 @@ import org.apache.james.mailbox.cassandra.quota.CassandraPerUserMaxQuotaManager; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.quota.CurrentQuotaManager; import org.apache.james.mailbox.quota.MaxQuotaManager; import org.apache.james.mailbox.quota.QuotaManager; @@ -82,7 +85,8 @@ class CassandraTestSystemFixture { return cassandraMailboxManager; } - static StoreMessageIdManager createMessageIdManager(CassandraMailboxSessionMapperFactory mapperFactory, QuotaManager quotaManager, EventBus eventBus) { + static StoreMessageIdManager createMessageIdManager(CassandraMailboxSessionMapperFactory mapperFactory, QuotaManager quotaManager, EventBus eventBus, + Set<PreDeletionHook> preDeletionHooks) { CassandraMailboxManager mailboxManager = createMailboxManager(mapperFactory); return new StoreMessageIdManager( mailboxManager, @@ -90,7 +94,8 @@ class CassandraTestSystemFixture { eventBus, new CassandraMessageId.Factory(), quotaManager, - new DefaultUserQuotaRootResolver(mailboxManager.getSessionProvider(), mapperFactory)); + new DefaultUserQuotaRootResolver(mailboxManager.getSessionProvider(), mapperFactory), + preDeletionHooks); } static MaxQuotaManager createMaxQuotaManager(CassandraCluster cassandra) { diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java index 4556681..e49c063 100644 --- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java +++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java @@ -42,6 +42,7 @@ import org.apache.james.mailbox.elasticsearch.query.QueryConverter; import org.apache.james.mailbox.elasticsearch.search.ElasticSearchSearcher; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; @@ -159,7 +160,8 @@ public class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest eventBus, storeMailboxManager.getMessageIdFactory(), quotaComponents.getQuotaManager(), - quotaComponents.getQuotaRootResolver()); + quotaComponents.getQuotaRootResolver(), + PreDeletionHook.NO_PRE_DELETION_HOOK); eventBus.register(elasticSearchListeningMessageSearchIndex); this.messageSearchIndex = elasticSearchListeningMessageSearchIndex; diff --git a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java index 9dffa1d..d82be9c 100644 --- a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java +++ b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMessageSearchIndexTest.java @@ -24,6 +24,7 @@ import org.apache.james.mailbox.acl.UnionMailboxACLResolver; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; @@ -85,7 +86,8 @@ public class LuceneMessageSearchIndexTest extends AbstractMessageSearchIndexTest eventBus, storeMailboxManager.getMessageIdFactory(), quotaComponents.getQuotaManager(), - quotaComponents.getQuotaRootResolver()); + quotaComponents.getQuotaRootResolver(), + PreDeletionHook.NO_PRE_DELETION_HOOK); eventBus.register(luceneMessageSearchIndex); this.messageSearchIndex = luceneMessageSearchIndex; diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java index 1f5beff..9e5e5a5 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java @@ -26,6 +26,7 @@ import org.apache.james.mailbox.acl.UnionMailboxACLResolver; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.inmemory.InMemoryMessageId; @@ -193,7 +194,8 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM mailboxManager.getEventBus(), factory, quotaComponents.getQuotaManager(), - quotaComponents.getQuotaRootResolver()); + quotaComponents.getQuotaRootResolver(), + PreDeletionHook.NO_PRE_DELETION_HOOK); } @Override diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryMessageIdManagerSideEffectTest.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryMessageIdManagerSideEffectTest.java index e270749..c99bb6b 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryMessageIdManagerSideEffectTest.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryMessageIdManagerSideEffectTest.java @@ -19,9 +19,12 @@ package org.apache.james.mailbox.inmemory.manager; +import java.util.Set; + import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver; import org.apache.james.mailbox.acl.UnionMailboxACLResolver; import org.apache.james.mailbox.events.EventBus; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.inmemory.InMemoryMessageId; @@ -55,7 +58,7 @@ public class InMemoryMessageIdManagerSideEffectTest extends AbstractMessageIdMan } @Override - protected MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus) { + protected MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus, Set<PreDeletionHook> preDeletionHooks) { InMemoryMailboxSessionMapperFactory mapperFactory = new InMemoryMailboxSessionMapperFactory(); FakeAuthenticator fakeAuthenticator = new FakeAuthenticator(); @@ -87,7 +90,8 @@ public class InMemoryMessageIdManagerSideEffectTest extends AbstractMessageIdMan eventBus, messageIdFactory, quotaManager, - quotaComponents.getQuotaRootResolver()); + quotaComponents.getQuotaRootResolver(), + preDeletionHooks); return new MessageIdManagerTestSystem(messageIdManager, messageIdFactory, mapperFactory, mailboxManager); } } diff --git a/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndexTest.java b/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndexTest.java index 38cd886..8b423d6 100644 --- a/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndexTest.java +++ b/mailbox/scanning-search/src/test/java/org/apache/james/mailbox/store/search/SimpleMessageSearchIndexTest.java @@ -21,6 +21,7 @@ package org.apache.james.mailbox.store.search; import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver; import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources; import org.apache.james.mailbox.store.StoreMessageIdManager; import org.junit.Ignore; @@ -47,7 +48,8 @@ public class SimpleMessageSearchIndexTest extends AbstractMessageSearchIndexTest storeMailboxManager.getEventBus(), storeMailboxManager.getMessageIdFactory(), storeMailboxManager.getQuotaComponents().getQuotaManager(), - storeMailboxManager.getQuotaComponents().getQuotaRootResolver()); + storeMailboxManager.getQuotaComponents().getQuotaRootResolver(), + PreDeletionHook.NO_PRE_DELETION_HOOK); } /** diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java index 3031b9b..808f8aa 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java @@ -37,10 +37,12 @@ import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageIdManager; import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.MetadataWithMailboxId; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.events.MailboxIdRegistrationKey; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxNotFoundException; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.model.DeleteResult; import org.apache.james.mailbox.model.MailboxACL.Right; import org.apache.james.mailbox.model.MailboxId; @@ -95,17 +97,19 @@ public class StoreMessageIdManager implements MessageIdManager { private final MessageId.Factory messageIdFactory; private final QuotaManager quotaManager; private final QuotaRootResolver quotaRootResolver; + private final Set<PreDeletionHook> preDeletionHooks; @Inject public StoreMessageIdManager(MailboxManager mailboxManager, MailboxSessionMapperFactory mailboxSessionMapperFactory, EventBus eventBus, MessageId.Factory messageIdFactory, - QuotaManager quotaManager, QuotaRootResolver quotaRootResolver) { + QuotaManager quotaManager, QuotaRootResolver quotaRootResolver, Set<PreDeletionHook> preDeletionHooks) { this.mailboxManager = mailboxManager; this.mailboxSessionMapperFactory = mailboxSessionMapperFactory; this.eventBus = eventBus; this.messageIdFactory = messageIdFactory; this.quotaManager = quotaManager; this.quotaRootResolver = quotaRootResolver; + this.preDeletionHooks = preDeletionHooks; } @Override @@ -167,7 +171,7 @@ public class StoreMessageIdManager implements MessageIdManager { .collect(Guavate.toImmutableList()); if (!messageList.isEmpty()) { - delete(messageIdMapper, messageList, mailboxSession); + deleteWithPreHooks(messageIdMapper, messageList, mailboxSession); return DeleteResult.destroyed(messageId); } return DeleteResult.notFound(messageId); @@ -187,7 +191,7 @@ public class StoreMessageIdManager implements MessageIdManager { .collect(Guavate.toImmutableSet()); Sets.SetView<MessageId> nonAccessibleMessages = Sets.difference(ImmutableSet.copyOf(messageIds), accessibleMessages); - delete(messageIdMapper, messageList, mailboxSession); + deleteWithPreHooks(messageIdMapper, messageList, mailboxSession); return DeleteResult.builder() .addDestroyed(accessibleMessages) @@ -195,11 +199,21 @@ public class StoreMessageIdManager implements MessageIdManager { .build(); } - private void delete(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession) throws MailboxException { + private void deleteWithPreHooks(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession) throws MailboxException { ImmutableList<MetadataWithMailboxId> metadataWithMailbox = messageList.stream() - .map(MetadataWithMailboxId::from) + .map(mailboxMessage -> MetadataWithMailboxId.from(mailboxMessage.metaData(), mailboxMessage.getMailboxId())) .collect(Guavate.toImmutableList()); + PreDeletionHook.DeleteOperation deleteOperation = PreDeletionHook.DeleteOperation.from(metadataWithMailbox); + Flux.fromIterable(preDeletionHooks) + .flatMap(preDeletionHook -> preDeletionHook.notifyDelete(deleteOperation)) + .then(Mono.fromRunnable(Throwing.runnable( + () -> delete(messageIdMapper, messageList, mailboxSession, metadataWithMailbox)))) + .block(); + } + + private void delete(MessageIdMapper messageIdMapper, List<MailboxMessage> messageList, MailboxSession mailboxSession, + ImmutableList<MetadataWithMailboxId> metadataWithMailbox) throws MailboxException { messageIdMapper.delete( messageList.stream() .collect(Guavate.toImmutableListMultimap( diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java index b798123..f26e772 100644 --- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java @@ -21,11 +21,16 @@ package org.apache.james.mailbox.store; import static org.apache.james.mailbox.fixture.MailboxFixture.ALICE; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; +import java.util.Set; +import java.util.concurrent.CountDownLatch; import javax.mail.Flags; @@ -37,6 +42,7 @@ import org.apache.james.mailbox.MessageIdManager; import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.MessageManager.FlagsUpdateMode; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.MetadataWithMailboxId; import org.apache.james.mailbox.events.EventBus; import org.apache.james.mailbox.events.InVMEventBus; import org.apache.james.mailbox.events.MailboxListener; @@ -44,6 +50,7 @@ import org.apache.james.mailbox.events.MessageMoveEvent; import org.apache.james.mailbox.events.delivery.InVmEventDelivery; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.OverQuotaException; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.fixture.MailboxFixture; import org.apache.james.mailbox.model.FetchGroupImpl; import org.apache.james.mailbox.model.MessageId; @@ -58,13 +65,18 @@ import org.apache.james.mailbox.util.EventCollector; import org.apache.james.metrics.api.NoopMetricFactory; import org.assertj.core.api.AbstractListAssert; import org.assertj.core.api.ObjectAssert; +import org.assertj.core.api.SoftAssertions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.mockito.ArgumentCaptor; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; +import reactor.core.publisher.Mono; + public abstract class AbstractMessageIdManagerSideEffectTest { private static final Quota<QuotaCount> OVER_QUOTA = Quota.<QuotaCount>builder() .used(QuotaCount.count(102)) @@ -87,8 +99,10 @@ public abstract class AbstractMessageIdManagerSideEffectTest { private MessageIdManagerTestSystem testingData; private EventCollector eventCollector; private EventBus eventBus; + private PreDeletionHook preDeletionHook1; + private PreDeletionHook preDeletionHook2; - protected abstract MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus) throws Exception; + protected abstract MessageIdManagerTestSystem createTestSystem(QuotaManager quotaManager, EventBus eventBus, Set<PreDeletionHook> preDeletionHooks) throws Exception; public void setUp() throws Exception { eventBus = new InVMEventBus(new InVmEventDelivery(new NoopMetricFactory())); @@ -96,7 +110,8 @@ public abstract class AbstractMessageIdManagerSideEffectTest { quotaManager = mock(QuotaManager.class); session = MailboxSessionUtil.create(ALICE); - testingData = createTestSystem(quotaManager, eventBus); + setupMockForPreDeletionHooks(); + testingData = createTestSystem(quotaManager, eventBus, ImmutableSet.of(preDeletionHook1, preDeletionHook2)); messageIdManager = testingData.getMessageIdManager(); mailbox1 = testingData.createMailbox(MailboxFixture.INBOX_ALICE, session); @@ -104,6 +119,15 @@ public abstract class AbstractMessageIdManagerSideEffectTest { mailbox3 = testingData.createMailbox(MailboxFixture.SENT_ALICE, session); } + private void setupMockForPreDeletionHooks() { + preDeletionHook1 = mock(PreDeletionHook.class); + when(preDeletionHook1.notifyDelete(any(PreDeletionHook.DeleteOperation.class))) + .thenReturn(Mono.empty()); + + preDeletionHook2 = mock(PreDeletionHook.class); + when(preDeletionHook2.notifyDelete(any(PreDeletionHook.DeleteOperation.class))) + .thenReturn(Mono.empty()); + } @Test public void deleteShouldCallEventDispatcher() throws Exception { givenUnlimitedQuota(); @@ -164,6 +188,128 @@ public abstract class AbstractMessageIdManagerSideEffectTest { } @Test + public void deletesShouldCallAllPreDeletionHooks() throws Exception { + givenUnlimitedQuota(); + + MessageId messageId = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + messageIdManager.delete(messageId, ImmutableList.of(mailbox1.getMailboxId()), session); + + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor1 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor2 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + verify(preDeletionHook1, times(1)).notifyDelete(preDeleteCaptor1.capture()); + verify(preDeletionHook2, times(1)).notifyDelete(preDeleteCaptor2.capture()); + + assertThat(preDeleteCaptor1.getValue().getDeletionMetadataList()) + .hasSize(1) + .hasSameElementsAs(preDeleteCaptor2.getValue().getDeletionMetadataList()) + .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> { + softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(mailbox1.getMailboxId()); + softy.assertThat(deleteMetadata.getMessageMetaData().getMessageId()).isEqualTo(messageId); + softy.assertThat(deleteMetadata.getMessageMetaData().getFlags()).isEqualTo(FLAGS); + })); + + } + + @Test + public void deletesShouldCallAllPreDeletionHooksOnEachMessageDeletionCall() throws Exception { + givenUnlimitedQuota(); + + MessageId messageId1 = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + MessageId messageId2 = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + messageIdManager.delete(messageId1, ImmutableList.of(mailbox1.getMailboxId()), session); + messageIdManager.delete(messageId2, ImmutableList.of(mailbox1.getMailboxId()), session); + + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor1 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor2 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + verify(preDeletionHook1, times(2)).notifyDelete(preDeleteCaptor1.capture()); + verify(preDeletionHook2, times(2)).notifyDelete(preDeleteCaptor2.capture()); + + assertThat(preDeleteCaptor1.getAllValues()) + .hasSize(2) + .hasSameElementsAs(preDeleteCaptor2.getAllValues()) + .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList) + .allSatisfy(deleteMetadata -> SoftAssertions.assertSoftly(softy -> { + softy.assertThat(deleteMetadata.getMailboxId()).isEqualTo(mailbox1.getMailboxId()); + softy.assertThat(deleteMetadata.getMessageMetaData().getFlags()).isEqualTo(FLAGS); + })) + .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId()) + .containsOnly(messageId1, messageId2); + + } + + @Test + public void deletesShouldCallAllPreDeletionHooksOnEachMessageDeletionOnDifferentMailboxes() throws Exception { + givenUnlimitedQuota(); + + MessageId messageId1 = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + MessageId messageId2 = testingData.persist(mailbox2.getMailboxId(), messageUid1, FLAGS, session); + messageIdManager.delete(messageId1, ImmutableList.of(mailbox1.getMailboxId()), session); + messageIdManager.delete(messageId2, ImmutableList.of(mailbox2.getMailboxId()), session); + + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor1 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + ArgumentCaptor<PreDeletionHook.DeleteOperation> preDeleteCaptor2 = ArgumentCaptor.forClass(PreDeletionHook.DeleteOperation.class); + verify(preDeletionHook1, times(2)).notifyDelete(preDeleteCaptor1.capture()); + verify(preDeletionHook2, times(2)).notifyDelete(preDeleteCaptor2.capture()); + + assertThat(preDeleteCaptor1.getAllValues()) + .hasSameElementsAs(preDeleteCaptor2.getAllValues()) + .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList) + .extracting(deleteMetadata -> deleteMetadata.getMessageMetaData().getMessageId()) + .containsOnly(messageId1, messageId2); + + assertThat(preDeleteCaptor1.getAllValues()) + .hasSameElementsAs(preDeleteCaptor2.getAllValues()) + .flatExtracting(PreDeletionHook.DeleteOperation::getDeletionMetadataList) + .extracting(MetadataWithMailboxId::getMailboxId) + .containsOnly(mailbox1.getMailboxId(), mailbox2.getMailboxId()); + } + + @Test + public void deletesShouldNotBeExecutedWhenOneOfPreDeleteHooksFails() throws Exception { + givenUnlimitedQuota(); + when(preDeletionHook1.notifyDelete(any(PreDeletionHook.DeleteOperation.class))) + .thenThrow(new RuntimeException("throw at hook 1")); + + MessageId messageId = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + assertThatThrownBy(() -> messageIdManager.delete(messageId, ImmutableList.of(mailbox1.getMailboxId()), session)) + .isInstanceOf(RuntimeException.class); + + assertThat(messageIdManager.getMessages(ImmutableList.of(messageId), FetchGroupImpl.MINIMAL, session) + .stream() + .map(MessageResult::getMessageId)) + .hasSize(1) + .containsOnly(messageId); + } + + @Test + public void deletesShouldBeExecutedAfterAllHooksFinish() throws Exception { + givenUnlimitedQuota(); + + CountDownLatch latchForHook1 = new CountDownLatch(1); + when(preDeletionHook1.notifyDelete(any(PreDeletionHook.DeleteOperation.class))) + .thenAnswer(invocation -> { + latchForHook1.countDown(); + return Mono.empty(); + }); + + CountDownLatch latchForHook2 = new CountDownLatch(1); + when(preDeletionHook2.notifyDelete(any(PreDeletionHook.DeleteOperation.class))) + .thenAnswer(invocation -> { + latchForHook2.countDown(); + return Mono.empty(); + }); + + MessageId messageId = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); + messageIdManager.delete(messageId, ImmutableList.of(mailbox1.getMailboxId()), session); + + latchForHook1.await(); + latchForHook2.await(); + + assertThat(messageIdManager.getMessages(ImmutableList.of(messageId), FetchGroupImpl.MINIMAL, session)) + .isEmpty(); + } + + @Test public void setInMailboxesShouldNotCallDispatcherWhenMessageAlreadyInMailbox() throws Exception { givenUnlimitedQuota(); MessageId messageId = testingData.persist(mailbox1.getMailboxId(), messageUid1, FLAGS, session); diff --git a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxModule.java b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxModule.java index f95e235..82ba294 100644 --- a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxModule.java +++ b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxModule.java @@ -23,6 +23,7 @@ import org.apache.james.mailbox.acl.GroupMembershipResolver; import org.apache.james.mailbox.acl.MailboxACLResolver; import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver; import org.apache.james.mailbox.acl.UnionMailboxACLResolver; +import org.apache.james.mailbox.extension.PreDeletionHook; import org.apache.james.mailbox.store.SystemMailboxesProviderImpl; import org.apache.james.utils.GuiceProbe; @@ -48,6 +49,8 @@ public class MailboxModule extends AbstractModule { bind(SystemMailboxesProviderImpl.class).in(Scopes.SINGLETON); bind(SystemMailboxesProvider.class).to(SystemMailboxesProviderImpl.class); + + Multibinder<PreDeletionHook> noPreDeletionHooks = Multibinder.newSetBinder(binder(), PreDeletionHook.class); } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
