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 746ca874aecfb5ea77679a081a32999471bbbce7 Author: Quan Tran <hqt...@linagora.com> AuthorDate: Thu Dec 1 17:35:58 2022 +0700 JAMES-3754 Mapper layers should set new saveDate when add/copy/move messages --- .../CassandraMailboxSessionMapperFactory.java | 9 +- .../mailbox/cassandra/mail/AttachmentLoader.java | 5 +- .../cassandra/mail/CassandraMessageIdMapper.java | 10 ++- .../cassandra/mail/CassandraMessageMapper.java | 24 ++++-- .../cassandra/mail/CassandraMessageMetadata.java | 5 ++ .../cassandra/mail/MessageRepresentation.java | 4 +- .../CassandraSubscriptionManagerTest.java | 5 +- .../TestCassandraMailboxSessionMapperFactory.java | 13 +++ .../cassandra/mail/CassandraMapperProvider.java | 14 +++- ...andraMessageIdMapperRelaxedConsistencyTest.java | 39 ++++++--- .../mail/CassandraMessageIdMapperTest.java | 16 +++- ...ssandraMessageMapperRelaxedConsistencyTest.java | 39 ++++++--- .../cassandra/mail/CassandraMessageMapperTest.java | 16 +++- .../mailbox/cassandra/mail/utils/GuiceUtils.java | 4 +- .../model/openjpa/AbstractJPAMailboxMessage.java | 5 ++ .../mailbox/jpa/mail/JpaMessageMapperTest.java | 16 +++- .../InMemoryMailboxSessionMapperFactory.java | 6 +- .../inmemory/mail/InMemoryMessageMapper.java | 7 +- .../inmemory/mail/InMemoryMapperProvider.java | 9 +- .../inmemory/mail/InMemoryMessageIdMapperTest.java | 9 +- .../inmemory/mail/MemoryMessageMapperTest.java | 10 ++- .../manager/InMemoryIntegrationResources.java | 4 +- .../OpenSearchListeningMessageSearchIndexTest.java | 6 +- .../mailbox/store/mail/AbstractMessageMapper.java | 5 +- .../mailbox/store/mail/model/MailboxMessage.java | 2 + .../mail/model/impl/SimpleMailboxMessage.java | 7 +- .../store/mail/model/MessageIdMapperTest.java | 51 ++++++++++++ .../store/mail/model/MessageMapperTest.java | 96 ++++++++++++++++++++++ 28 files changed, 370 insertions(+), 66 deletions(-) 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 4e57852f88..22e150af51 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 @@ -19,6 +19,8 @@ package org.apache.james.mailbox.cassandra; +import java.time.Clock; + import javax.inject.Inject; import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; @@ -106,7 +108,8 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa CassandraUserMailboxRightsDAO userMailboxRightsDAO, RecomputeMailboxCountersService recomputeMailboxCountersService, CassandraConfiguration cassandraConfiguration, - BatchSizes batchSizes) { + BatchSizes batchSizes, + Clock clock) { this.uidProvider = uidProvider; this.modSeqProvider = modSeqProvider; this.threadDAO = threadDAO; @@ -150,10 +153,10 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa firstUnseenDAO, deletedMessageDAO, blobStore, - cassandraConfiguration, batchSizes, recomputeMailboxCountersService); + cassandraConfiguration, batchSizes, recomputeMailboxCountersService, clock); this.cassandraMessageIdMapper = new CassandraMessageIdMapper(cassandraMailboxMapper, mailboxDAO, cassandraAttachmentMapper, imapUidDAO, messageIdDAO, messageDAO, messageDAOV3, indexTableHandler, - modSeqProvider, blobStore, cassandraConfiguration, batchSizes); + modSeqProvider, blobStore, cassandraConfiguration, batchSizes, clock); this.cassandraAnnotationMapper = new CassandraAnnotationMapper(session); } diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/AttachmentLoader.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/AttachmentLoader.java index 4d1a4f77c8..f37267caa2 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/AttachmentLoader.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/AttachmentLoader.java @@ -18,7 +18,9 @@ ****************************************************************/ package org.apache.james.mailbox.cassandra.mail; +import java.util.Date; import java.util.List; +import java.util.Optional; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; @@ -43,9 +45,10 @@ public class AttachmentLoader { } public Mono<CassandraMailboxMessage> addAttachmentToMessage(Pair<ComposedMessageIdWithMetaData, MessageRepresentation> messageRepresentation, + Optional<Date> saveDate, MessageMapper.FetchType fetchType) { return loadAttachments(messageRepresentation.getRight().getAttachments().stream(), fetchType) - .map(attachments -> messageRepresentation.getRight().toMailboxMessage(messageRepresentation.getLeft(), attachments)) + .map(attachments -> messageRepresentation.getRight().toMailboxMessage(messageRepresentation.getLeft(), attachments, saveDate)) .map(message -> new CassandraMailboxMessage(message, messageRepresentation.getRight().getHeaderId())); } 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 39bf53c626..9b2b7dd7af 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 @@ -23,8 +23,10 @@ import static org.apache.james.backends.cassandra.init.configuration.JamesExecut import static org.apache.james.blob.api.BlobStore.StoragePolicy.SIZE_BASED; import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY; +import java.time.Clock; import java.time.Duration; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -90,11 +92,12 @@ public class CassandraMessageIdMapper implements MessageIdMapper { private final BlobStore blobStore; private final CassandraConfiguration cassandraConfiguration; private final BatchSizes batchSizes; + private final Clock clock; public CassandraMessageIdMapper(MailboxMapper mailboxMapper, CassandraMailboxDAO mailboxDAO, CassandraAttachmentMapper attachmentMapper, CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO, CassandraMessageDAOV3 messageDAOV3, CassandraIndexTableHandler indexTableHandler, - ModSeqProvider modSeqProvider, BlobStore blobStore, CassandraConfiguration cassandraConfiguration, BatchSizes batchSizes) { + ModSeqProvider modSeqProvider, BlobStore blobStore, CassandraConfiguration cassandraConfiguration, BatchSizes batchSizes, Clock clock) { this.mailboxMapper = mailboxMapper; this.mailboxDAO = mailboxDAO; @@ -108,6 +111,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper { this.blobStore = blobStore; this.cassandraConfiguration = cassandraConfiguration; this.batchSizes = batchSizes; + this.clock = clock; } @Override @@ -138,7 +142,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper { return messageDAOV3.retrieveMessage(metadata.getComposedMessageId(), fetchType) .switchIfEmpty(Mono.defer(() -> messageDAO.retrieveMessage(metadata.getComposedMessageId(), fetchType))) .map(messageRepresentation -> Pair.of(metadata.getComposedMessageId(), messageRepresentation)) - .flatMap(messageRepresentation -> attachmentLoader.addAttachmentToMessage(messageRepresentation, fetchType)); + .flatMap(messageRepresentation -> attachmentLoader.addAttachmentToMessage(messageRepresentation, metadata.getSaveDate(), fetchType)); } @Override @@ -187,6 +191,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper { @Override public void save(MailboxMessage mailboxMessage) throws MailboxException { CassandraId mailboxId = (CassandraId) mailboxMessage.getMailboxId(); + mailboxMessage.setSaveDate(Date.from(clock.instant())); MailboxReactorUtils.block(mailboxMapper.findMailboxById(mailboxId) .switchIfEmpty(Mono.error(() -> new MailboxNotFoundException(mailboxId))) .then(messageDAOV3.save(mailboxMessage)) @@ -200,6 +205,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper { @Override public Mono<Void> copyInMailboxReactive(MailboxMessage mailboxMessage, Mailbox mailbox) { + mailboxMessage.setSaveDate(Date.from(clock.instant())); CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); return insertMetadata(mailboxMessage, mailboxId, CassandraMessageMetadata.from(mailboxMessage) .withMailboxId(mailboxId)); 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 ae473652a4..5f6924ae0b 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 @@ -25,9 +25,11 @@ import static org.apache.james.blob.api.BlobStore.StoragePolicy.SIZE_BASED; import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY; import java.security.SecureRandom; +import java.time.Clock; import java.time.Duration; import java.util.Collection; import java.util.Comparator; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -110,6 +112,7 @@ public class CassandraMessageMapper implements MessageMapper { private final RecomputeMailboxCountersService recomputeMailboxCountersService; private final SecureRandom secureRandom; private final int reactorConcurrency; + private final Clock clock; public CassandraMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider, CassandraAttachmentMapper attachmentMapper, @@ -118,7 +121,7 @@ public class CassandraMessageMapper implements MessageMapper { CassandraMailboxRecentsDAO mailboxRecentDAO, CassandraApplicableFlagDAO applicableFlagDAO, CassandraIndexTableHandler indexTableHandler, CassandraFirstUnseenDAO firstUnseenDAO, CassandraDeletedMessageDAO deletedMessageDAO, BlobStore blobStore, CassandraConfiguration cassandraConfiguration, - BatchSizes batchSizes, RecomputeMailboxCountersService recomputeMailboxCountersService) { + BatchSizes batchSizes, RecomputeMailboxCountersService recomputeMailboxCountersService, Clock clock) { this.uidProvider = uidProvider; this.modSeqProvider = modSeqProvider; this.messageDAO = messageDAO; @@ -138,6 +141,7 @@ public class CassandraMessageMapper implements MessageMapper { this.recomputeMailboxCountersService = recomputeMailboxCountersService; this.secureRandom = new SecureRandom(); this.reactorConcurrency = evaluateReactorConcurrency(); + this.clock = clock; } @Override @@ -273,7 +277,7 @@ public class CassandraMessageMapper implements MessageMapper { return messageDAOV3.retrieveMessage(metadata.getComposedMessageId(), fetchType) .switchIfEmpty(Mono.defer(() -> messageDAO.retrieveMessage(metadata.getComposedMessageId(), fetchType))) .map(messageRepresentation -> Pair.of(metadata.getComposedMessageId(), messageRepresentation)) - .flatMap(messageRepresentation -> attachmentLoader.addAttachmentToMessage(messageRepresentation, fetchType)); + .flatMap(messageRepresentation -> attachmentLoader.addAttachmentToMessage(messageRepresentation, metadata.getSaveDate(), fetchType)); } @Override @@ -333,18 +337,17 @@ public class CassandraMessageMapper implements MessageMapper { return Flux.fromIterable(MessageRange.toRanges(uids)) .concatMap(range -> messageIdDAO.retrieveMessages(mailboxId, range, Limit.unlimited())) - .map(CassandraMessageMetadata::getComposedMessageId) - .flatMap(this::expungeOne, cassandraConfiguration.getExpungeChunkSize()) + .flatMap(cassandraMessageMetadata -> expungeOne(cassandraMessageMetadata.getComposedMessageId(), cassandraMessageMetadata.getSaveDate()), cassandraConfiguration.getExpungeChunkSize()) .collect(ImmutableMap.toImmutableMap(MailboxMessage::getUid, MailboxMessage::metaData)) .flatMap(messageMap -> indexTableHandler.updateIndexOnDelete(mailboxId, messageMap.values()) .thenReturn(messageMap)); } - private Mono<SimpleMailboxMessage> expungeOne(ComposedMessageIdWithMetaData metaData) { + private Mono<SimpleMailboxMessage> expungeOne(ComposedMessageIdWithMetaData metaData, Optional<Date> saveDate) { return delete(metaData) .then(messageDAOV3.retrieveMessage(metaData, FetchType.METADATA) .switchIfEmpty(Mono.defer(() -> messageDAO.retrieveMessage(metaData, FetchType.METADATA)))) - .map(pair -> pair.toMailboxMessage(metaData, ImmutableList.of())); + .map(pair -> pair.toMailboxMessage(metaData, ImmutableList.of(), saveDate)); } @Override @@ -401,14 +404,14 @@ public class CassandraMessageMapper implements MessageMapper { public Mono<MessageMetaData> addReactive(Mailbox mailbox, MailboxMessage message) { CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); - return addUidAndModseq(message, mailboxId) + return addUidAndModseqAndSaveDate(message, mailboxId) .flatMap(Throwing.function((MailboxMessage messageWithUidAndModSeq) -> save(mailbox, messageWithUidAndModSeq) .thenReturn(messageWithUidAndModSeq))) .map(MailboxMessage::metaData); } - private Mono<MailboxMessage> addUidAndModseq(MailboxMessage message, CassandraId mailboxId) { + private Mono<MailboxMessage> addUidAndModseqAndSaveDate(MailboxMessage message, CassandraId mailboxId) { Mono<MessageUid> messageUidMono = uidProvider .nextUidReactive(mailboxId) .switchIfEmpty(Mono.error(() -> new MailboxException("Can not find a UID to save " + message.getMessageId() + " in " + mailboxId))); @@ -420,6 +423,7 @@ public class CassandraMessageMapper implements MessageMapper { .doOnNext(tuple -> { message.setUid(tuple.getT1()); message.setModSeq(tuple.getT2()); + message.setSaveDate(Date.from(clock.instant())); }) .thenReturn(message); } @@ -545,6 +549,7 @@ public class CassandraMessageMapper implements MessageMapper { @Override public Mono<MessageMetaData> copyReactive(Mailbox mailbox, MailboxMessage original) { original.setFlags(new FlagsBuilder().add(original.createFlags()).add(Flag.RECENT).build()); + original.setSaveDate(Date.from(clock.instant())); return setInMailboxReactive(mailbox, original); } @@ -556,6 +561,7 @@ public class CassandraMessageMapper implements MessageMapper { return setMessagesInMailboxReactive(mailbox, originals.stream() .map(original -> { original.setFlags(new FlagsBuilder().add(original.createFlags()).add(Flag.RECENT).build()); + original.setSaveDate(Date.from(clock.instant())); return original; }).collect(ImmutableList.toImmutableList())) .collectList(); @@ -584,7 +590,7 @@ public class CassandraMessageMapper implements MessageMapper { private Mono<MessageMetaData> setInMailboxReactive(Mailbox mailbox, MailboxMessage message) { CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); - return addUidAndModseq(message, mailboxId) + return addUidAndModseqAndSaveDate(message, mailboxId) .flatMap(messageWithUidAndModseq -> insertMetadata(messageWithUidAndModseq, mailboxId, CassandraMessageMetadata.from(messageWithUidAndModseq) diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java index 0e34163bf5..ebc9436315 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMetadata.java @@ -203,6 +203,11 @@ public class CassandraMessageMetadata { delegate.setFlags(flags); } + @Override + public void setSaveDate(Date saveDate) { + delegate.setSaveDate(saveDate); + } + @Override public Flags createFlags() { return delegate.createFlags(); diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/MessageRepresentation.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/MessageRepresentation.java index 39cada4b95..025e1b2bf9 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/MessageRepresentation.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/MessageRepresentation.java @@ -21,6 +21,7 @@ package org.apache.james.mailbox.cassandra.mail; import java.util.Date; import java.util.List; +import java.util.Optional; import org.apache.james.blob.api.BlobId; import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; @@ -54,7 +55,7 @@ public class MessageRepresentation { this.bodyId = bodyId; } - public SimpleMailboxMessage toMailboxMessage(ComposedMessageIdWithMetaData metadata, List<MessageAttachmentMetadata> attachments) { + public SimpleMailboxMessage toMailboxMessage(ComposedMessageIdWithMetaData metadata, List<MessageAttachmentMetadata> attachments, Optional<Date> saveDate) { return SimpleMailboxMessage.builder() .messageId(messageId) .threadId(metadata.getThreadId()) @@ -62,6 +63,7 @@ public class MessageRepresentation { .uid(metadata.getComposedMessageId().getUid()) .modseq(metadata.getModSeq()) .internalDate(internalDate) + .saveDate(saveDate) .bodyStartOctet(bodyStartOctet) .size(size) .content(content) diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java index 2c6a90d691..e51dc31081 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java @@ -21,6 +21,8 @@ package org.apache.james.mailbox.cassandra; import static org.assertj.core.api.Assertions.assertThat; +import java.time.Clock; + import org.apache.james.backends.cassandra.CassandraClusterExtension; import org.apache.james.backends.cassandra.components.CassandraModule; import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; @@ -136,7 +138,8 @@ class CassandraSubscriptionManagerTest implements SubscriptionManagerContract { userMailboxRightsDAO, recomputeMailboxCountersService, CassandraConfiguration.DEFAULT_CONFIGURATION, - BatchSizes.defaultValues()); + BatchSizes.defaultValues(), + Clock.systemUTC()); InVMEventBus eventBus = new InVMEventBus(new InVmEventDelivery(new RecordingMetricFactory()), EventBusTestFixture.RETRY_BACKOFF_CONFIGURATION, new MemoryEventDeadLetters()); diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java index a1e3d7976f..a4cd1f124d 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java @@ -19,11 +19,14 @@ package org.apache.james.mailbox.cassandra; +import java.time.Clock; + import org.apache.james.backends.cassandra.CassandraCluster; import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; import org.apache.james.mailbox.cassandra.ids.CassandraMessageId; import org.apache.james.mailbox.cassandra.mail.utils.GuiceUtils; import org.apache.james.mailbox.store.BatchSizes; +import org.apache.james.utils.UpdatableTickingClock; import com.google.inject.Guice; import com.google.inject.util.Modules; @@ -42,6 +45,16 @@ public class TestCassandraMailboxSessionMapperFactory { .getInstance(CassandraMailboxSessionMapperFactory.class); } + public static CassandraMailboxSessionMapperFactory forTests(CassandraCluster cassandra, + CassandraMessageId.Factory factory, + CassandraConfiguration cassandraConfiguration, + UpdatableTickingClock updatableTickingClock) { + + return Guice.createInjector(Modules.override(GuiceUtils.commonModules(cassandra.getConf(), cassandra.getTypesProvider(), factory, cassandraConfiguration)) + .with(binder -> binder.bind(Clock.class).toInstance(updatableTickingClock))) + .getInstance(CassandraMailboxSessionMapperFactory.class); + } + public static CassandraMailboxSessionMapperFactory forTests(CassandraCluster cassandra, CassandraMessageId.Factory factory, CassandraConfiguration cassandraConfiguration, diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java index 145fb709b8..e3e7cd9f2b 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.mailbox.cassandra.mail; +import java.time.Instant; import java.util.List; import org.apache.james.backends.cassandra.CassandraCluster; @@ -41,6 +42,7 @@ import org.apache.james.mailbox.store.mail.MessageIdMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageUidProvider; +import org.apache.james.utils.UpdatableTickingClock; import com.google.common.collect.ImmutableList; @@ -51,6 +53,7 @@ public class CassandraMapperProvider implements MapperProvider { private final CassandraCluster cassandra; private final MessageUidProvider messageUidProvider; private final CassandraModSeqProvider cassandraModSeqProvider; + private final UpdatableTickingClock updatableTickingClock; private final MailboxSession mailboxSession = MailboxSessionUtil.create(Username.of("benwa")); private CassandraMailboxSessionMapperFactory mapperFactory; @@ -61,7 +64,8 @@ public class CassandraMapperProvider implements MapperProvider { cassandraModSeqProvider = new CassandraModSeqProvider( this.cassandra.getConf(), cassandraConfiguration); - mapperFactory = createMapperFactory(cassandraConfiguration); + updatableTickingClock = new UpdatableTickingClock(Instant.now()); + mapperFactory = createMapperFactory(cassandraConfiguration, updatableTickingClock); } @Override @@ -84,10 +88,11 @@ public class CassandraMapperProvider implements MapperProvider { return mapperFactory.getMessageIdMapper(mailboxSession); } - private CassandraMailboxSessionMapperFactory createMapperFactory(CassandraConfiguration cassandraConfiguration) { + private CassandraMailboxSessionMapperFactory createMapperFactory(CassandraConfiguration cassandraConfiguration, UpdatableTickingClock updatableTickingClock) { return TestCassandraMailboxSessionMapperFactory.forTests(cassandra, new CassandraMessageId.Factory(), - cassandraConfiguration); + cassandraConfiguration, + updatableTickingClock); } @Override @@ -125,4 +130,7 @@ public class CassandraMapperProvider implements MapperProvider { return cassandraModSeqProvider.highestModSeq(mailbox); } + public UpdatableTickingClock getUpdatableTickingClock() { + return updatableTickingClock; + } } diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperRelaxedConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperRelaxedConsistencyTest.java index 5a00c78bc2..5380a66fa9 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperRelaxedConsistencyTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperRelaxedConsistencyTest.java @@ -22,6 +22,7 @@ package org.apache.james.mailbox.cassandra.mail; import org.apache.james.backends.cassandra.CassandraClusterExtension; import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; import org.apache.james.mailbox.store.mail.model.MessageIdMapperTest; +import org.apache.james.utils.UpdatableTickingClock; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.RegisterExtension; @@ -32,27 +33,41 @@ class CassandraMessageIdMapperRelaxedConsistencyTest { @Nested class WeakReadConsistency extends MessageIdMapperTest { + private final CassandraMapperProvider mapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.builder() + .messageReadStrongConsistency(false) + .messageWriteStrongConsistency(true) + .build()); + @Override protected CassandraMapperProvider provideMapper() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.builder() - .messageReadStrongConsistency(false) - .messageWriteStrongConsistency(true) - .build()); + return mapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return mapperProvider.getUpdatableTickingClock(); } } @Nested class WeakWriteConsistency extends MessageIdMapperTest { + private final CassandraMapperProvider mapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.builder() + .messageReadStrongConsistency(false) + .messageWriteStrongConsistency(false) + .build()); + @Override protected CassandraMapperProvider provideMapper() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.builder() - .messageReadStrongConsistency(false) - .messageWriteStrongConsistency(false) - .build()); + return mapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return mapperProvider.getUpdatableTickingClock(); } @Disabled("JAMES-3435 Without strong consistency flags update is not thread safe as long as it follows a read-before-write pattern") diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperTest.java index 958be20af9..4af4d7785b 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapperTest.java @@ -47,6 +47,7 @@ import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.MailboxMessage; import org.apache.james.mailbox.store.mail.model.MessageIdMapperTest; import org.apache.james.util.streams.Limit; +import org.apache.james.utils.UpdatableTickingClock; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Tag; @@ -61,12 +62,19 @@ class CassandraMessageIdMapperTest extends MessageIdMapperTest { @RegisterExtension static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MailboxAggregateModule.MODULE); - + + private final CassandraMapperProvider mapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.DEFAULT_CONFIGURATION); + @Override protected CassandraMapperProvider provideMapper() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.DEFAULT_CONFIGURATION); + return mapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return mapperProvider.getUpdatableTickingClock(); } @Test diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperRelaxedConsistencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperRelaxedConsistencyTest.java index 4028772cec..f6d65f6c00 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperRelaxedConsistencyTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperRelaxedConsistencyTest.java @@ -23,6 +23,7 @@ import org.apache.james.backends.cassandra.CassandraClusterExtension; import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageMapperTest; +import org.apache.james.utils.UpdatableTickingClock; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.RegisterExtension; @@ -33,27 +34,41 @@ class CassandraMessageMapperRelaxedConsistencyTest { @Nested class WeakReadConsistency extends MessageMapperTest { + private final CassandraMapperProvider cassandraMapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.builder() + .messageReadStrongConsistency(false) + .messageWriteStrongConsistency(true) + .build()); + @Override protected MapperProvider createMapperProvider() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.builder() - .messageReadStrongConsistency(false) - .messageWriteStrongConsistency(true) - .build()); + return cassandraMapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return cassandraMapperProvider.getUpdatableTickingClock(); } } @Nested class WeakWriteConsistency extends MessageMapperTest { + private final CassandraMapperProvider cassandraMapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.builder() + .messageReadStrongConsistency(false) + .messageWriteStrongConsistency(false) + .build()); + @Override protected MapperProvider createMapperProvider() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.builder() - .messageReadStrongConsistency(false) - .messageWriteStrongConsistency(false) - .build()); + return cassandraMapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return cassandraMapperProvider.getUpdatableTickingClock(); } @Disabled("JAMES-3435 Without strong consistency flags update is not thread safe as long as it follows a read-before-write pattern") diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java index ec84ffb1fe..dfc02de3ac 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java @@ -46,6 +46,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageMapperTest; import org.apache.james.util.streams.Limit; +import org.apache.james.utils.UpdatableTickingClock; import org.assertj.core.api.SoftAssertions; import org.awaitility.Awaitility; import org.junit.jupiter.api.Nested; @@ -58,12 +59,19 @@ import com.google.common.collect.ImmutableList; class CassandraMessageMapperTest extends MessageMapperTest { @RegisterExtension static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MailboxAggregateModule.MODULE); - + + private final CassandraMapperProvider cassandraMapperProvider = new CassandraMapperProvider( + cassandraCluster.getCassandraCluster(), + CassandraConfiguration.DEFAULT_CONFIGURATION); + @Override protected MapperProvider createMapperProvider() { - return new CassandraMapperProvider( - cassandraCluster.getCassandraCluster(), - CassandraConfiguration.DEFAULT_CONFIGURATION); + return cassandraMapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return cassandraMapperProvider.getUpdatableTickingClock(); } @Nested diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java index a6dd9b3773..ed06c2c280 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/utils/GuiceUtils.java @@ -19,6 +19,7 @@ package org.apache.james.mailbox.cassandra.mail.utils; +import java.time.Clock; import java.util.Set; import org.apache.james.backends.cassandra.CassandraCluster; @@ -94,6 +95,7 @@ public class GuiceUtils { binder -> Multibinder.newSetBinder(binder, new TypeLiteral<EventDTOModule<? extends Event, ? extends EventDTO>>() {}), binder -> binder.bind(EventStore.class).to(CassandraEventStore.class), binder -> binder.bind(CassandraTypesProvider.class).toInstance(typesProvider), - binder -> binder.bind(CassandraConfiguration.class).toInstance(configuration)); + binder -> binder.bind(CassandraConfiguration.class).toInstance(configuration), + binder -> binder.bind(Clock.class).toInstance(Clock.systemUTC())); } } diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java index 879171416d..d8c40546db 100644 --- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java +++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java @@ -435,6 +435,11 @@ public abstract class AbstractJPAMailboxMessage implements MailboxMessage { this.uid = uid.asLong(); } + @Override + public void setSaveDate(Date saveDate) { + + } + @Override public long getHeaderOctets() { return bodyStartOctet; diff --git a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMessageMapperTest.java b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMessageMapperTest.java index 1e5db354e0..6a9c7055dd 100644 --- a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMessageMapperTest.java +++ b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMessageMapperTest.java @@ -35,7 +35,10 @@ import org.apache.james.mailbox.model.UpdatedFlags; import org.apache.james.mailbox.store.FlagsUpdateCalculator; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageMapperTest; +import org.apache.james.utils.UpdatableTickingClock; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class JpaMessageMapperTest extends MessageMapperTest { @@ -46,7 +49,12 @@ class JpaMessageMapperTest extends MessageMapperTest { protected MapperProvider createMapperProvider() { return new JPAMapperProvider(JPA_TEST_CLUSTER); } - + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return null; + } + @AfterEach void cleanUp() { JPA_TEST_CLUSTER.clear(JPAMailboxFixture.MAILBOX_TABLE_NAMES); @@ -139,4 +147,10 @@ class JpaMessageMapperTest extends MessageMapperTest { .newFlags(new Flags()) .build()); } + + @Nested + @Disabled("JPA does not support saveDate.") + class SaveDateTests { + + } } diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java index 42967eeccf..c9e384f3bb 100644 --- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java +++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.mailbox.inmemory; +import java.time.Clock; + import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.inmemory.mail.InMemoryAnnotationMapper; @@ -49,11 +51,11 @@ public class InMemoryMailboxSessionMapperFactory extends MailboxSessionMapperFac private final InMemoryUidProvider uidProvider; private final InMemoryModSeqProvider modSeqProvider; - public InMemoryMailboxSessionMapperFactory() { + public InMemoryMailboxSessionMapperFactory(Clock clock) { mailboxMapper = new InMemoryMailboxMapper(); uidProvider = new InMemoryUidProvider(); modSeqProvider = new InMemoryModSeqProvider(); - messageMapper = new InMemoryMessageMapper(null, uidProvider, modSeqProvider); + messageMapper = new InMemoryMessageMapper(null, uidProvider, modSeqProvider, clock); messageIdMapper = new InMemoryMessageIdMapper(mailboxMapper, messageMapper); subscriptionMapper = new InMemorySubscriptionMapper(); diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java index 3545e85b7c..5b6295e0c5 100644 --- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java +++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageMapper.java @@ -19,8 +19,10 @@ package org.apache.james.mailbox.inmemory.mail; +import java.time.Clock; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -54,8 +56,8 @@ public class InMemoryMessageMapper extends AbstractMessageMapper { private static final int INITIAL_SIZE = 256; public InMemoryMessageMapper(MailboxSession session, UidProvider uidProvider, - ModSeqProvider modSeqProvider) { - super(session, uidProvider, modSeqProvider); + ModSeqProvider modSeqProvider, Clock clock) { + super(session, uidProvider, modSeqProvider, clock); this.mailboxByUid = new ConcurrentHashMap<>(INITIAL_SIZE); } @@ -200,6 +202,7 @@ public class InMemoryMessageMapper extends AbstractMessageMapper { SimpleMailboxMessage copy = SimpleMailboxMessage.copy(mailbox.getMailboxId(), message); copy.setUid(message.getUid()); copy.setModSeq(message.getModSeq()); + copy.setSaveDate(Date.from(clock.instant())); getMembershipByUidForMailbox(mailbox).put(message.getUid(), copy); return copy.metaData(); diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java index 8f60b26760..4667385719 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java @@ -19,6 +19,7 @@ package org.apache.james.mailbox.inmemory.mail; +import java.time.Instant; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -39,6 +40,7 @@ import org.apache.james.mailbox.store.mail.MessageIdMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageUidProvider; +import org.apache.james.utils.UpdatableTickingClock; import com.google.common.collect.ImmutableList; @@ -50,12 +52,14 @@ public class InMemoryMapperProvider implements MapperProvider { private final MessageId.Factory messageIdFactory; private final MessageUidProvider messageUidProvider; private final InMemoryMailboxSessionMapperFactory inMemoryMailboxSessionMapperFactory; + private final UpdatableTickingClock clock; public InMemoryMapperProvider() { messageIdFactory = new InMemoryMessageId.Factory(); messageUidProvider = new MessageUidProvider(); - inMemoryMailboxSessionMapperFactory = new InMemoryMailboxSessionMapperFactory(); + clock = new UpdatableTickingClock(Instant.now()); + inMemoryMailboxSessionMapperFactory = new InMemoryMailboxSessionMapperFactory(clock); } @Override @@ -124,4 +128,7 @@ public class InMemoryMapperProvider implements MapperProvider { .highestModSeq(mailbox); } + public UpdatableTickingClock getClock() { + return clock; + } } diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapperTest.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapperTest.java index faf70ab9f9..080f8b8915 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapperTest.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapperTest.java @@ -21,11 +21,18 @@ package org.apache.james.mailbox.inmemory.mail; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageIdMapperTest; +import org.apache.james.utils.UpdatableTickingClock; class InMemoryMessageIdMapperTest extends MessageIdMapperTest { + private final InMemoryMapperProvider mapperProvider = new InMemoryMapperProvider(); @Override protected MapperProvider provideMapper() { - return new InMemoryMapperProvider(); + return mapperProvider; + } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return mapperProvider.getClock(); } } diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryMessageMapperTest.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryMessageMapperTest.java index 77b3696081..8e0c618a69 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryMessageMapperTest.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryMessageMapperTest.java @@ -21,11 +21,19 @@ package org.apache.james.mailbox.inmemory.mail; import org.apache.james.mailbox.store.mail.model.MapperProvider; import org.apache.james.mailbox.store.mail.model.MessageMapperTest; +import org.apache.james.utils.UpdatableTickingClock; class MemoryMessageMapperTest extends MessageMapperTest { + private final InMemoryMapperProvider inMemoryMapperProvider = new InMemoryMapperProvider(); @Override protected MapperProvider createMapperProvider() { - return new InMemoryMapperProvider(); + return inMemoryMapperProvider; } + + @Override + protected UpdatableTickingClock updatableTickingClock() { + return inMemoryMapperProvider.getClock(); + } + } 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 d6a5c06759..8a390fecfa 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 @@ -294,7 +294,8 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM Preconditions.checkState(searchIndexFactory.isPresent()); Preconditions.checkState(messageParser.isPresent()); - InMemoryMailboxSessionMapperFactory mailboxSessionMapperFactory = new InMemoryMailboxSessionMapperFactory(); + UpdatableTickingClock clock = new UpdatableTickingClock(Instant.now()); + InMemoryMailboxSessionMapperFactory mailboxSessionMapperFactory = new InMemoryMailboxSessionMapperFactory(clock); EventBus eventBus = this.eventBus.get(); StoreRightManager storeRightManager = new StoreRightManager(mailboxSessionMapperFactory, new UnionMailboxACLResolver(), eventBus); @@ -314,7 +315,6 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM InMemoryMessageId.Factory messageIdFactory = new InMemoryMessageId.Factory(); ThreadIdGuessingAlgorithm threadIdGuessingAlgorithm = new NaiveThreadIdGuessingAlgorithm(); - UpdatableTickingClock clock = new UpdatableTickingClock(Instant.now()); MailboxManagerPreInstanciationStage preInstanciationStage = new MailboxManagerPreInstanciationStage(mailboxSessionMapperFactory, sessionProvider); PreDeletionHooks hooks = createHooks(preInstanciationStage); diff --git a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndexTest.java b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndexTest.java index 3070f59872..ce34fbe26b 100644 --- a/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndexTest.java +++ b/mailbox/opensearch/src/test/java/org/apache/james/mailbox/opensearch/events/OpenSearchListeningMessageSearchIndexTest.java @@ -26,6 +26,7 @@ import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; +import java.time.Instant; import java.time.ZoneId; import java.util.Date; @@ -77,6 +78,7 @@ import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; import org.apache.james.mailbox.store.search.ListeningMessageSearchIndex; import org.apache.james.mailbox.store.search.ListeningMessageSearchIndexContract; +import org.apache.james.utils.UpdatableTickingClock; import org.awaitility.Awaitility; import org.awaitility.Durations; import org.awaitility.core.ConditionFactory; @@ -166,13 +168,15 @@ class OpenSearchListeningMessageSearchIndexTest { OpenSearchIndexer openSearchIndexer; OpenSearchSearcher openSearchSearcher; SessionProviderImpl sessionProvider; + UpdatableTickingClock clock; @RegisterExtension DockerOpenSearchExtension openSearch = new DockerOpenSearchExtension(); @BeforeEach void setup() throws Exception { - mapperFactory = new InMemoryMailboxSessionMapperFactory(); + clock = new UpdatableTickingClock(Instant.now()); + mapperFactory = new InMemoryMailboxSessionMapperFactory(clock); MessageToOpenSearchJson messageToOpenSearchJson = new MessageToOpenSearchJson( new DefaultTextExtractor(), diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java index ad1d9adeed..96a5d09ebc 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.mailbox.store.mail; +import java.time.Clock; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -54,11 +55,13 @@ public abstract class AbstractMessageMapper extends TransactionalMapper implemen protected final MailboxSession mailboxSession; private final UidProvider uidProvider; private final ModSeqProvider modSeqProvider; + protected final Clock clock; - public AbstractMessageMapper(MailboxSession mailboxSession, UidProvider uidProvider, ModSeqProvider modSeqProvider) { + public AbstractMessageMapper(MailboxSession mailboxSession, UidProvider uidProvider, ModSeqProvider modSeqProvider, Clock clock) { this.mailboxSession = mailboxSession; this.uidProvider = uidProvider; this.modSeqProvider = modSeqProvider; + this.clock = clock; } @Override diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java index d6e7f17b2b..249cece8dc 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/MailboxMessage.java @@ -108,6 +108,8 @@ public interface MailboxMessage extends Message, Comparable<MailboxMessage> { */ void setFlags(Flags flags); + void setSaveDate(Date saveDate); + /** * Creates a new flags instance populated * with the current flag data. diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java index e5caaa9bef..c5339ff690 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java @@ -208,7 +208,7 @@ public class SimpleMailboxMessage extends DelegatingMailboxMessage { private MessageUid uid; private final MailboxId mailboxId; private final ThreadId threadId; - private final Optional<Date> saveDate; + private Optional<Date> saveDate; private boolean answered; private boolean deleted; private boolean draft; @@ -319,6 +319,11 @@ public class SimpleMailboxMessage extends DelegatingMailboxMessage { this.uid = uid; } + @Override + public void setSaveDate(Date saveDate) { + this.saveDate = Optional.of(saveDate); + } + @Override public Optional<Date> getSaveDate() { return saveDate; diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java index cf66cdcd05..a1c2abd8fb 100644 --- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java @@ -24,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.time.Duration; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.List; @@ -52,9 +53,11 @@ import org.apache.james.mailbox.store.mail.MessageMapper.FetchType; import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; import org.apache.james.util.concurrency.ConcurrentTestRunner; +import org.apache.james.utils.UpdatableTickingClock; import org.assertj.core.data.MapEntry; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableList; @@ -84,6 +87,8 @@ public abstract class MessageIdMapperTest { protected abstract MapperProvider provideMapper(); + protected abstract UpdatableTickingClock updatableTickingClock(); + @BeforeEach void setUp() throws MailboxException { this.mapperProvider = provideMapper(); @@ -984,6 +989,52 @@ public abstract class MessageIdMapperTest { .build()); } + @Nested + class SaveDateTests { + @Test + void saveMessagesShouldSetNewSaveDate() throws MailboxException { + message1.setUid(mapperProvider.generateMessageUid()); + message1.setModSeq(mapperProvider.generateModSeq(benwaInboxMailbox)); + message1.setFlags(new Flags(Flag.SEEN)); + + message2.setUid(mapperProvider.generateMessageUid()); + message2.setModSeq(mapperProvider.generateModSeq(benwaInboxMailbox)); + + sut.save(message1); + updatableTickingClock().setInstant(updatableTickingClock().instant().plusSeconds(1000)); + sut.save(message2); + + MailboxMessage firstMessage = sut.find(ImmutableList.of(message1.getMessageId()), FetchType.METADATA).get(0); + MailboxMessage secondMessage = sut.find(ImmutableList.of(message2.getMessageId()), FetchType.METADATA).get(0); + + assertThat(firstMessage.getSaveDate()).isNotEqualTo(secondMessage.getSaveDate()); + } + + @Test + void copyInMailboxReactiveShouldSetNewSaveDate() throws MailboxException, InterruptedException { + message1.setUid(mapperProvider.generateMessageUid()); + message1.setModSeq(mapperProvider.generateModSeq(benwaInboxMailbox)); + message1.setFlags(new Flags(Flag.SEEN)); + sut.save(message1); + + MailboxMessage copy = sut.find(ImmutableList.of(message1.getMessageId()), FetchType.METADATA).get(0) + .copy(benwaWorkMailbox); + copy.setUid(mapperProvider.generateMessageUid()); + copy.setModSeq(mapperProvider.generateModSeq(benwaWorkMailbox)); + + updatableTickingClock().setInstant(updatableTickingClock().instant().plus(8, ChronoUnit.DAYS)); + + sut.copyInMailboxReactive(copy, benwaWorkMailbox).block(); + + List<MailboxMessage> messages = sut.find(ImmutableList.of(message1.getMessageId()), FetchType.METADATA); + MailboxMessage firstMessage = messages.get(0); + MailboxMessage secondMessage = messages.get(1); + + assertThat(firstMessage.getSaveDate()).isNotEqualTo(secondMessage.getSaveDate()); + } + } + + private Mailbox createMailbox(MailboxPath mailboxPath) throws MailboxException { return mailboxMapper.create(mailboxPath, UID_VALIDITY).block(); } diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java index 5503e4e782..f2b478d744 100644 --- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java @@ -42,6 +42,7 @@ import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.model.ByteContent; +import org.apache.james.mailbox.model.Content; import org.apache.james.mailbox.model.Mailbox; import org.apache.james.mailbox.model.MailboxCounters; import org.apache.james.mailbox.model.MailboxPath; @@ -59,8 +60,10 @@ import org.apache.james.mailbox.store.mail.model.MapperProvider.Capabilities; import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; import org.apache.james.util.concurrency.ConcurrentTestRunner; +import org.apache.james.utils.UpdatableTickingClock; import org.junit.Assume; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableList; @@ -94,6 +97,8 @@ public abstract class MessageMapperTest { protected abstract MapperProvider createMapperProvider(); + protected abstract UpdatableTickingClock updatableTickingClock(); + @BeforeEach void setUp() throws Exception { this.mapperProvider = createMapperProvider(); @@ -1220,6 +1225,97 @@ public abstract class MessageMapperTest { .containsOnly(message1.getUid(), message5.getUid()); } + @Nested + class SaveDateTests { + @Test + void addMessageShouldSetNewSaveDate() throws MailboxException { + MailboxMessage messageWithoutSaveDate = createMessage(Optional.empty()); + + MessageMetaData messageMetaData = messageMapper.add(benwaInboxMailbox, messageWithoutSaveDate); + + assertThat(messageMetaData.getSaveDate()).isPresent(); + } + + @Test + void deleteMessageShouldReturnMetaDataContainsSaveDate() throws MailboxException { + MessageMetaData toBeDeletedMessage = messageMapper.add(benwaInboxMailbox, createMessage(Optional.empty())); + + assertThat(messageMapper.deleteMessages(benwaInboxMailbox, List.of(toBeDeletedMessage.getUid())) + .values() + .stream() + .allMatch(messageMetaData -> messageMetaData.getSaveDate().equals(toBeDeletedMessage.getSaveDate()))) + .isTrue(); + } + + @Test + void copyMessageShouldSetNewSaveDate() throws MailboxException { + MailboxMessage originalMessage = createMessage(Optional.of(new Date())); + MessageUid uid = messageMapper.add(benwaInboxMailbox, originalMessage).getUid(); + + updatableTickingClock().setInstant(updatableTickingClock().instant().plusSeconds(1000)); + + MessageMetaData copiedMessageMetaData = messageMapper.copy(benwaInboxMailbox, + messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(uid), FetchType.METADATA, 1).next()); + + assertThat(copiedMessageMetaData.getSaveDate()).isNotEqualTo(originalMessage.getSaveDate()); + } + + @Test + void copyListOfMessagesShouldSetNewSaveDate() throws MailboxException { + MailboxMessage originalMessage = createMessage(Optional.of(new Date())); + MessageUid uid = messageMapper.add(benwaInboxMailbox, originalMessage).getUid(); + + updatableTickingClock().setInstant(updatableTickingClock().instant().plusSeconds(1000)); + + List<MessageMetaData> copiedMessageMetaData = messageMapper.copy(benwaInboxMailbox, + List.of(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(uid), FetchType.METADATA, 1).next())); + + assertThat(copiedMessageMetaData.get(0).getSaveDate()).isNotEqualTo(originalMessage.getSaveDate()); + } + + @Test + void moveMessageShouldSetNewSaveDate() throws MailboxException { + MailboxMessage originalMessage = createMessage(Optional.of(new Date())); + MessageUid uid = messageMapper.add(benwaInboxMailbox, originalMessage).getUid(); + + updatableTickingClock().setInstant(updatableTickingClock().instant().plusSeconds(1000)); + + MessageMetaData movedMessageMetaData = messageMapper.move(benwaInboxMailbox, + messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(uid), FetchType.METADATA, 1).next()); + + assertThat(movedMessageMetaData.getSaveDate()).isNotEqualTo(originalMessage.getSaveDate()); + } + + @Test + void moveListOfMessagesShouldSetNewSaveDate() throws MailboxException { + MailboxMessage originalMessage = createMessage(Optional.of(new Date())); + MessageUid uid = messageMapper.add(benwaInboxMailbox, originalMessage).getUid(); + + updatableTickingClock().setInstant(updatableTickingClock().instant().plusSeconds(1000)); + + List<MessageMetaData> movedMessageMetaData = messageMapper.move(benwaInboxMailbox, + List.of(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.one(uid), FetchType.METADATA, 1).next())); + + assertThat(movedMessageMetaData.get(0).getSaveDate()).isNotEqualTo(originalMessage.getSaveDate()); + } + + private SimpleMailboxMessage createMessage(Optional<Date> saveDate) throws MailboxException { + Content content = new ByteContent("Subject: messagePropertiesShouldBeStoredWhenDuplicateEntries \n\nBody\n.\n".getBytes()); + return SimpleMailboxMessage.builder() + .messageId(mapperProvider.generateMessageId()) + .mailboxId(benwaInboxMailbox.getMailboxId()) + .threadId(ThreadId.fromBaseMessageId(mapperProvider.generateMessageId())) + .internalDate(new Date()) + .saveDate(saveDate) + .bodyStartOctet(16) + .size(content.size()) + .content(content) + .flags(new Flags()) + .properties(new PropertyBuilder()) + .build(); + } + } + private List<MessageUid> markThenPerformRetrieveMessagesMarkedForDeletion(MessageRange range) throws MailboxException { messageMapper.updateFlags(benwaInboxMailbox, message1.getUid(), new FlagsUpdateCalculator(new Flags(Flags.Flag.DELETED), FlagsUpdateMode.REPLACE)); messageMapper.updateFlags(benwaInboxMailbox, message4.getUid(), new FlagsUpdateCalculator(new Flags(Flags.Flag.DELETED), FlagsUpdateMode.REPLACE)); --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org