JAMES-1874 Factorize more logic between CassandraMessageIdMapper and CassandraMessageMapper
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/948b58f8 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/948b58f8 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/948b58f8 Branch: refs/heads/master Commit: 948b58f8a34345f5231d985d63c179cbf0cdc97f Parents: 796f8eb Author: Benoit Tellier <btell...@linagora.com> Authored: Fri Feb 3 18:51:04 2017 +0700 Committer: Benoit Tellier <btell...@linagora.com> Committed: Tue Feb 7 08:57:46 2017 +0700 ---------------------------------------------------------------------- .../CassandraMailboxSessionMapperFactory.java | 8 +- .../mail/CassandraIndexTableHandler.java | 103 ++++++ .../mail/CassandraMessageIdMapper.java | 61 +-- .../cassandra/mail/CassandraMessageMapper.java | 70 +--- .../mail/CassandraIndexTableHandlerTest.java | 369 +++++++++++++++++++ 5 files changed, 489 insertions(+), 122 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java ---------------------------------------------------------------------- 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 b384297..42dd429 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 @@ -25,6 +25,7 @@ import org.apache.james.backends.cassandra.init.CassandraTypesProvider; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper; import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper; +import org.apache.james.mailbox.cassandra.mail.CassandraIndexTableHandler; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO; @@ -62,9 +63,9 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa private final CassandraMessageIdToImapUidDAO imapUidDAO; private final CassandraMailboxCounterDAO mailboxCounterDAO; private final CassandraMailboxRecentsDAO mailboxRecentsDAO; + private final CassandraIndexTableHandler indexTableHandler; private int maxRetry; - @Inject public CassandraMailboxSessionMapperFactory(UidProvider uidProvider, ModSeqProvider modSeqProvider, Session session, CassandraTypesProvider typesProvider, @@ -78,6 +79,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa this.imapUidDAO = imapUidDAO; this.mailboxCounterDAO = mailboxCounterDAO; this.mailboxRecentsDAO = mailboxRecentsDAO; + this.indexTableHandler = new CassandraIndexTableHandler(mailboxRecentsDAO, mailboxCounterDAO); this.maxRetry = DEFAULT_MAX_RETRY; this.typesProvider = typesProvider; } @@ -89,13 +91,13 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa @Override public CassandraMessageMapper createMessageMapper(MailboxSession mailboxSession) { return new CassandraMessageMapper(uidProvider, modSeqProvider, null, maxRetry, createAttachmentMapper(mailboxSession), - messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO); + messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO, indexTableHandler); } @Override public MessageIdMapper createMessageIdMapper(MailboxSession mailboxSession) throws MailboxException { return new CassandraMessageIdMapper(getMailboxMapper(mailboxSession), getAttachmentMapper(mailboxSession), - imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, mailboxRecentsDAO, modSeqProvider, mailboxSession); + imapUidDAO, messageIdDAO, messageDAO, indexTableHandler, modSeqProvider, mailboxSession); } @Override http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java new file mode 100644 index 0000000..860a632 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java @@ -0,0 +1,103 @@ +/**************************************************************** + * 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.mail; + +import java.util.concurrent.CompletableFuture; + +import javax.inject.Inject; +import javax.mail.Flags; + +import org.apache.james.mailbox.cassandra.CassandraId; +import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; +import org.apache.james.mailbox.model.UpdatedFlags; +import org.apache.james.mailbox.store.mail.model.MailboxMessage; + +public class CassandraIndexTableHandler { + + private final CassandraMailboxRecentsDAO mailboxRecentDAO; + private final CassandraMailboxCounterDAO mailboxCounterDAO; + + @Inject + public CassandraIndexTableHandler(CassandraMailboxRecentsDAO mailboxRecentDAO, + CassandraMailboxCounterDAO mailboxCounterDAO) { + this.mailboxRecentDAO = mailboxRecentDAO; + this.mailboxCounterDAO = mailboxCounterDAO; + } + + public CompletableFuture<Void> updateIndexOnDelete(ComposedMessageIdWithMetaData composedMessageIdWithMetaData, CassandraId mailboxId) { + return CompletableFuture.allOf( + mailboxRecentDAO.removeFromRecent(mailboxId, composedMessageIdWithMetaData.getComposedMessageId().getUid()), + mailboxCounterDAO.decrementCount(mailboxId), + decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags())); + } + + public CompletableFuture<Void> updateIndexOnAdd(MailboxMessage message, CassandraId mailboxId) { + return CompletableFuture.allOf( + addRecentOnSave(mailboxId, message), + incrementUnseenOnSave(mailboxId, message.createFlags()), + mailboxCounterDAO.incrementCount(mailboxId)); + } + + public CompletableFuture<Void> updateIndexOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) { + return CompletableFuture.allOf(manageUnseenMessageCountsOnFlagsUpdate(mailboxId, updatedFlags), + manageRecentOnFlagsUpdate(mailboxId, updatedFlags)); + } + + private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) { + if (flags.contains(Flags.Flag.SEEN)) { + return CompletableFuture.completedFuture(null); + } + return mailboxCounterDAO.decrementUnseen(mailboxId); + } + + private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) { + if (flags.contains(Flags.Flag.SEEN)) { + return CompletableFuture.completedFuture(null); + } + return mailboxCounterDAO.incrementUnseen(mailboxId); + } + + private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) { + if (message.createFlags().contains(Flags.Flag.RECENT)) { + return mailboxRecentDAO.addToRecent(mailboxId, message.getUid()); + } + return CompletableFuture.completedFuture(null); + } + + private CompletableFuture<Void> manageUnseenMessageCountsOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) { + if (updatedFlags.isModifiedToUnset(Flags.Flag.SEEN)) { + return mailboxCounterDAO.incrementUnseen(mailboxId); + } + if (updatedFlags.isModifiedToSet(Flags.Flag.SEEN)) { + return mailboxCounterDAO.decrementUnseen(mailboxId); + } + return CompletableFuture.completedFuture(null); + } + + private CompletableFuture<Void> manageRecentOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) { + if (updatedFlags.isModifiedToUnset(Flags.Flag.RECENT)) { + return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid()); + } + if (updatedFlags.isModifiedToSet(Flags.Flag.RECENT)) { + return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid()); + } + return CompletableFuture.completedFuture(null); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java index 41d8749..e403268 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java @@ -68,21 +68,19 @@ public class CassandraMessageIdMapper implements MessageIdMapper { private final CassandraMessageIdToImapUidDAO imapUidDAO; private final CassandraMessageIdDAO messageIdDAO; private final CassandraMessageDAO messageDAO; - private final CassandraMailboxCounterDAO mailboxCounterDAO; - private final CassandraMailboxRecentsDAO mailboxRecentsDAO; + private final CassandraIndexTableHandler indexTableHandler; private final ModSeqProvider modSeqProvider; private final MailboxSession mailboxSession; public CassandraMessageIdMapper(MailboxMapper mailboxMapper, AttachmentMapper attachmentMapper, CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO, - CassandraMailboxCounterDAO cassandraMailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) { + CassandraIndexTableHandler indexTableHandler, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) { this.mailboxMapper = mailboxMapper; this.attachmentMapper = attachmentMapper; this.imapUidDAO = imapUidDAO; this.messageIdDAO = messageIdDAO; this.messageDAO = messageDAO; - this.mailboxCounterDAO = cassandraMailboxCounterDAO; - this.mailboxRecentsDAO = mailboxRecentsDAO; + this.indexTableHandler = indexTableHandler; this.modSeqProvider = modSeqProvider; this.mailboxSession = mailboxSession; } @@ -150,20 +148,10 @@ public class CassandraMessageIdMapper implements MessageIdMapper { .thenCompose(voidValue -> CompletableFuture.allOf( imapUidDAO.insert(composedMessageIdWithMetaData), messageIdDAO.insert(composedMessageIdWithMetaData))) - .thenCompose(voidValue -> CompletableFuture.allOf( - mailboxRecentsDAO.addToRecent(mailboxId, mailboxMessage.getUid()), - mailboxCounterDAO.incrementCount(mailboxId), - incrementUnseenOnSave(mailboxId, mailboxMessage.createFlags()))) + .thenCompose(voidValue -> indexTableHandler.updateIndexOnAdd(mailboxMessage, mailboxId)) .join(); } - private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) { - if (flags.contains(Flags.Flag.SEEN)) { - return CompletableFuture.completedFuture(null); - } - return mailboxCounterDAO.incrementUnseen(mailboxId); - } - @Override public void delete(MessageId messageId, List<MailboxId> mailboxIds) { CassandraMessageId cassandraMessageId = (CassandraMessageId) messageId; @@ -197,17 +185,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper { return CompletableFuture.allOf( imapUidDAO.delete(messageId, mailboxId), messageIdDAO.delete(mailboxId, metaData.getComposedMessageId().getUid())) - .thenCompose(voidValue -> CompletableFuture.allOf( - mailboxCounterDAO.decrementCount(mailboxId), - mailboxRecentsDAO.removeFromRecent(mailboxId, metaData.getComposedMessageId().getUid()), - decrementUnseenOnDelete(mailboxId, metaData.getFlags()))); - } - - private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) { - if (flags.contains(Flags.Flag.SEEN)) { - return CompletableFuture.completedFuture(null); - } - return mailboxCounterDAO.decrementUnseen(mailboxId); + .thenCompose(voidValue -> indexTableHandler.updateIndexOnDelete(metaData, mailboxId)); } @Override @@ -246,37 +224,10 @@ public class CassandraMessageIdMapper implements MessageIdMapper { private CompletableFuture<Pair<MailboxId, UpdatedFlags>> updateCounts(Pair<MailboxId, UpdatedFlags> pair) { CassandraId cassandraId = (CassandraId) pair.getLeft(); - return CompletableFuture.allOf( - manageRecent(pair.getRight(), cassandraId), - incrementCountIfNeeded(pair.getRight(), cassandraId), - decrementCountIfNeeded(pair.getRight(), cassandraId)) + return indexTableHandler.updateIndexOnFlagsUpdate(cassandraId, pair.getRight()) .thenApply(voidValue -> pair); } - private CompletableFuture<Void> manageRecent(UpdatedFlags updatedFlags, CassandraId cassandraId) { - if (updatedFlags.isSet(Flags.Flag.RECENT)) { - return mailboxRecentsDAO.addToRecent(cassandraId, updatedFlags.getUid()); - } - if (updatedFlags.isUnset(Flags.Flag.RECENT)){ - return mailboxRecentsDAO.removeFromRecent(cassandraId, updatedFlags.getUid()); - } - return CompletableFuture.completedFuture(null); - } - - private CompletableFuture<Void> incrementCountIfNeeded(UpdatedFlags updatedFlags, CassandraId cassandraId) { - if (updatedFlags.isUnset(Flags.Flag.SEEN)) { - return mailboxCounterDAO.incrementUnseen(cassandraId); - } - return CompletableFuture.completedFuture(null); - } - - private CompletableFuture<Void> decrementCountIfNeeded(UpdatedFlags updatedFlags, CassandraId cassandraId) { - if (updatedFlags.isSet(Flags.Flag.SEEN)) { - return mailboxCounterDAO.decrementUnseen(cassandraId); - } - return CompletableFuture.completedFuture(null); - } - private Optional<Pair<Flags, ComposedMessageIdWithMetaData>> tryFlagsUpdate(Flags newState, MessageManager.FlagsUpdateMode updateMode, MailboxId mailboxId, MessageId messageId) { try { return updateFlags(mailboxId, messageId, newState, updateMode); http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java index 38d27b8..91d600e 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java @@ -76,11 +76,12 @@ public class CassandraMessageMapper implements MessageMapper { private final CassandraMessageIdToImapUidDAO imapUidDAO; private final CassandraMailboxCounterDAO mailboxCounterDAO; private final CassandraMailboxRecentsDAO mailboxRecentDAO; + private final CassandraIndexTableHandler indexTableHandler; public CassandraMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider, MailboxSession mailboxSession, int maxRetries, AttachmentMapper attachmentMapper, CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO, - CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO) { + CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO, CassandraIndexTableHandler indexTableHandler) { this.uidProvider = uidProvider; this.modSeqProvider = modSeqProvider; this.mailboxSession = mailboxSession; @@ -91,6 +92,7 @@ public class CassandraMessageMapper implements MessageMapper { this.imapUidDAO = imapUidDAO; this.mailboxCounterDAO = mailboxCounterDAO; this.mailboxRecentDAO = mailboxRecentDAO; + this.indexTableHandler = indexTableHandler; } @Override @@ -125,27 +127,9 @@ public class CassandraMessageMapper implements MessageMapper { return CompletableFuture.allOf( imapUidDAO.delete(messageId, mailboxId), messageIdDAO.delete(mailboxId, uid) - ).thenCompose(voidValue -> CompletableFuture.allOf( - removeRecentOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags(), composedMessageId.getUid()), - mailboxCounterDAO.decrementCount(mailboxId), - decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags()))); + ).thenCompose(voidValue -> indexTableHandler.updateIndexOnDelete(composedMessageIdWithMetaData, mailboxId)); } - private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) { - if (flags.contains(Flags.Flag.SEEN)) { - return CompletableFuture.completedFuture(null); - } - return mailboxCounterDAO.decrementUnseen(mailboxId); - } - - private CompletableFuture<Void> removeRecentOnDelete(CassandraId mailboxId, Flags flags, MessageUid uid) { - if (flags.contains(Flag.RECENT)) { - return mailboxRecentDAO.removeFromRecent(mailboxId, uid); - } - return CompletableFuture.completedFuture(null); - } - - private CompletableFuture<Optional<ComposedMessageIdWithMetaData>> retrieveMessageId(CassandraId mailboxId, MailboxMessage message) { return messageIdDAO.retrieve(mailboxId, message.getUid()); } @@ -231,34 +215,19 @@ public class CassandraMessageMapper implements MessageMapper { message.setModSeq(modSeqProvider.nextModSeq(mailboxSession, mailbox)); CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); save(mailbox, message) - .thenCompose(voidValue -> CompletableFuture.allOf( - addRecentOnSave(mailboxId, message), - incrementUnseenOnSave(mailboxId, message.createFlags()), - mailboxCounterDAO.incrementCount(mailboxId))) + .thenCompose(voidValue -> indexTableHandler.updateIndexOnAdd(message, mailboxId)) .join(); return new SimpleMessageMetaData(message); } - private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) { - if (flags.contains(Flags.Flag.SEEN)) { - return CompletableFuture.completedFuture(null); - } - return mailboxCounterDAO.incrementUnseen(mailboxId); - } - private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) { - if (message.createFlags().contains(Flag.RECENT)) { - return mailboxRecentDAO.addToRecent(mailboxId, message.getUid()); - } - return CompletableFuture.completedFuture(null); - } @Override public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MessageRange set) throws MailboxException { CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); return retrieveMessages(retrieveMessageIds(mailboxId, set), FetchType.Metadata, Optional.empty()) .flatMap(message -> updateFlagsOnMessage(mailbox, flagUpdateCalculator, message)) - .map((UpdatedFlags updatedFlags) -> manageCounters(mailbox, updatedFlags) + .map((UpdatedFlags updatedFlags) -> indexTableHandler.updateIndexOnFlagsUpdate(mailboxId, updatedFlags) .thenApply(voidValue -> updatedFlags)) .map(CompletableFuture::join) .collect(Collectors.toList()) // This collect is here as we need to consume all the stream before returning result @@ -297,33 +266,6 @@ public class CassandraMessageMapper implements MessageMapper { imapUidDAO.insert(composedMessageIdWithMetaData)); } - private CompletableFuture<Void> manageCounters(Mailbox mailbox, UpdatedFlags updatedFlags) { - return CompletableFuture.allOf(manageUnseenMessageCounts(mailbox, updatedFlags), - manageRecents(mailbox, updatedFlags)); - } - - private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, UpdatedFlags updatedFlags) { - CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); - if (updatedFlags.isUnset(Flag.SEEN)) { - return mailboxCounterDAO.incrementUnseen(mailboxId); - } - if (updatedFlags.isSet(Flag.SEEN)) { - return mailboxCounterDAO.decrementUnseen(mailboxId); - } - return CompletableFuture.completedFuture(null); - } - - private CompletableFuture<Void> manageRecents(Mailbox mailbox, UpdatedFlags updatedFlags) { - CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); - if (updatedFlags.isUnset(Flag.RECENT)) { - return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid()); - } - if (updatedFlags.isSet(Flag.RECENT)) { - return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid()); - } - return CompletableFuture.completedFuture(null); - } - private Stream<UpdatedFlags> updateFlagsOnMessage(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MailboxMessage message) { return tryMessageFlagsUpdate(flagUpdateCalculator, mailbox, message) .map(Stream::of) http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java new file mode 100644 index 0000000..93d14e4 --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java @@ -0,0 +1,369 @@ +/**************************************************************** + * 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.mail; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import javax.mail.Flags; + +import org.apache.james.backends.cassandra.CassandraCluster; +import org.apache.james.backends.cassandra.init.CassandraModuleComposite; +import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.cassandra.CassandraId; +import org.apache.james.mailbox.cassandra.CassandraMessageId; +import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule; +import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule; +import org.apache.james.mailbox.model.ComposedMessageId; +import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.UpdatedFlags; +import org.apache.james.mailbox.store.mail.model.Mailbox; +import org.apache.james.mailbox.store.mail.model.MailboxMessage; +import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class CassandraIndexTableHandlerTest { + + public static final CassandraId MAILBOX_ID = CassandraId.timeBased(); + public static final MessageUid MESSAGE_UID = MessageUid.of(18L); + public static final CassandraMessageId CASSANDRA_MESSAGE_ID = new CassandraMessageId.Factory().generate(); + public static final int UID_VALIDITY = 15; + public static final long MODSEQ = 17; + + private CassandraCluster cassandra; + private CassandraMailboxCounterDAO mailboxCounterDAO; + private CassandraMailboxRecentsDAO mailboxRecentsDAO; + private CassandraIndexTableHandler testee; + private Mailbox mailbox; + + @Before + public void setUp() { + cassandra = CassandraCluster.create( + new CassandraModuleComposite( + new CassandraMailboxCounterModule(), + new CassandraMailboxRecentsModule())); + cassandra.ensureAllTables(); + + mailboxCounterDAO = new CassandraMailboxCounterDAO(cassandra.getConf()); + mailboxRecentsDAO = new CassandraMailboxRecentsDAO(cassandra.getConf()); + testee = new CassandraIndexTableHandler(mailboxRecentsDAO, mailboxCounterDAO); + + mailbox = new SimpleMailbox(new MailboxPath("#private", "user", "name"), + UID_VALIDITY, + MAILBOX_ID); + } + + @After + public void tearDown() { + cassandra.clearAllTables(); + } + + @Test + public void updateIndexOnAddShouldIncrementMessageCount() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnAddShouldIncrementUnseenMessageCountWhenUnseen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnAddShouldNotIncrementUnseenMessageCountWhenSeen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN)); + when(message.getUid()).thenReturn(MESSAGE_UID); + + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(0); + } + + @Test + public void updateIndexOnAddShouldNotAddRecentWhenNoRecent() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .isEmpty(); + } + + @Test + public void updateIndexOnAddShouldAddRecentWhenRecent() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT)); + when(message.getUid()).thenReturn(MESSAGE_UID); + + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .containsOnly(MESSAGE_UID); + } + + @Test + public void updateIndexOnDeleteShouldDecrementMessageCount() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData( + new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID), + new Flags(Flags.Flag.RECENT), + MODSEQ), + MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(0); + } + + @Test + public void updateIndexOnDeleteShouldDecrementUnseenMessageCountWhenUnseen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData( + new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID), + new Flags(), + MODSEQ), + MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(0); + } + + @Test + public void updateIndexOnDeleteShouldNotDecrementUnseenMessageCountWhenSeen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData( + new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID), + new Flags(Flags.Flag.SEEN), + MODSEQ), + MAILBOX_ID).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnDeleteShouldRemoveRecentWhenRecent() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT)); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData( + new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID), + new Flags(Flags.Flag.RECENT), + MODSEQ), + MAILBOX_ID).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .isEmpty(); + } + + @Test + public void updateIndexOnDeleteShouldRemoveUidFromRecentAnyway() throws Exception { + // Clean up strategy if some flags updates missed + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT)); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData( + new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID), + new Flags(), + MODSEQ), + MAILBOX_ID).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .isEmpty(); + } + + @Test + public void updateIndexOnFlagsUpdateShouldNotChangeMessageCount() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags(Flags.Flag.RECENT)) + .oldFlags(new Flags()) + .modSeq(MODSEQ) + .build()).join(); + + Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnFlagsUpdateShouldDecrementUnseenMessageCountWhenSeenIsSet() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags(Flags.Flag.SEEN)) + .oldFlags(new Flags()) + .modSeq(MODSEQ) + .build()).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(0); + } + + @Test + public void updateIndexOnFlagsUpdateShouldIncrementUnseenMessageCountWhenSeenIsUnset() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN)); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags()) + .oldFlags(new Flags(Flags.Flag.SEEN)) + .modSeq(MODSEQ) + .build()).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnFlagsUpdateShouldNotChangeUnseenCountWhenBothSeen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN)); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags(Flags.Flag.SEEN)) + .oldFlags(new Flags(Flags.Flag.SEEN)) + .modSeq(MODSEQ) + .build()).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(0); + } + + @Test + public void updateIndexOnFlagsUpdateShouldNotChangeUnseenCountWhenBothUnSeen() throws Exception { + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags()) + .oldFlags(new Flags()) + .modSeq(MODSEQ) + .build()).join(); + + Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join(); + assertThat(actual.isPresent()).isTrue(); + assertThat(actual.get()).isEqualTo(1); + } + + @Test + public void updateIndexOnFlagsUpdateShouldAddRecentOnSettingRecentFlag() throws Exception { + // Clean up strategy if some flags updates missed + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags()); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags(Flags.Flag.RECENT)) + .oldFlags(new Flags()) + .modSeq(MODSEQ) + .build()).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .containsOnly(MESSAGE_UID); + } + + @Test + public void updateIndexOnFlagsUpdateShouldRemoveRecentOnUnsettingRecentFlag() throws Exception { + // Clean up strategy if some flags updates missed + MailboxMessage message = mock(MailboxMessage.class); + when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT)); + when(message.getUid()).thenReturn(MESSAGE_UID); + testee.updateIndexOnAdd(message, MAILBOX_ID).join(); + + testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder() + .uid(MESSAGE_UID) + .newFlags(new Flags()) + .oldFlags(new Flags(Flags.Flag.RECENT)) + .modSeq(MODSEQ) + .build()).join(); + + assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join()) + .isEmpty(); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org