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 2020/05/07 02:12:32 UTC
[james-project] 08/22: JAMES-3148 Add a listener for cleaning
mailbox/cassandra upon deletions
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 a42e36ecd7fd6ea24f06735065ce7adb2090ee29
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sun Apr 12 14:36:30 2020 +0700
JAMES-3148 Add a listener for cleaning mailbox/cassandra upon deletions
---
.../apache/james/mailbox/MailboxManagerTest.java | 4 +-
.../CassandraMailboxSessionMapperFactory.java | 4 +
.../mailbox/cassandra/DeleteMessageListener.java | 145 ++++++++++++
.../cassandra/mail/CassandraMessageDAO.java | 4 +-
.../cassandra/CassandraMailboxManagerProvider.java | 1 +
.../cassandra/CassandraMailboxManagerTest.java | 255 +++++++++++++++++++++
.../cassandra/CassandraTestSystemFixture.java | 1 +
.../modules/mailbox/CassandraMailboxModule.java | 7 +-
8 files changed, 414 insertions(+), 7 deletions(-)
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index f3dfaa4..28f5f94 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -111,9 +111,9 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
public static final Username USER_2 = Username.of("USER_2");
private static final int DEFAULT_MAXIMUM_LIMIT = 256;
- private T mailboxManager;
+ protected T mailboxManager;
private MailboxSession session;
- private Message.Builder message;
+ protected Message.Builder message;
private PreDeletionHook preDeletionHook1;
private PreDeletionHook preDeletionHook2;
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index c4177b6..7d29e36 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -202,4 +202,8 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
}
return mapper;
}
+
+ public DeleteMessageListener deleteMessageListener() {
+ return new DeleteMessageListener(imapUidDAO, messageIdDAO, messageDAO, attachmentDAOV2, ownerDAO, attachmentMessageIdDAO);
+ }
}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java
new file mode 100644
index 0000000..15d7b35
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/DeleteMessageListener.java
@@ -0,0 +1,145 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra;
+
+import static org.apache.james.util.FunctionalUtils.negate;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAOV2;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentOwnerDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
+import org.apache.james.mailbox.cassandra.mail.MessageAttachmentRepresentation;
+import org.apache.james.mailbox.cassandra.mail.MessageRepresentation;
+import org.apache.james.mailbox.events.Event;
+import org.apache.james.mailbox.events.Group;
+import org.apache.james.mailbox.events.MailboxListener;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.MessageMetaData;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.store.mail.MessageMapper;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class DeleteMessageListener implements MailboxListener.GroupMailboxListener {
+ private static final Optional<CassandraId> ALL_MAILBOXES = Optional.empty();
+
+ public static class DeleteMessageListenerGroup extends Group {
+
+ }
+
+ @Inject
+ public DeleteMessageListener(CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO,
+ CassandraAttachmentDAOV2 attachmentDAO, CassandraAttachmentOwnerDAO ownerDAO,
+ CassandraAttachmentMessageIdDAO attachmentMessageIdDAO) {
+ this.imapUidDAO = imapUidDAO;
+ this.messageIdDAO = messageIdDAO;
+ this.messageDAO = messageDAO;
+ this.attachmentDAO = attachmentDAO;
+ this.ownerDAO = ownerDAO;
+ this.attachmentMessageIdDAO = attachmentMessageIdDAO;
+ }
+
+ private final CassandraMessageIdToImapUidDAO imapUidDAO;
+ private final CassandraMessageIdDAO messageIdDAO;
+ private final CassandraMessageDAO messageDAO;
+ private final CassandraAttachmentDAOV2 attachmentDAO;
+ private final CassandraAttachmentOwnerDAO ownerDAO;
+ private final CassandraAttachmentMessageIdDAO attachmentMessageIdDAO;
+
+ @Override
+ public Group getDefaultGroup() {
+ return new DeleteMessageListenerGroup();
+ }
+
+ @Override
+ public boolean isHandling(Event event) {
+ return event instanceof Expunged || event instanceof MailboxDeletion;
+ }
+
+ @Override
+ public void event(Event event) {
+ if (event instanceof Expunged) {
+ Expunged expunged = (Expunged) event;
+
+ Flux.fromIterable(expunged.getExpunged()
+ .values())
+ .map(MessageMetaData::getMessageId)
+ .map(CassandraMessageId.class::cast)
+ .concatMap(this::handleDeletion)
+ .then()
+ .block();
+ }
+ if (event instanceof MailboxDeletion) {
+ MailboxDeletion mailboxDeletion = (MailboxDeletion) event;
+
+ CassandraId mailboxId = (CassandraId) mailboxDeletion.getMailboxId();
+
+ messageIdDAO.retrieveMessages(mailboxId, MessageRange.all())
+ .map(ComposedMessageIdWithMetaData::getComposedMessageId)
+ .concatMap(metadata -> imapUidDAO.delete((CassandraMessageId) metadata.getMessageId(), mailboxId)
+ .then(messageIdDAO.delete(mailboxId, metadata.getUid()))
+ .then(handleDeletion((CassandraMessageId) metadata.getMessageId())))
+ .then()
+ .block();
+ }
+ }
+
+ private Mono<Void> handleDeletion(CassandraMessageId messageId) {
+ return Mono.just(messageId)
+ .filterWhen(this::isReferenced)
+ .flatMap(id -> readMessage(id)
+ .flatMap(this::deleteUnreferencedAttachments)
+ .then(messageDAO.delete(messageId)));
+ }
+
+ private Mono<MessageRepresentation> readMessage(CassandraMessageId id) {
+ return messageDAO.retrieveMessage(id, MessageMapper.FetchType.Metadata);
+ }
+
+ private Mono<Void> deleteUnreferencedAttachments(MessageRepresentation message) {
+ return Flux.fromIterable(message.getAttachments())
+ .filterWhen(attachment -> ownerDAO.retrieveOwners(attachment.getAttachmentId()).hasElements().map(negate()))
+ .filterWhen(attachment -> hasOtherMessagesReferences(message, attachment))
+ .concatMap(attachment -> attachmentDAO.delete(attachment.getAttachmentId()))
+ .then();
+ }
+
+ private Mono<Boolean> hasOtherMessagesReferences(MessageRepresentation message, MessageAttachmentRepresentation attachment) {
+ return attachmentMessageIdDAO.getOwnerMessageIds(attachment.getAttachmentId())
+ .filter(messageId -> !message.getMessageId().equals(messageId))
+ .hasElements()
+ .map(negate());
+ }
+
+ private Mono<Boolean> isReferenced(CassandraMessageId id) {
+ return imapUidDAO.retrieve(id, ALL_MAILBOXES)
+ .hasElements()
+ .map(negate());
+ }
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
index baa1466..800b296 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
@@ -227,10 +227,10 @@ public class CassandraMessageDAO {
public Mono<MessageRepresentation> retrieveMessage(ComposedMessageIdWithMetaData id, FetchType fetchType) {
CassandraMessageId cassandraMessageId = (CassandraMessageId) id.getComposedMessageId().getMessageId();
- return retrieveMessage(fetchType, cassandraMessageId);
+ return retrieveMessage(cassandraMessageId, fetchType);
}
- private Mono<MessageRepresentation> retrieveMessage(FetchType fetchType, CassandraMessageId cassandraMessageId) {
+ public Mono<MessageRepresentation> retrieveMessage(CassandraMessageId cassandraMessageId, FetchType fetchType) {
return retrieveRow(cassandraMessageId, fetchType)
.flatMap(resultSet -> message(resultSet, cassandraMessageId, fetchType));
}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
index ab3dc5d..42ea282 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java
@@ -107,6 +107,7 @@ public class CassandraMailboxManagerProvider {
eventBus.register(quotaUpdater);
eventBus.register(new MailboxAnnotationListener(mapperFactory, sessionProvider));
+ eventBus.register(mapperFactory.deleteMessageListener());
return manager;
}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
index 5cd44e3..55eeb7c 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
@@ -18,13 +18,49 @@
****************************************************************/
package org.apache.james.mailbox.cassandra;
+import static org.mockito.Mockito.mock;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.blob.api.BlobStore;
+import org.apache.james.blob.api.HashBlobId;
+import org.apache.james.core.Username;
import org.apache.james.mailbox.MailboxManagerTest;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAOV2;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentOwnerDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
import org.apache.james.mailbox.cassandra.mail.MailboxAggregateModule;
import org.apache.james.mailbox.events.EventBus;
+import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.FetchGroup;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.MessageResult;
import org.apache.james.mailbox.store.PreDeletionHooks;
+import org.apache.james.mailbox.store.mail.MessageMapper;
import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.apache.james.util.ClassLoaderUtils;
+import org.apache.james.util.streams.Iterators;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
+
+import com.github.fge.lambdas.Throwing;
public class CassandraMailboxManagerTest extends MailboxManagerTest<CassandraMailboxManager> {
@RegisterExtension
@@ -41,4 +77,223 @@ public class CassandraMailboxManagerTest extends MailboxManagerTest<CassandraMai
protected EventBus retrieveEventBus(CassandraMailboxManager mailboxManager) {
return mailboxManager.getEventBus();
}
+
+ @Nested
+ class DeletionTests {
+ private MailboxSession session;
+ private MailboxPath inbox;
+ private MailboxId inboxId;
+ private MessageManager inboxManager;
+ private MessageManager otherBoxManager;
+ private MailboxPath newPath;
+
+ @BeforeEach
+ void setUp() throws Exception {
+ session = mailboxManager.createSystemSession(USER_1);
+ inbox = MailboxPath.inbox(session);
+ newPath = MailboxPath.forUser(USER_1, "specialMailbox");
+
+ inboxId = mailboxManager.createMailbox(inbox, session).get();
+ inboxManager = mailboxManager.getMailbox(inbox, session);
+ MailboxId otherId = mailboxManager.createMailbox(newPath, session).get();
+ otherBoxManager = mailboxManager.getMailbox(otherId, session);
+ }
+
+ @Test
+ void deleteMailboxShouldUnreferenceMessageMetadata(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ mailboxManager.deleteMailbox(inbox, session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+ CassandraId mailboxId = (CassandraId) composedMessageId.getMailboxId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isEmpty();
+
+ softly.assertThat(imapUidDAO(cassandraCluster).retrieve(cassandraMessageId, Optional.of(mailboxId)).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(messageIdDAO(cassandraCluster).retrieveMessages(mailboxId, MessageRange.all()).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isEmpty();
+ });
+ }
+
+ @Test
+ void deleteShouldUnreferenceMessageMetadata(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ inboxManager.delete(ImmutableList.of(composedMessageId.getUid()), session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isEmpty();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isEmpty();
+ });
+ }
+
+ @Test
+ void deleteMailboxShouldNotUnreferenceMessageMetadataWhenOtherReference(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ mailboxManager.copyMessages(MessageRange.all(), inboxId, otherBoxManager.getId(), session);
+
+ mailboxManager.deleteMailbox(inbox, session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+ CassandraId mailboxId = (CassandraId) composedMessageId.getMailboxId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isPresent();
+
+ softly.assertThat(imapUidDAO(cassandraCluster).retrieve(cassandraMessageId, Optional.of(mailboxId)).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(messageIdDAO(cassandraCluster).retrieveMessages(mailboxId, MessageRange.all()).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isPresent();
+ });
+ }
+
+ @Test
+ void deleteShouldNotUnreferenceMessageMetadataWhenOtherReference(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ mailboxManager.copyMessages(MessageRange.all(), inboxId, otherBoxManager.getId(), session);
+
+ inboxManager.delete(ImmutableList.of(composedMessageId.getUid()), session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isPresent();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isPresent();
+ });
+ }
+
+ @Test
+ void deleteMailboxShouldNotUnreferenceAttachmentWhenOtherReference(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ new CassandraAttachmentOwnerDAO(cassandraCluster.getConf()).addOwner(attachmentId, Username.of("bob")).block();
+
+ mailboxManager.deleteMailbox(inbox, session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+ CassandraId mailboxId = (CassandraId) composedMessageId.getMailboxId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isEmpty();
+
+ softly.assertThat(imapUidDAO(cassandraCluster).retrieve(cassandraMessageId, Optional.of(mailboxId)).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(messageIdDAO(cassandraCluster).retrieveMessages(mailboxId, MessageRange.all()).collectList().block())
+ .isEmpty();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isPresent();
+ });
+ }
+
+ @Test
+ void deleteShouldNotUnreferenceAttachmentWhenOtherReference(CassandraCluster cassandraCluster) throws Exception {
+ ComposedMessageId composedMessageId = inboxManager.appendMessage(MessageManager.AppendCommand.builder()
+ .build(ClassLoaderUtils.getSystemResourceAsByteArray("eml/emailWithOnlyAttachment.eml")), session);
+
+ AttachmentId attachmentId = Iterators.toStream(inboxManager.getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session))
+ .map(Throwing.function(MessageResult::getLoadedAttachments))
+ .flatMap(Collection::stream)
+ .map(MessageAttachment::getAttachmentId)
+ .findFirst()
+ .get();
+
+ new CassandraAttachmentOwnerDAO(cassandraCluster.getConf()).addOwner(attachmentId, Username.of("bob")).block();
+
+ inboxManager.delete(ImmutableList.of(composedMessageId.getUid()), session);
+
+ SoftAssertions.assertSoftly(softly -> {
+ CassandraMessageId cassandraMessageId = (CassandraMessageId) composedMessageId.getMessageId();
+ CassandraId mailboxId = (CassandraId) composedMessageId.getMailboxId();
+
+ softly.assertThat(messageDAO(cassandraCluster).retrieveMessage(cassandraMessageId, MessageMapper.FetchType.Metadata)
+ .blockOptional()).isEmpty();
+
+ softly.assertThat(attachmentDAO(cassandraCluster).getAttachment(attachmentId).blockOptional())
+ .isPresent();
+ });
+ }
+
+ private CassandraAttachmentDAOV2 attachmentDAO(CassandraCluster cassandraCluster) {
+ return new CassandraAttachmentDAOV2(new HashBlobId.Factory(), cassandraCluster.getConf());
+ }
+
+ private CassandraMessageIdDAO messageIdDAO(CassandraCluster cassandraCluster) {
+ return new CassandraMessageIdDAO(cassandraCluster.getConf(), new CassandraMessageId.Factory());
+ }
+
+ private CassandraMessageIdToImapUidDAO imapUidDAO(CassandraCluster cassandraCluster) {
+ return new CassandraMessageIdToImapUidDAO(cassandraCluster.getConf(), new CassandraMessageId.Factory());
+ }
+
+ private CassandraMessageDAO messageDAO(CassandraCluster cassandraCluster) {
+ return new CassandraMessageDAO(cassandraCluster.getConf(), cassandraCluster.getTypesProvider(),
+ mock(BlobStore.class), new HashBlobId.Factory(), new CassandraMessageId.Factory());
+ }
+ }
}
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 6524206..dea017b 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
@@ -80,6 +80,7 @@ public class CassandraTestSystemFixture {
eventBus, annotationManager, storeRightManager, quotaComponents, index, MailboxManagerConfiguration.DEFAULT, PreDeletionHooks.NO_PRE_DELETION_HOOK);
eventBus.register(new MailboxAnnotationListener(mapperFactory, sessionProvider));
+ eventBus.register(mapperFactory.deleteMessageListener());
return cassandraMailboxManager;
}
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 7630f5e..a3a0742 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.SessionProvider;
import org.apache.james.mailbox.SubscriptionManager;
import org.apache.james.mailbox.cassandra.CassandraMailboxManager;
import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory;
+import org.apache.james.mailbox.cassandra.DeleteMessageListener;
import org.apache.james.mailbox.cassandra.ids.CassandraId;
import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
import org.apache.james.mailbox.cassandra.mail.CassandraACLMapper;
@@ -192,9 +193,9 @@ public class CassandraMailboxModule extends AbstractModule {
Multibinder.newSetBinder(binder(), MailboxManagerDefinition.class).addBinding().to(CassandraMailboxManagerDefinition.class);
- Multibinder.newSetBinder(binder(), MailboxListener.GroupMailboxListener.class)
- .addBinding()
- .to(MailboxAnnotationListener.class);
+ Multibinder<MailboxListener.GroupMailboxListener> mailboxListeners = Multibinder.newSetBinder(binder(), MailboxListener.GroupMailboxListener.class);
+ mailboxListeners.addBinding().to(MailboxAnnotationListener.class);
+ mailboxListeners.addBinding().to(DeleteMessageListener.class);
bind(MailboxManager.class).annotatedWith(Names.named(MAILBOXMANAGER_NAME)).to(MailboxManager.class);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org