This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 73d99ac5b2dea05849417d9a1e5535a594378a5e Author: TungTV <vtt...@linagora.com> AuthorDate: Fri Sep 13 14:49:38 2024 +0700 JAMES-4071 - Task fix inconsistencies mailbox flags: messagedeleted, mailboxRecents --- .../SolveMailboxFlagInconsistenciesService.java | 181 +++++++++++++++++ ...nconsistenciesTaskAdditionalInformationDTO.java | 67 +++++++ .../SolveMailboxFlagInconsistenciesTaskDTO.java | 49 +++++ .../task/SolveMailboxFlagInconsistencyTask.java | 85 ++++++++ ...SolveMailboxFlagInconsistenciesServiceTest.java | 220 +++++++++++++++++++++ ...lboxFlagInconsistencyTaskSerializationTest.java | 65 ++++++ ...assandraConsistencyTaskSerializationModule.java | 20 ++ .../webadmin/InconsistencySolvingRoutesModule.java | 33 ++++ .../rabbitmq/ConsistencyTasksIntegrationTest.java | 80 ++++++++ ...MessageDeletedInconsistenciesRequestToTask.java | 42 ++++ ...eMessageRecentInconsistenciesRequestToTask.java | 42 ++++ 11 files changed, 884 insertions(+) diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesService.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesService.java new file mode 100644 index 0000000000..1cf8d3b614 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesService.java @@ -0,0 +1,181 @@ +/**************************************************************** + * 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.task; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Predicate; + +import jakarta.inject.Inject; +import jakarta.mail.Flags; + +import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.cassandra.ids.CassandraId; +import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMessageMetadata; +import org.apache.james.mailbox.model.MessageRange; +import org.apache.james.task.Task; +import org.apache.james.util.streams.Limit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; + +import reactor.core.publisher.Mono; + +public class SolveMailboxFlagInconsistenciesService { + + public enum TargetFlag { + RECENT, + DELETED; + + public static TargetFlag from(String value) { + return TargetFlag.valueOf(value); + } + } + + public record Context(AtomicLong processedMailboxEntries, + ConcurrentLinkedDeque<CassandraId> errors) { + + record Snapshot(long processedMailboxEntries, ImmutableList<CassandraId> errors) { + } + + public Context() { + this(new AtomicLong(0), new ConcurrentLinkedDeque<>()); + } + + void incrementProcessedMailboxEntries() { + processedMailboxEntries.incrementAndGet(); + } + + void addError(CassandraId cassandraId) { + errors.add(cassandraId); + } + + Snapshot snapshot() { + return new Snapshot(processedMailboxEntries.get(), ImmutableList.copyOf(errors)); + } + } + + public interface SolveMessageFlagInconsistencyStrategy { + Predicate<CassandraMessageMetadata> filterOutFlagInconsistencies(); + + Mono<Void> removeAllByMailboxId(CassandraId cassandraId); + + Mono<Void> addEntry(CassandraId cassandraId, List<MessageUid> uids); + + TargetFlag targetFlag(); + } + + public record SolveMailboxDeletedFlagInconsistenciesStrategy(CassandraDeletedMessageDAO deletedMessageDAO) implements SolveMessageFlagInconsistencyStrategy { + @Override + public Predicate<CassandraMessageMetadata> filterOutFlagInconsistencies() { + return metaData -> metaData.getComposedMessageId().getFlags().contains(Flags.Flag.DELETED); + } + + @Override + public Mono<Void> removeAllByMailboxId(CassandraId cassandraId) { + return deletedMessageDAO.removeAll(cassandraId); + } + + @Override + public Mono<Void> addEntry(CassandraId cassandraId, List<MessageUid> uids) { + return deletedMessageDAO.addDeleted(cassandraId, uids); + } + + @Override + public TargetFlag targetFlag() { + return TargetFlag.DELETED; + } + } + + public record SoleMailboxRecentFlagInconsistenciesStrategy(CassandraMailboxRecentsDAO mailboxRecentDAO) implements SolveMessageFlagInconsistencyStrategy { + @Override + public Predicate<CassandraMessageMetadata> filterOutFlagInconsistencies() { + return metaData -> metaData.getComposedMessageId().getFlags().contains(Flags.Flag.RECENT); + } + + @Override + public Mono<Void> removeAllByMailboxId(CassandraId cassandraId) { + return mailboxRecentDAO.delete(cassandraId); + } + + @Override + public Mono<Void> addEntry(CassandraId cassandraId, List<MessageUid> uids) { + return mailboxRecentDAO.addToRecent(cassandraId, uids); + } + + @Override + public TargetFlag targetFlag() { + return TargetFlag.RECENT; + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(SolveMailboxFlagInconsistenciesService.class); + + private final Set<SolveMessageFlagInconsistencyStrategy> fixInconsistenciesStrategies; + private final CassandraMessageIdDAO messageIdDAO; + private final CassandraMailboxDAO mailboxDAO; + + @Inject + public SolveMailboxFlagInconsistenciesService(Set<SolveMessageFlagInconsistencyStrategy> fixInconsistenciesStrategies, + CassandraMessageIdDAO messageIdDAO, + CassandraMailboxDAO mailboxDAO) { + this.messageIdDAO = messageIdDAO; + this.fixInconsistenciesStrategies = fixInconsistenciesStrategies; + this.mailboxDAO = mailboxDAO; + } + + public Mono<Task.Result> fixInconsistencies(Context context, TargetFlag targetFlag) { + SolveMessageFlagInconsistencyStrategy fixInconsistencyStrategy = getFixInconsistencyStrategy(targetFlag); + return mailboxDAO.retrieveAllMailboxes() + .concatMap(mailbox -> fixDeletedMessagesInconsistencyPerMailbox(fixInconsistencyStrategy, (CassandraId) mailbox.getMailboxId(), context) + .doOnNext(any -> context.incrementProcessedMailboxEntries())) + .reduce(Task.Result.COMPLETED, Task::combine); + } + + private Mono<Task.Result> fixDeletedMessagesInconsistencyPerMailbox(SolveMessageFlagInconsistencyStrategy fixInconsistenciesStrategy, CassandraId cassandraId, Context context) { + return fixInconsistenciesStrategy.removeAllByMailboxId(cassandraId) + .then(messageIdDAO.retrieveMessages(cassandraId, MessageRange.all(), Limit.unlimited()) + .filter(fixInconsistenciesStrategy.filterOutFlagInconsistencies()) + .map(metadata -> metadata.getComposedMessageId().getComposedMessageId().getUid()) + .collectList() + .filter(uids -> !uids.isEmpty()) + .flatMap(uids -> fixInconsistenciesStrategy.addEntry(cassandraId, uids))) + .thenReturn(Task.Result.COMPLETED) + .onErrorResume(e -> { + LOGGER.error("Error while fixing inconsistencies for mailbox {}", cassandraId, e); + context.addError(cassandraId); + return Mono.just(Task.Result.PARTIAL); + }); + } + + private SolveMessageFlagInconsistencyStrategy getFixInconsistencyStrategy(TargetFlag targetFlag) { + return fixInconsistenciesStrategies.stream() + .filter(strategy -> strategy.targetFlag() == targetFlag) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("No strategy found for " + targetFlag)); + } +} diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.java new file mode 100644 index 0000000000..44cf6dce89 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.java @@ -0,0 +1,67 @@ +/**************************************************************** + * 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.task; + +import java.time.Instant; + +import org.apache.james.json.DTOModule; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistencyTask.Details; +import org.apache.james.server.task.json.dto.AdditionalInformationDTO; +import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.collect.ImmutableList; + +public record SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO( + @JsonProperty("timestamp") Instant timestamp, + @JsonProperty("type") String type, + @JsonProperty("processedMailboxEntries") long processedMailboxEntries, + @JsonProperty("errors") ImmutableList<String> errors, + @JsonProperty("targetFlag") String targetFlag) implements AdditionalInformationDTO { + + + public static AdditionalInformationDTOModule<Details, SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO> module() { + return DTOModule.forDomainObject(Details.class) + .convertToDTO(SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.class) + .toDomainObjectConverter(SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO::toDomainObject) + .toDTOConverter((details, typeAsObject) -> new SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO( + details.timestamp(), + typeAsObject, + details.processedMailboxEntries(), + details.errors(), + details.targetFlag())) + .typeName(SolveMailboxFlagInconsistencyTask.TASK_TYPE.asString()) + .withFactory(AdditionalInformationDTOModule::new); + } + + @Override + public String getType() { + return type; + } + + @Override + public Instant getTimestamp() { + return timestamp; + } + + private Details toDomainObject() { + return new Details(timestamp, processedMailboxEntries, errors, targetFlag); + } +} diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskDTO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskDTO.java new file mode 100644 index 0000000000..d0776edeb4 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesTaskDTO.java @@ -0,0 +1,49 @@ +/**************************************************************** + * 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.task; + +import org.apache.james.json.DTOModule; +import org.apache.james.server.task.json.dto.TaskDTO; +import org.apache.james.server.task.json.dto.TaskDTOModule; +import org.apache.james.task.TaskType; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public record SolveMailboxFlagInconsistenciesTaskDTO(@JsonProperty("type") String type, + @JsonProperty("flagName") String flagName) implements TaskDTO { + + public static final String TYPE = "solve-mailbox-flag-inconsistencies"; + public static final TaskType TASK_TYPE = TaskType.of(TYPE); + + public static TaskDTOModule<SolveMailboxFlagInconsistencyTask, SolveMailboxFlagInconsistenciesTaskDTO> module(SolveMailboxFlagInconsistenciesService solveMailboxFlagInconsistenciesService) { + return DTOModule + .forDomainObject(SolveMailboxFlagInconsistencyTask.class) + .convertToDTO(SolveMailboxFlagInconsistenciesTaskDTO.class) + .toDomainObjectConverter(dto -> new SolveMailboxFlagInconsistencyTask(solveMailboxFlagInconsistenciesService, dto.flagName())) + .toDTOConverter((task, type) -> new SolveMailboxFlagInconsistenciesTaskDTO(type, task.targetFlag())) + .typeName(TYPE) + .withFactory(TaskDTOModule::new); + } + + @Override + public String getType() { + return type; + } +} diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTask.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTask.java new file mode 100644 index 0000000000..49dbc730d2 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTask.java @@ -0,0 +1,85 @@ +/**************************************************************** + * 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.task; + +import java.time.Clock; +import java.time.Instant; +import java.util.Optional; + +import org.apache.james.mailbox.cassandra.ids.CassandraId; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.Context; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.TargetFlag; +import org.apache.james.task.Task; +import org.apache.james.task.TaskExecutionDetails; +import org.apache.james.task.TaskType; + +import com.google.common.collect.ImmutableList; + +public class SolveMailboxFlagInconsistencyTask implements Task { + + public record Details(Instant timestamp, + long processedMailboxEntries, + ImmutableList<String> errors, + String targetFlag) implements TaskExecutionDetails.AdditionalInformation { + } + + public static final String TYPE = "solve-mailbox-flag-inconsistencies"; + public static final TaskType TASK_TYPE = TaskType.of(TYPE); + + public SolveMailboxFlagInconsistencyTask(SolveMailboxFlagInconsistenciesService service, String targetFlag) { + this(service, Optional.ofNullable(TargetFlag.from(targetFlag)) + .orElseThrow(() -> new IllegalArgumentException("Invalid target flag: " + targetFlag))); + } + + public SolveMailboxFlagInconsistencyTask(SolveMailboxFlagInconsistenciesService service, TargetFlag targetFlag) { + this.context = new Context(); + this.service = service; + this.targetFlag = targetFlag; + } + + private final Context context; + private final SolveMailboxFlagInconsistenciesService service; + private final TargetFlag targetFlag; + + @Override + public Result run() throws InterruptedException { + return service.fixInconsistencies(context, targetFlag).block(); + } + + @Override + public TaskType type() { + return TASK_TYPE; + } + + @Override + public Optional<TaskExecutionDetails.AdditionalInformation> details() { + Context.Snapshot snapshot = context.snapshot(); + + return Optional.of(new Details(Clock.systemUTC().instant(), + snapshot.processedMailboxEntries(), + ImmutableList.copyOf(snapshot.errors().stream() + .map(CassandraId::serialize).toList()), + targetFlag.name())); + } + + public String targetFlag() { + return targetFlag.name(); + } +} diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesServiceTest.java new file mode 100644 index 0000000000..3d568fba6f --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistenciesServiceTest.java @@ -0,0 +1,220 @@ +/**************************************************************** + * 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.task; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Date; +import java.util.Optional; +import java.util.Set; + +import jakarta.mail.Flags; + +import org.apache.james.backends.cassandra.CassandraCluster; +import org.apache.james.backends.cassandra.CassandraClusterExtension; +import org.apache.james.backends.cassandra.components.CassandraModule; +import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule; +import org.apache.james.blob.api.PlainBlobId; +import org.apache.james.core.Username; +import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.ModSeq; +import org.apache.james.mailbox.cassandra.ids.CassandraId; +import org.apache.james.mailbox.cassandra.ids.CassandraMessageId; +import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMessageMetadata; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.Context; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.SolveMailboxDeletedFlagInconsistenciesStrategy; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.TargetFlag; +import org.apache.james.mailbox.cassandra.modules.CassandraAclModule; +import org.apache.james.mailbox.cassandra.modules.CassandraDeletedMessageModule; +import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule; +import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule; +import org.apache.james.mailbox.model.ComposedMessageId; +import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; +import org.apache.james.mailbox.model.Mailbox; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.MessageRange; +import org.apache.james.mailbox.model.ThreadId; +import org.apache.james.mailbox.model.UidValidity; +import org.apache.james.task.Task; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class SolveMailboxFlagInconsistenciesServiceTest { + private static final UidValidity UID_VALIDITY_1 = UidValidity.of(145); + private static final Username USER = Username.of("user"); + private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER, "abc"); + private static final CassandraId CASSANDRA_ID_1 = CassandraId.timeBased(); + private static final MessageUid MESSAGE_UID_1 = MessageUid.of(1); + private static final MessageUid MESSAGE_UID_2 = MessageUid.of(2); + private static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UID_VALIDITY_1, CASSANDRA_ID_1); + private static final CassandraId CASSANDRA_ID_2 = CassandraId.timeBased(); + private static final Mailbox MAILBOX_2 = new Mailbox(MAILBOX_PATH, UID_VALIDITY_1, CASSANDRA_ID_2); + private static final PlainBlobId HEADER_BLOB_ID_1 = new PlainBlobId.Factory().of("abc"); + private static final CassandraMessageId MESSAGE_ID_1 = new CassandraMessageId.Factory().fromString("d2bee791-7e63-11ea-883c-95b84008f979"); + private static final CassandraMessageId MESSAGE_ID_2 = new CassandraMessageId.Factory().fromString("eeeeeeee-7e63-11ea-883c-95b84008f979"); + + + @RegisterExtension + static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension( + CassandraModule.aggregateModules( + CassandraSchemaVersionModule.MODULE, + CassandraDeletedMessageModule.MODULE, + CassandraMessageModule.MODULE, + CassandraMailboxModule.MODULE, + CassandraAclModule.MODULE)); + + SolveMailboxFlagInconsistenciesService testee; + CassandraMailboxDAO mailboxDAO; + CassandraMessageIdDAO messageIdDAO; + CassandraDeletedMessageDAO deletedMessageDAO; + + @BeforeEach + void setUp(CassandraCluster cassandra) { + messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new PlainBlobId.Factory()); + deletedMessageDAO = new CassandraDeletedMessageDAO(cassandra.getConf()); + mailboxDAO = new CassandraMailboxDAO( + cassandra.getConf(), + cassandra.getTypesProvider()); + + SolveMailboxDeletedFlagInconsistenciesStrategy inconsistenciesStrategy = new SolveMailboxDeletedFlagInconsistenciesStrategy(deletedMessageDAO); + testee = new SolveMailboxFlagInconsistenciesService(Set.of(inconsistenciesStrategy), messageIdDAO, mailboxDAO); + } + + @Test + void fixInconsistenciesShouldReturnCompletedWhenNoData() { + assertThat(testee.fixInconsistencies(new Context(), TargetFlag.DELETED).block()) + .isEqualTo(Task.Result.COMPLETED); + } + + @Test + void fixMessageInconsistenciesShouldReturnCompletedWhenConsistentData() { + MessageUid messageUid = MessageUid.of(1); + CassandraMessageId messageId1 = new CassandraMessageId.Factory().generate(); + + mailboxDAO.save(MAILBOX).block(); + messageIdDAO.insert(CassandraMessageMetadata.builder() + .ids(ComposedMessageIdWithMetaData.builder() + .composedMessageId(new ComposedMessageId(CASSANDRA_ID_1, messageId1, messageUid)) + .flags(new Flags(Flags.Flag.DELETED)) + .modSeq(ModSeq.of(1)) + .threadId(ThreadId.fromBaseMessageId(messageId1)) + .build()) + .internalDate(new Date()) + .bodyStartOctet(18L) + .headerContent(Optional.of(HEADER_BLOB_ID_1)) + .size(36L) + .build()) + .block(); + + deletedMessageDAO.addDeleted(CASSANDRA_ID_1, messageUid).block(); + + Context context = new Context(); + assertThat(testee.fixInconsistencies(context, TargetFlag.DELETED).block()) + .isEqualTo(Task.Result.COMPLETED); + + assertThat(context.snapshot().processedMailboxEntries()).isEqualTo(1); + } + + @Test + void fixMessageInconsistenciesShouldFixInconsistency() { + // Given inconsistent data + mailboxDAO.save(MAILBOX).block(); + messageIdDAO.insert(CassandraMessageMetadata.builder() + .ids(ComposedMessageIdWithMetaData.builder() + .composedMessageId(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MESSAGE_UID_1)) + .flags(new Flags(Flags.Flag.DELETED)) + .modSeq(ModSeq.of(1)) + .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_1)) + .build()) + .internalDate(new Date()) + .bodyStartOctet(18L) + .headerContent(Optional.of(HEADER_BLOB_ID_1)) + .size(36L) + .build()) + .block(); + + deletedMessageDAO.addDeleted(CASSANDRA_ID_1, MESSAGE_UID_2).block(); + + // When fixing inconsistencies + assertThat(testee.fixInconsistencies(new Context(), TargetFlag.DELETED).block()) + .isEqualTo(Task.Result.COMPLETED); + + // Then the inconsistency should be fixed + // CASSANDRA_ID_1 - MESSAGE_UID_2 should be removed + // CASSANDRA_ID_1 - MESSAGE_UID_1 should be added + assertThat(deletedMessageDAO.retrieveDeletedMessage(CASSANDRA_ID_1, MessageRange.all()).collectList().block()) + .contains(MESSAGE_UID_1); + } + + @Test + void fixInconsistenciesShouldWorkOnSeveralMailbox() { + mailboxDAO.save(MAILBOX).block(); + mailboxDAO.save(MAILBOX_2).block(); + + messageIdDAO.insert(CassandraMessageMetadata.builder() + .ids(ComposedMessageIdWithMetaData.builder() + .composedMessageId(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MESSAGE_UID_1)) + .flags(new Flags(Flags.Flag.DELETED)) + .modSeq(ModSeq.of(1)) + .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_1)) + .build()) + .internalDate(new Date()) + .bodyStartOctet(18L) + .headerContent(Optional.of(HEADER_BLOB_ID_1)) + .size(36L) + .build()) + .block(); + + messageIdDAO.insert(CassandraMessageMetadata.builder() + .ids(ComposedMessageIdWithMetaData.builder() + .composedMessageId(new ComposedMessageId(CASSANDRA_ID_2, MESSAGE_ID_2, MESSAGE_UID_2)) + .flags(new Flags(Flags.Flag.DELETED)) + .modSeq(ModSeq.of(1)) + .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_2)) + .build()) + .internalDate(new Date()) + .bodyStartOctet(18L) + .headerContent(Optional.of(HEADER_BLOB_ID_1)) + .size(36L) + .build()) + .block(); + + MessageUid messageUid3 = MessageUid.of(3); + deletedMessageDAO.addDeleted(CASSANDRA_ID_1, messageUid3).block(); + deletedMessageDAO.addDeleted(CASSANDRA_ID_2, MESSAGE_UID_2).block(); + + // When fixing inconsistencies + assertThat(testee.fixInconsistencies(new Context(), TargetFlag.DELETED).block()) + .isEqualTo(Task.Result.COMPLETED); + + // Then the inconsistency should be fixed + // CASSANDRA_ID_1 - messageUid3 should be removed + // CASSANDRA_ID_1 - messageUid1 should be added + // CASSANDRA_ID_2 - messageUid2 should be kept + assertThat(deletedMessageDAO.retrieveDeletedMessage(CASSANDRA_ID_1, MessageRange.all()).collectList().block()) + .contains(MESSAGE_UID_1); + assertThat(deletedMessageDAO.retrieveDeletedMessage(CASSANDRA_ID_2, MessageRange.all()).collectList().block()) + .contains(MESSAGE_UID_2); + } +} \ No newline at end of file diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTaskSerializationTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTaskSerializationTest.java new file mode 100644 index 0000000000..6c4305695a --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxFlagInconsistencyTaskSerializationTest.java @@ -0,0 +1,65 @@ +/**************************************************************** + * 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.task; + +import static org.mockito.Mockito.mock; + +import java.time.Instant; + +import org.apache.james.JsonSerializationVerifier; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.TargetFlag; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistencyTask.Details; +import org.junit.jupiter.api.Test; + +import com.google.common.collect.ImmutableList; + +public class SolveMailboxFlagInconsistencyTaskSerializationTest { + + private static final SolveMailboxFlagInconsistenciesService SERVICE = mock(SolveMailboxFlagInconsistenciesService.class); + + private static final Instant INSTANT = Instant.parse("2007-12-03T10:15:30.00Z"); + + @Test + void taskShouldBeSerializable() throws Exception { + JsonSerializationVerifier.dtoModule(SolveMailboxFlagInconsistenciesTaskDTO.module(SERVICE)) + .bean(new SolveMailboxFlagInconsistencyTask(SERVICE, TargetFlag.RECENT)) + .json("{" + + " \"type\":\"solve-mailbox-flag-inconsistencies\"," + + " \"flagName\":\"RECENT\"" + + "}") + .verify(); + } + + @Test + void additionalInformationShouldBeSerializable() throws Exception { + JsonSerializationVerifier.dtoModule(SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.module()) + .bean(new Details(INSTANT, 2, + ImmutableList.of("d2bee791-7e63-11ea-883c-95b84008f979", "ffffffff-7e63-11ea-883c-95b84008f979"), + TargetFlag.DELETED.name())) + .json("{" + + " \"errors\": [\"d2bee791-7e63-11ea-883c-95b84008f979\", \"ffffffff-7e63-11ea-883c-95b84008f979\"]," + + " \"processedMailboxEntries\": 2," + + " \"timestamp\": \"2007-12-03T10:15:30Z\"," + + " \"targetFlag\": \"DELETED\"," + + " \"type\": \"solve-mailbox-flag-inconsistencies\"" + + "}") + .verify(); + } +} diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/CassandraConsistencyTaskSerializationModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/CassandraConsistencyTaskSerializationModule.java index 735b4ace9f..d65aba895b 100644 --- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/CassandraConsistencyTaskSerializationModule.java +++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/CassandraConsistencyTaskSerializationModule.java @@ -27,6 +27,9 @@ import org.apache.james.mailbox.cassandra.mail.task.MailboxMergingTaskRunner; import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService; import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersTaskAdditionalInformationDTO; import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersTaskDTO; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesTaskDTO; import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesService; import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesTaskAdditionalInformationDTO; import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesTaskDTO; @@ -146,4 +149,21 @@ public class CassandraConsistencyTaskSerializationModule extends AbstractModule public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends AdditionalInformationDTO> webAdminSolveMessageInconsistenciesAdditionalInformation() { return SolveMessageInconsistenciesTaskAdditionalInformationDTO.module(); } + + @ProvidesIntoSet + public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends AdditionalInformationDTO> solveMailboxFlagInconsistenciesAdditionalInformation() { + return SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.module(); + } + + @Named(DTOModuleInjections.WEBADMIN_DTO) + @ProvidesIntoSet + public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends AdditionalInformationDTO> webAdminSolveMailboxFlagInconsistenciesAdditionalInformation() { + return SolveMailboxFlagInconsistenciesTaskAdditionalInformationDTO.module(); + } + + @ProvidesIntoSet + public TaskDTOModule<? extends Task, ? extends TaskDTO> solveMailboxFlagInconsistenciesTask(SolveMailboxFlagInconsistenciesService solveMailboxFlagInconsistenciesService) { + return SolveMailboxFlagInconsistenciesTaskDTO.module(solveMailboxFlagInconsistenciesService); + } + } diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java index f5197e15fb..1fa5b5db8d 100644 --- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java +++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java @@ -19,7 +19,10 @@ package org.apache.james.modules.webadmin; +import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO; +import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO; import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService; import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesService; import org.apache.james.mailbox.cassandra.mail.task.SolveMessageInconsistenciesService; import org.apache.james.webadmin.Routes; @@ -28,12 +31,16 @@ import org.apache.james.webadmin.routes.MailboxesRoutes; import org.apache.james.webadmin.routes.MessagesRoutes; import org.apache.james.webadmin.routes.RecomputeMailboxCountersRequestToTask; import org.apache.james.webadmin.routes.SolveMailboxInconsistenciesRequestToTask; +import org.apache.james.webadmin.routes.SolveMessageDeletedInconsistenciesRequestToTask; import org.apache.james.webadmin.routes.SolveMessageInconsistenciesRequestToTask; +import org.apache.james.webadmin.routes.SolveMessageRecentInconsistenciesRequestToTask; import org.apache.james.webadmin.service.CassandraMappingsService; import org.apache.james.webadmin.tasks.TaskFromRequestRegistry; import com.google.inject.AbstractModule; +import com.google.inject.Provides; import com.google.inject.Scopes; +import com.google.inject.Singleton; import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Names; @@ -60,6 +67,8 @@ public class InconsistencySolvingRoutesModule extends AbstractModule { multiBinder.addBinding().to(SolveMailboxInconsistenciesRequestToTask.class); multiBinder.addBinding().to(RecomputeMailboxCountersRequestToTask.class); + multiBinder.addBinding().to(SolveMessageDeletedInconsistenciesRequestToTask.class); + multiBinder.addBinding().to(SolveMessageRecentInconsistenciesRequestToTask.class); } } @@ -75,10 +84,34 @@ public class InconsistencySolvingRoutesModule extends AbstractModule { } } + public static class MailboxFlagInconsistencyModule extends AbstractModule { + + @Override + public void configure() { + Multibinder<SolveMailboxFlagInconsistenciesService.SolveMessageFlagInconsistencyStrategy> solveMailboxFlagInconsistenciesServiceMultibinder = Multibinder.newSetBinder(binder(), SolveMailboxFlagInconsistenciesService.SolveMessageFlagInconsistencyStrategy.class); + solveMailboxFlagInconsistenciesServiceMultibinder.addBinding().to(SolveMailboxFlagInconsistenciesService.SolveMailboxDeletedFlagInconsistenciesStrategy.class); + solveMailboxFlagInconsistenciesServiceMultibinder.addBinding().to(SolveMailboxFlagInconsistenciesService.SoleMailboxRecentFlagInconsistenciesStrategy.class); + } + + @Provides + @Singleton + public SolveMailboxFlagInconsistenciesService.SolveMailboxDeletedFlagInconsistenciesStrategy provideSolveMailboxDeletedFlagInconsistenciesStrategy(CassandraDeletedMessageDAO dao) { + return new SolveMailboxFlagInconsistenciesService.SolveMailboxDeletedFlagInconsistenciesStrategy(dao); + } + + + @Provides + @Singleton + public SolveMailboxFlagInconsistenciesService.SoleMailboxRecentFlagInconsistenciesStrategy provideSoleMailboxRecentFlagInconsistenciesStrategy(CassandraMailboxRecentsDAO dao) { + return new SolveMailboxFlagInconsistenciesService.SoleMailboxRecentFlagInconsistenciesStrategy(dao); + } + } + @Override protected void configure() { install(new SolveRRTInconsistenciesModules()); install(new SolveMailboxInconsistenciesModules()); install(new SolveMessageInconsistenciesModules()); + install(new MailboxFlagInconsistencyModule()); } } diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java index 585cd0ce69..c0eef95628 100644 --- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java +++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java @@ -34,6 +34,7 @@ import static org.apache.james.webadmin.Constants.SEPARATOR; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import java.io.IOException; @@ -544,4 +545,83 @@ class ConsistencyTasksIntegrationTest { assertThat(testIMAPClient.readFirstMessage()).contains(MESSAGE); } + + @Test + void shouldExposeSolveMessagesDeletedInconsistency(GuiceJamesServer server) throws Exception { + String solveInconsistenciesTaskId = with() + .queryParam("task", "SolveMessageDeletedInconsistencies") + .post("/mailboxes") + .jsonPath() + .get("taskId"); + + with() + .basePath(TasksRoutes.BASE) + .get(solveInconsistenciesTaskId + "/await") + .then() + .body("status", is("completed")) + .body("type", is("solve-mailbox-flag-inconsistencies")) + .body("additionalInformation.processedMailboxEntries", is(0)) + .body("additionalInformation.targetFlag", is("DELETED")) + .body("additionalInformation.errors", hasSize(0)); + } + + @Test + void shouldSolveMessagesDeletedInconsistency(GuiceJamesServer server) throws Exception { + // Given a message and a messageDeleted inconsistency + smtpMessageSender.connect(LOCALHOST_IP, server.getProbe(SmtpGuiceProbe.class).getSmtpPort()) + .sendMessageWithHeaders(ALICE.asString(), BOB.asString(), MESSAGE); + + Thread.sleep(1000); + TestingSession testingSession = server.getProbe(TestingSessionProbe.class).getTestingSession(); + + testIMAPClient.connect(JAMES_SERVER_HOST, server.getProbe(ImapGuiceProbe.class).getImapPort()) + .login(BOB, BOB_PASSWORD) + .select(MailboxConstants.INBOX) + .awaitMessage(Awaitility.await()); + + testIMAPClient.setFlagsForAllMessagesInMailbox("\\Deleted"); + + // Make sure messageDeleted inconsistency is present + testingSession.execute("TRUNCATE messagedeleted"); + + // When we run solveInconsistenciesTask + String solveInconsistenciesTaskId = with() + .queryParam("task", "SolveMessageDeletedInconsistencies") + .post("/mailboxes") + .jsonPath() + .get("taskId"); + + // Then task details should be as expected + with() + .basePath(TasksRoutes.BASE) + .get(solveInconsistenciesTaskId + "/await") + .then() + .body("status", is("completed")) + .body("type", is("solve-mailbox-flag-inconsistencies")) + .body("additionalInformation.targetFlag", is("DELETED")) + .body("additionalInformation.processedMailboxEntries", is(1)); + + // And messageDeleted inconsistency should be solved + assertThat(testingSession.execute("SELECT * FROM messageDeleted").getAvailableWithoutFetching()) + .isEqualTo(1); + } + + @Test + void shouldExposeSolveMessagesRecentInconsistency(GuiceJamesServer server) throws Exception { + String solveInconsistenciesTaskId = with() + .queryParam("task", "SolveMessageRecentInconsistencies") + .post("/mailboxes") + .jsonPath() + .get("taskId"); + + with() + .basePath(TasksRoutes.BASE) + .get(solveInconsistenciesTaskId + "/await") + .then() + .body("status", is("completed")) + .body("type", is("solve-mailbox-flag-inconsistencies")) + .body("additionalInformation.targetFlag", is("RECENT")) + .body("additionalInformation.processedMailboxEntries", is(0)) + .body("additionalInformation.errors", hasSize(0)); + } } diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageDeletedInconsistenciesRequestToTask.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageDeletedInconsistenciesRequestToTask.java new file mode 100644 index 0000000000..044649ec4d --- /dev/null +++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageDeletedInconsistenciesRequestToTask.java @@ -0,0 +1,42 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.routes; + + +import jakarta.inject.Inject; + +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.TargetFlag; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistencyTask; +import org.apache.james.webadmin.tasks.TaskFromRequestRegistry; +import org.apache.james.webadmin.tasks.TaskRegistrationKey; + +public class SolveMessageDeletedInconsistenciesRequestToTask extends TaskFromRequestRegistry.TaskRegistration { + private static final TaskRegistrationKey REGISTRATION_KEY = TaskRegistrationKey.of("SolveMessageDeletedInconsistencies"); + + @Inject + public SolveMessageDeletedInconsistenciesRequestToTask(SolveMailboxFlagInconsistenciesService service) { + super(REGISTRATION_KEY, request -> toTask(service)); + } + + private static SolveMailboxFlagInconsistencyTask toTask(SolveMailboxFlagInconsistenciesService service) { + return new SolveMailboxFlagInconsistencyTask(service, TargetFlag.DELETED); + } +} diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageRecentInconsistenciesRequestToTask.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageRecentInconsistenciesRequestToTask.java new file mode 100644 index 0000000000..ffc2780c0a --- /dev/null +++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/SolveMessageRecentInconsistenciesRequestToTask.java @@ -0,0 +1,42 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.webadmin.routes; + + +import jakarta.inject.Inject; + +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistenciesService.TargetFlag; +import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxFlagInconsistencyTask; +import org.apache.james.webadmin.tasks.TaskFromRequestRegistry; +import org.apache.james.webadmin.tasks.TaskRegistrationKey; + +public class SolveMessageRecentInconsistenciesRequestToTask extends TaskFromRequestRegistry.TaskRegistration { + private static final TaskRegistrationKey REGISTRATION_KEY = TaskRegistrationKey.of("SolveMessageRecentInconsistencies"); + + @Inject + public SolveMessageRecentInconsistenciesRequestToTask(SolveMailboxFlagInconsistenciesService service) { + super(REGISTRATION_KEY, request -> toTask(service)); + } + + private static SolveMailboxFlagInconsistencyTask toTask(SolveMailboxFlagInconsistenciesService service) { + return new SolveMailboxFlagInconsistencyTask(service, TargetFlag.RECENT); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org