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 8b31b688652ce38dea7a9dce6f3759232e03ecbf Author: Tung TRAN <[email protected]> AuthorDate: Tue Aug 3 18:06:16 2021 +0700 JAMES-3621 Mailbox webadmin - Clearing content of mailbox - Task --- .../webadmin/service/ClearMailboxContentTask.java | 214 +++++++++++++++++++++ ...MailboxContentTaskAdditionalInformationDTO.java | 100 ++++++++++ .../webadmin/service/UserMailboxesService.java | 38 +++- 3 files changed, 351 insertions(+), 1 deletion(-) diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTask.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTask.java new file mode 100644 index 0000000..32ffdd4 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTask.java @@ -0,0 +1,214 @@ +/**************************************************************** + * 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.service; + +import java.time.Clock; +import java.time.Instant; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.james.core.Username; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.task.Task; +import org.apache.james.task.TaskExecutionDetails; +import org.apache.james.task.TaskType; +import org.apache.james.user.api.UsersRepositoryException; +import org.apache.james.webadmin.validation.MailboxName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +import reactor.core.scheduler.Schedulers; + +public class ClearMailboxContentTask implements Task { + private static final Logger LOGGER = LoggerFactory.getLogger(ClearMailboxContentTask.class); + + public static class Context { + public static class Snapshot { + private final long messagesSuccessCount; + private final long messagesFailedCount; + + private Snapshot(long messagesSuccessCount, long messagesFailedCount) { + this.messagesSuccessCount = messagesSuccessCount; + this.messagesFailedCount = messagesFailedCount; + } + + public long getMessagesSuccessCount() { + return messagesSuccessCount; + } + + public long getMessagesFailedCount() { + return messagesFailedCount; + } + + @Override + public final boolean equals(Object o) { + if (o instanceof Snapshot) { + Snapshot that = (Snapshot) o; + + return Objects.equals(this.messagesSuccessCount, that.messagesSuccessCount) + && Objects.equals(this.messagesFailedCount, that.messagesFailedCount); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hash(messagesSuccessCount, messagesFailedCount); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("messagesSuccessCount", messagesSuccessCount) + .add("messagesFailedCount", messagesFailedCount) + .toString(); + } + } + + private final AtomicLong messagesSuccessCount; + private final AtomicLong messagesFailedCount; + + public Context() { + this.messagesSuccessCount = new AtomicLong(); + this.messagesFailedCount = new AtomicLong(); + } + + public Context(long messagesSuccessCount, long messagesFailedCount) { + this.messagesSuccessCount = new AtomicLong(messagesSuccessCount); + this.messagesFailedCount = new AtomicLong(messagesFailedCount); + } + + public void incrementSuccesses() { + messagesSuccessCount.incrementAndGet(); + } + + + public void incrementMessageFails() { + messagesFailedCount.incrementAndGet(); + } + + public Snapshot snapshot() { + return new Snapshot(messagesSuccessCount.get(), messagesFailedCount.get()); + } + } + + public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation { + + private static AdditionalInformation from(Username username, + MailboxName mailboxName, + Context context) { + Context.Snapshot snapshot = context.snapshot(); + return new AdditionalInformation(username, mailboxName, Clock.systemUTC().instant(), snapshot.messagesSuccessCount, snapshot.messagesFailedCount); + } + + private final Username username; + private final MailboxName mailboxName; + private final Instant timestamp; + private final long messagesSuccessCount; + private final long messagesFailCount; + + public AdditionalInformation(Username username, + MailboxName mailboxName, + Instant timestamp, + long messagesSuccessCount, + long messagesFailCount) { + this.username = username; + this.mailboxName = mailboxName; + this.timestamp = timestamp; + this.messagesSuccessCount = messagesSuccessCount; + this.messagesFailCount = messagesFailCount; + } + + public Username getUsername() { + return username; + } + + public MailboxName getMailboxName() { + return mailboxName; + } + + public Instant getTimestamp() { + return timestamp; + } + + public long getMessagesSuccessCount() { + return messagesSuccessCount; + } + + public long getMessagesFailCount() { + return messagesFailCount; + } + + @Override + public Instant timestamp() { + return timestamp; + } + } + + public static final TaskType TASK_TYPE = TaskType.of("ClearMailboxContentTask"); + + private final Context context; + private final Username username; + private final MailboxName mailboxName; + private final UserMailboxesService userMailboxesService; + + public ClearMailboxContentTask(Username username, + MailboxName mailboxName, + UserMailboxesService userMailboxesService) { + this.username = username; + this.mailboxName = mailboxName; + this.userMailboxesService = userMailboxesService; + this.context = new Context(); + + } + + @Override + public Result run() throws InterruptedException { + try { + return userMailboxesService.clearMailboxContent(username, mailboxName, context) + .subscribeOn(Schedulers.elastic()) + .block(); + } catch (UsersRepositoryException | MailboxException e) { + LOGGER.error("Has an error when clear the mailbox content. ", e); + throw new InterruptedException(e.getMessage()); + } + } + + @Override + public TaskType type() { + return TASK_TYPE; + } + + @Override + public Optional<TaskExecutionDetails.AdditionalInformation> details() { + return Optional.of(AdditionalInformation.from(username, mailboxName, context)); + } + + public Username getUsername() { + return username; + } + + public MailboxName getMailboxName() { + return mailboxName; + } +} diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTaskAdditionalInformationDTO.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTaskAdditionalInformationDTO.java new file mode 100644 index 0000000..0e37eb7 --- /dev/null +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/ClearMailboxContentTaskAdditionalInformationDTO.java @@ -0,0 +1,100 @@ +/**************************************************************** + * 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.service; + +import java.time.Instant; + +import org.apache.james.core.Username; +import org.apache.james.json.DTOModule; +import org.apache.james.server.task.json.dto.AdditionalInformationDTO; +import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule; +import org.apache.james.webadmin.validation.MailboxName; + +public class ClearMailboxContentTaskAdditionalInformationDTO implements AdditionalInformationDTO { + + public static final AdditionalInformationDTOModule<ClearMailboxContentTask.AdditionalInformation, ClearMailboxContentTaskAdditionalInformationDTO> SERIALIZATION_MODULE = + DTOModule.forDomainObject(ClearMailboxContentTask.AdditionalInformation.class) + .convertToDTO(ClearMailboxContentTaskAdditionalInformationDTO.class) + .toDomainObjectConverter(ClearMailboxContentTaskAdditionalInformationDTO::toDomainObject) + .toDTOConverter(ClearMailboxContentTaskAdditionalInformationDTO::toDto) + .typeName(ClearMailboxContentTask.TASK_TYPE.asString()) + .withFactory(AdditionalInformationDTOModule::new); + + private static ClearMailboxContentTask.AdditionalInformation toDomainObject(ClearMailboxContentTaskAdditionalInformationDTO dto) { + return new ClearMailboxContentTask.AdditionalInformation( + Username.of(dto.getUsername()), + new MailboxName(dto.getMailboxName()), + dto.getTimestamp(), + dto.getMessagesSuccessCount(), + dto.getMessagesFailCount()); + } + + private static ClearMailboxContentTaskAdditionalInformationDTO toDto(ClearMailboxContentTask.AdditionalInformation domain, String type) { + return new ClearMailboxContentTaskAdditionalInformationDTO( + type, + domain.getUsername().asString(), + domain.getMailboxName().asString(), + domain.getTimestamp(), + domain.getMessagesSuccessCount(), + domain.getMessagesFailCount()); + } + + private final String type; + private final String username; + private final String mailboxName; + private final Instant timestamp; + private final long messagesSuccessCount; + private final long messagesFailCount; + + public ClearMailboxContentTaskAdditionalInformationDTO(String type, String username, String mailboxName, Instant timestamp, long messagesSuccessCount, long messagesFailCount) { + this.type = type; + this.username = username; + this.mailboxName = mailboxName; + this.timestamp = timestamp; + this.messagesSuccessCount = messagesSuccessCount; + this.messagesFailCount = messagesFailCount; + } + + @Override + public String getType() { + return type; + } + + @Override + public Instant getTimestamp() { + return timestamp; + } + + public String getUsername() { + return username; + } + + public String getMailboxName() { + return mailboxName; + } + + public long getMessagesSuccessCount() { + return messagesSuccessCount; + } + + public long getMessagesFailCount() { + return messagesFailCount; + } +} diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java index 4a839d4..dde29fe 100644 --- a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java +++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/service/UserMailboxesService.java @@ -29,12 +29,17 @@ import javax.inject.Inject; import org.apache.james.core.Username; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.model.MailboxMetaData; import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.MessageRange; import org.apache.james.mailbox.model.search.MailboxQuery; +import org.apache.james.task.Task; +import org.apache.james.task.Task.Result; import org.apache.james.user.api.UsersRepository; import org.apache.james.user.api.UsersRepositoryException; import org.apache.james.webadmin.dto.MailboxResponse; @@ -47,8 +52,9 @@ import com.github.fge.lambdas.Throwing; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; - +import reactor.core.scheduler.Schedulers; public class UserMailboxesService { private static final Logger LOGGER = LoggerFactory.getLogger(UserMailboxesService.class); @@ -99,6 +105,36 @@ public class UserMailboxesService { .block(); } + public Mono<Result> clearMailboxContent(Username username, MailboxName mailboxName, ClearMailboxContentTask.Context context) throws UsersRepositoryException, MailboxException { + MailboxSession mailboxSession = mailboxManager.createSystemSession(username); + MessageManager messageManager = mailboxManager.getMailbox(MailboxPath.forUser(username, mailboxName.asString()), mailboxSession); + + return Flux.from(messageManager.listMessagesMetadata(MessageRange.all(), mailboxSession)) + .map(metaData -> metaData.getComposedMessageId().getUid()) + .concatMap(messageUid -> deleteMessage(messageManager, messageUid, mailboxSession, context)) + .onErrorResume(e -> { + LOGGER.error("Error when clear mailbox content. Mailbox {} for user {}", mailboxName.asString(), username, e); + context.incrementMessageFails(); + return Mono.just(Result.PARTIAL); + }) + .reduce(Task::combine) + .switchIfEmpty(Mono.just(Result.COMPLETED)); + } + + private Mono<Result> deleteMessage(MessageManager messageManager, MessageUid messageUid, MailboxSession mailboxSession, ClearMailboxContentTask.Context context) { + return Mono.fromCallable(() -> { + try { + messageManager.delete(List.of(messageUid), mailboxSession); + context.incrementSuccesses(); + return Result.COMPLETED; + } catch (MailboxException e) { + context.incrementMessageFails(); + return Result.PARTIAL; + } + }) + .subscribeOn(Schedulers.elastic()); + } + public void deleteMailbox(Username username, MailboxName mailboxName) throws MailboxException, UsersRepositoryException, MailboxHaveChildrenException { usernamePreconditions(username); MailboxSession mailboxSession = mailboxManager.createSystemSession(username); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
