This is an automated email from the ASF dual-hosted git repository. rouazana pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 3805a8868913ef58bb6211ad388d830a1f2affa6 Author: RĂ©mi Kowalski <rkowal...@linagora.com> AuthorDate: Fri Mar 8 11:49:23 2019 +0100 JAMES-2681 rename Backup to Archive --- .../backup/{Backup.java => ArchiveService.java} | 2 +- .../james/mailbox/backup/DefaultMailboxBackup.java | 129 ++++++++++++++++++ .../org/apache/james/mailbox/backup/Zipper.java | 2 +- .../mailbox/backup/DefaultMailboxBackupTest.java | 148 +++++++++++++++++++++ 4 files changed, 279 insertions(+), 2 deletions(-) diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Backup.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/ArchiveService.java similarity index 98% rename from mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Backup.java rename to mailbox/backup/src/main/java/org/apache/james/mailbox/backup/ArchiveService.java index e77871e..2789691 100644 --- a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Backup.java +++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/ArchiveService.java @@ -25,7 +25,7 @@ import java.util.stream.Stream; import org.apache.james.mailbox.store.mail.model.MailboxMessage; -public interface Backup { +public interface ArchiveService { /** * @param mailboxes list of mailboxes and their annotations to be stored in the archive diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/DefaultMailboxBackup.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/DefaultMailboxBackup.java new file mode 100644 index 0000000..2bff492 --- /dev/null +++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/DefaultMailboxBackup.java @@ -0,0 +1,129 @@ +/**************************************************************** + * 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.backup; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.james.blob.api.BlobStore; +import org.apache.james.core.User; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.model.BlobId; +import org.apache.james.mailbox.model.FetchGroupImpl; +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.MessageRange; +import org.apache.james.mailbox.model.MessageResult; +import org.apache.james.mailbox.model.search.MailboxQuery; +import org.apache.james.mailbox.store.StoreMessageManager; +import org.apache.james.mailbox.store.mail.model.Mailbox; +import org.apache.james.util.OptionalUtils; +import org.apache.james.util.streams.Iterators; +import org.reactivestreams.Publisher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DefaultMailboxBackup implements MailboxBackup { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMailboxBackup.class); + + protected class MailAccountContent { + final MailboxWithAnnotations mailboxWithAnnotations; + final Stream<MessageResult> messages; + + MailAccountContent(MailboxWithAnnotations mailboxWithAnnotations, Stream<MessageResult> messages) { + this.mailboxWithAnnotations = mailboxWithAnnotations; + this.messages = messages; + } + } + + public DefaultMailboxBackup(MailboxManager mailboxManager, ArchiveService archiveService, BlobStore store) { + this.mailboxManager = mailboxManager; + this.archiveService = archiveService; + this.store = store; + } + + private final MailboxManager mailboxManager; + private final ArchiveService archiveService; + private final BlobStore store; + + private Function<MailboxPath, Optional<MailAccountContent>> getMailboxWithAnnotationsFromPath(MailboxSession session) { + return path -> { + try { + StoreMessageManager messageManager = (StoreMessageManager) mailboxManager.getMailbox(path, session); + Mailbox mailbox = messageManager.getMailboxEntity(); + List<MailboxAnnotation> annotations = mailboxManager.getAllAnnotations(path, session); + MailboxWithAnnotations mailboxWithAnnotations = new MailboxWithAnnotations(mailbox, annotations); + Stream<MessageResult> messages = Iterators.toStream(messageManager.getMessages(MessageRange.all(), FetchGroupImpl.FULL_CONTENT, session)); + return Optional.of(new MailAccountContent(mailboxWithAnnotations, messages)); + } catch (MailboxException e) { + LOGGER.error("Error while fetching Mailbox during backup", e); + return Optional.empty(); + } + }; + } + + private List<MailAccountContent> getAllMailboxesForUser(MailboxSession session) throws MailboxException { + MailboxQuery queryUser = MailboxQuery.builder().username(session.getUser().asString()).build(); + Stream<MailboxPath> paths = mailboxManager.search(queryUser, session).stream().map(mailboxMetaData -> mailboxMetaData.getPath()); + List<MailAccountContent> mailboxes = paths + .flatMap(getMailboxWithAnnotationsFromPath(session).andThen(OptionalUtils::toStream)) + .collect(Collectors.toList()); + + return mailboxes; + } + + private Publisher<BlobId> saveToStore(List<MailboxWithAnnotations> mailboxes, Stream<MessageResult> messages) throws IOException { + File tmp = File.createTempFile(UUID.randomUUID().toString(), ".tmp"); + try (OutputStream out = new FileOutputStream(tmp)) { + archiveService.archive(mailboxes, messages, out); + try (InputStream in = new BufferedInputStream(new FileInputStream(tmp))) { + return store.save(in).map(b -> BlobId.fromString(b.asString())); + } + } finally { + tmp.delete(); + } + } + + private Stream<MessageResult> allMessagesForUser(List<MailAccountContent> mailboxes) { + return mailboxes.stream().flatMap(messages -> messages.messages); + } + + @Override + public Publisher<BlobId> backupAccount(User user) throws IOException, MailboxException { + MailboxSession session = mailboxManager.createSystemSession(user.asString()); + List<MailAccountContent> mailboxesWithMessages = getAllMailboxesForUser(session); + List<MailboxWithAnnotations> mailboxes = mailboxesWithMessages.stream().map(m -> m.mailboxWithAnnotations).collect(Collectors.toList()); + Stream<MessageResult> messages = allMessagesForUser(mailboxesWithMessages); + return saveToStore(mailboxes, messages); + } +} diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Zipper.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Zipper.java index 7f87a69..370dbb4 100644 --- a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Zipper.java +++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Zipper.java @@ -37,7 +37,7 @@ import org.apache.james.mailbox.store.mail.model.MailboxMessage; import com.github.fge.lambdas.Throwing; import com.google.common.base.Charsets; -public class Zipper implements Backup { +public class Zipper implements ArchiveService { private static final String ANNOTATION_DIRECTORY = "annotations"; private static final boolean AUTO_FLUSH = true; diff --git a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/DefaultMailboxBackupTest.java b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/DefaultMailboxBackupTest.java new file mode 100644 index 0000000..3f57508 --- /dev/null +++ b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/DefaultMailboxBackupTest.java @@ -0,0 +1,148 @@ +/**************************************************************** + * 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.backup; + + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.HashSet; + +import org.apache.commons.compress.archivers.zip.ZipFile; +import org.apache.james.blob.api.BlobStore; +import org.apache.james.blob.api.HashBlobId; +import org.apache.james.blob.memory.MemoryBlobStore; +import org.apache.james.core.User; +import org.apache.james.junit.TemporaryFolderExtension; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.backup.ZipAssert.EntryChecks; +import org.apache.james.mailbox.extension.PreDeletionHook; +import org.apache.james.mailbox.inmemory.MemoryMailboxManagerProvider; +import org.apache.james.mailbox.manager.ManagerTestResources; +import org.apache.james.mailbox.model.BlobId; +import org.apache.james.mailbox.model.MailboxId; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.store.mail.model.MailboxMessage; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.reactivestreams.Publisher; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; + +import com.github.fge.lambdas.Throwing; +import reactor.core.publisher.Mono; + +@ExtendWith(TemporaryFolderExtension.class) +class DefaultMailboxBackupTest implements MailboxMessageFixture { + + private static final User USER1 = User.fromUsername(ManagerTestResources.USER); + private static final MailboxPath MAILBOX_PATH_USER1_MAILBOX1 = MailboxPath.forUser(ManagerTestResources.USER, MAILBOX_1_NAME); + private static final MailboxPath MAILBOX_PATH_OTHER_USER_MAILBOX1 = MailboxPath.forUser(ManagerTestResources.OTHER_USER, MAILBOX_OTHER_USER_NAME); + private static final HashBlobId.Factory BLOB_ID_FACTORY = new HashBlobId.Factory(); + private static final HashSet<PreDeletionHook> PRE_DELETION_HOOKS = new HashSet<>(); + + private final ArchiveService archiveService = new Zipper(); + + private MailboxManager mailboxManager; + private BlobStore store; + private File destination; + private DefaultMailboxBackup backup; + + @BeforeEach + void beforeEach(TemporaryFolderExtension.TemporaryFolder temporaryFolder) throws Exception { + destination = File.createTempFile("backup-test", ".zip", temporaryFolder.getTempDir()); + mailboxManager = MemoryMailboxManagerProvider.provideMailboxManager(PRE_DELETION_HOOKS); + store = new MemoryBlobStore(new HashBlobId.Factory()); + backup = new DefaultMailboxBackup(mailboxManager, archiveService, store); + } + + private void readFromStoreAndCopyInFile(BlobId blobId) throws Exception { + InputStream content = store.read(BLOB_ID_FACTORY.from(blobId.asString())); + try (OutputStream out = new FileOutputStream(destination)) { + IOUtils.copy(content, out); + } + } + + private void createMailBoxWithMessage(MailboxSession session, MailboxPath mailboxPath, MailboxMessage... messages) throws Exception { + MailboxId mailboxId = mailboxManager.createMailbox(mailboxPath, session).get(); + Arrays.stream(messages).forEach(Throwing.consumer(message -> + mailboxManager.getMailbox(mailboxId, session).appendMessage(MessageManager.AppendCommand.from(message.getFullContent()), session) + ) + ); + } + + private ZipAssert assertThatZipFromIdContainsOnly(Publisher<BlobId> blobIdPublisher, EntryChecks... onlyEntriesMatching) throws Exception { + BlobId blobId = Mono.from(blobIdPublisher).block(); + readFromStoreAndCopyInFile(blobId); + + try (ZipFile zipFile = new ZipFile(destination)) { + return ZipAssert.assertThatZip(zipFile).containsOnlyEntriesMatching(onlyEntriesMatching); + } + } + + @Test + void doBackupWithoutMailboxShouldStoreEmptyBackup() throws Exception { + Publisher<BlobId> res = backup.backupAccount(USER1); + + assertThatZipFromIdContainsOnly(res); + } + + @Test + void doBackupWithoutMessageShouldStoreAnArchiveWithOnlyOneEntry() throws Exception { + MailboxSession session = mailboxManager.createSystemSession(ManagerTestResources.USER); + createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX1); + + Publisher<BlobId> res = backup.backupAccount(USER1); + + assertThatZipFromIdContainsOnly(res, EntryChecks.hasName(MAILBOX_1_NAME + "/").isDirectory()); + } + + @Test + void doBackupWithOneMessageShouldStoreAnArchiveWithTwoEntries() throws Exception { + MailboxSession session = mailboxManager.createSystemSession(ManagerTestResources.USER); + createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX1, MESSAGE_1); + + Publisher<BlobId> res = backup.backupAccount(USER1); + + assertThatZipFromIdContainsOnly(res, + EntryChecks.hasName(MAILBOX_1_NAME + "/").isDirectory(), + EntryChecks.hasName(MESSAGE_ID_1.serialize()).hasStringContent(MESSAGE_CONTENT_1) + ); + } + + @Test + void doBackupShouldOnlyArchiveTheMailboxOfTheUser() throws Exception { + MailboxSession session = mailboxManager.createSystemSession(ManagerTestResources.USER); + MailboxSession otherSession = mailboxManager.createSystemSession(ManagerTestResources.OTHER_USER); + + createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX1, MESSAGE_1); + createMailBoxWithMessage(otherSession, MAILBOX_PATH_OTHER_USER_MAILBOX1, MESSAGE_1_OTHER_USER); + + Publisher<BlobId> res = backup.backupAccount(USER1); + + assertThatZipFromIdContainsOnly(res, + EntryChecks.hasName(MAILBOX_1_NAME + "/").isDirectory(), + EntryChecks.hasName(MESSAGE_ID_1.serialize()).hasStringContent(MESSAGE_CONTENT_1) + ); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org