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 d3238e1d6d8eff96045ae82ab4dafc4a9df3a5ad Author: RĂ©mi Kowalski <rkowal...@linagora.com> AuthorDate: Tue Mar 5 14:26:22 2019 +0100 JAMES-2676 add mailbox annotations in backup zip --- .../james/mailbox/model/MailboxAnnotationKey.java | 2 +- .../org/apache/james/mailbox/backup/Backup.java | 7 +- .../{Backup.java => MailboxWithAnnotations.java} | 35 ++++++--- .../org/apache/james/mailbox/backup/Zipper.java | 59 ++++++++++++--- .../mailbox/backup/MailboxMessageFixture.java | 22 +++++- .../apache/james/mailbox/backup/ZipperTest.java | 86 ++++++++++++++++++++-- 6 files changed, 177 insertions(+), 34 deletions(-) diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotationKey.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotationKey.java index 1e7349d..a4ac543 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotationKey.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotationKey.java @@ -46,7 +46,7 @@ public class MailboxAnnotationKey { public MailboxAnnotationKey(String key) { this.key = key; Preconditions.checkArgument(isValid(), - "Key must start with '/' and not end with '/' and does not contain charater with hex from '\u0000' to '\u00019' or {'*', '%', two consecutive '/'} "); + "Key must start with '/' and not end with '/' and does not contain character with hex from '\u0000' to '\u00019' or {'*', '%', two consecutive '/'} "); } private boolean isValid() { 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/Backup.java index 81ebddb..e77871e 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/Backup.java @@ -23,15 +23,14 @@ import java.io.OutputStream; import java.util.List; import java.util.stream.Stream; -import org.apache.james.mailbox.store.mail.model.Mailbox; import org.apache.james.mailbox.store.mail.model.MailboxMessage; public interface Backup { /** - * @param mailboxes list of mailboxes to be stored in the archive - * @param messages a stream of MailboxMessages that will be consumed + * @param mailboxes list of mailboxes and their annotations to be stored in the archive + * @param messages a stream of MailboxMessages that will be consumed * @param destination an OutputStream in which the zip will be written */ - void archive(List<Mailbox> mailboxes, Stream<MailboxMessage> messages, OutputStream destination) throws IOException; + void archive(List<MailboxWithAnnotations> mailboxes, Stream<MailboxMessage> messages, OutputStream destination) throws IOException; } 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/MailboxWithAnnotations.java similarity index 62% copy from mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Backup.java copy to mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailboxWithAnnotations.java index 81ebddb..c460685 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/MailboxWithAnnotations.java @@ -18,20 +18,33 @@ ****************************************************************/ package org.apache.james.mailbox.backup; -import java.io.IOException; -import java.io.OutputStream; import java.util.List; -import java.util.stream.Stream; +import java.util.Objects; +import org.apache.james.mailbox.model.MailboxAnnotation; import org.apache.james.mailbox.store.mail.model.Mailbox; -import org.apache.james.mailbox.store.mail.model.MailboxMessage; -public interface Backup { +public class MailboxWithAnnotations { + public final Mailbox mailbox; + public final List<MailboxAnnotation> annotations; - /** - * @param mailboxes list of mailboxes to be stored in the archive - * @param messages a stream of MailboxMessages that will be consumed - * @param destination an OutputStream in which the zip will be written - */ - void archive(List<Mailbox> mailboxes, Stream<MailboxMessage> messages, OutputStream destination) throws IOException; + public MailboxWithAnnotations(Mailbox mailbox, List<MailboxAnnotation> annotations) { + this.mailbox = mailbox; + this.annotations = annotations; + } + + @Override + public boolean equals(Object o) { + if (o instanceof MailboxAnnotation) { + MailboxWithAnnotations that = (MailboxWithAnnotations) o; + return Objects.equals(mailbox, that.mailbox) && + Objects.equals(annotations, that.annotations); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mailbox, annotations); + } } 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 4d1de4f..7f87a69 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 @@ -21,6 +21,8 @@ package org.apache.james.mailbox.backup; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.util.List; import java.util.stream.Stream; @@ -28,12 +30,18 @@ import org.apache.commons.compress.archivers.zip.ExtraFieldUtils; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream; import org.apache.commons.io.IOUtils; +import org.apache.james.mailbox.model.MailboxAnnotation; import org.apache.james.mailbox.store.mail.model.Mailbox; 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 { + + private static final String ANNOTATION_DIRECTORY = "annotations"; + private static final boolean AUTO_FLUSH = true; + public Zipper() { ExtraFieldUtils.register(SizeExtraField.class); ExtraFieldUtils.register(UidExtraField.class); @@ -45,7 +53,7 @@ public class Zipper implements Backup { } @Override - public void archive(List<Mailbox> mailboxes, Stream<MailboxMessage> messages, OutputStream destination) throws IOException { + public void archive(List<MailboxWithAnnotations> mailboxes, Stream<MailboxMessage> messages, OutputStream destination) throws IOException { try (ZipArchiveOutputStream archiveOutputStream = new ZipArchiveOutputStream(destination)) { storeMailboxes(mailboxes, archiveOutputStream); storeMessages(messages, archiveOutputStream); @@ -53,19 +61,22 @@ public class Zipper implements Backup { } } - private void storeMailboxes(List<Mailbox> mailboxes, ZipArchiveOutputStream archiveOutputStream) throws IOException { - for (Mailbox mailbox: mailboxes) { - storeInArchive(mailbox, archiveOutputStream); - } + private void storeMailboxes(List<MailboxWithAnnotations> mailboxes, ZipArchiveOutputStream archiveOutputStream) throws IOException { + mailboxes.forEach(Throwing.<MailboxWithAnnotations>consumer(mailbox -> + storeInArchive(mailbox, archiveOutputStream) + ).sneakyThrow()); } - private void storeMessages(Stream<MailboxMessage> messages, ZipArchiveOutputStream archiveOutputStream) { - messages.forEach(Throwing.<MailboxMessage>consumer(message -> { - storeInArchive(message, archiveOutputStream); - }).sneakyThrow()); + private void storeMessages(Stream<MailboxMessage> messages, ZipArchiveOutputStream archiveOutputStream) throws IOException { + messages.forEach(Throwing.<MailboxMessage>consumer(message -> + storeInArchive(message, archiveOutputStream) + ).sneakyThrow()); } - private void storeInArchive(Mailbox mailbox, ZipArchiveOutputStream archiveOutputStream) throws IOException { + private void storeInArchive(MailboxWithAnnotations mailboxWithAnnotations, ZipArchiveOutputStream archiveOutputStream) throws IOException { + Mailbox mailbox = mailboxWithAnnotations.mailbox; + List<MailboxAnnotation> annotations = mailboxWithAnnotations.annotations; + String name = mailbox.getName(); ZipArchiveEntry archiveEntry = (ZipArchiveEntry) archiveOutputStream.createArchiveEntry(new Directory(name), name); @@ -74,6 +85,34 @@ public class Zipper implements Backup { archiveOutputStream.putArchiveEntry(archiveEntry); archiveOutputStream.closeArchiveEntry(); + + storeAllAnnotationsInArchive(archiveOutputStream, annotations, name); + } + + private void storeAllAnnotationsInArchive(ZipArchiveOutputStream archiveOutputStream, List<MailboxAnnotation> annotations, String name) throws IOException { + if (!annotations.isEmpty()) { + String annotationsDirectoryPath = name + "/" + ANNOTATION_DIRECTORY; + ZipArchiveEntry annotationDirectory = (ZipArchiveEntry) archiveOutputStream.createArchiveEntry( + new Directory(annotationsDirectoryPath), annotationsDirectoryPath); + archiveOutputStream.putArchiveEntry(annotationDirectory); + archiveOutputStream.closeArchiveEntry(); + annotations.forEach(Throwing.consumer(annotation -> + storeInArchive(annotation, annotationsDirectoryPath, archiveOutputStream))); + } + } + + private void storeInArchive(MailboxAnnotation annotation, String directory, ZipArchiveOutputStream archiveOutputStream) throws IOException { + String entryId = directory + "/" + annotation.getKey().asString(); + ZipArchiveEntry archiveEntry = (ZipArchiveEntry) archiveOutputStream.createArchiveEntry(new File(entryId), entryId); + archiveOutputStream.putArchiveEntry(archiveEntry); + + annotation.getValue().ifPresent(value -> { + try (PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(archiveOutputStream, Charsets.UTF_8), AUTO_FLUSH)) { + printWriter.print(value); + } + }); + + archiveOutputStream.closeArchiveEntry(); } private void storeInArchive(MailboxMessage message, ZipArchiveOutputStream archiveOutputStream) throws IOException { diff --git a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java index 098a858..c4a368a 100644 --- a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java +++ b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java @@ -23,6 +23,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.util.Date; +import java.util.List; import javax.mail.Flags; import javax.mail.util.SharedByteArrayInputStream; @@ -30,6 +31,8 @@ import javax.mail.util.SharedByteArrayInputStream; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MailboxSessionUtil; import org.apache.james.mailbox.MessageUid; +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.model.MailboxAnnotationKey; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MessageId; @@ -40,6 +43,8 @@ import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox; import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage; +import com.google.common.collect.ImmutableList; + public interface MailboxMessageFixture { String DATE_STRING_1 = "2018-02-15T15:54:02Z"; @@ -70,6 +75,22 @@ public interface MailboxMessageFixture { Mailbox MAILBOX_1_SUB_1 = new SimpleMailbox(MailboxPath.forUser("user", "mailbox1" + MAILBOX_SESSION.getPathDelimiter() + "sub1"), 420, TestId.of(11L)); Mailbox MAILBOX_2 = new SimpleMailbox(MailboxPath.forUser("user", "mailbox2"), 43, TestId.of(2L)); + List<MailboxAnnotation> NO_ANNOTATION = ImmutableList.of(); + + MailboxAnnotationKey ANNOTATION_1_KEY = new MailboxAnnotationKey("/annotation1/test"); + MailboxAnnotationKey ANNOTATION_2_KEY = new MailboxAnnotationKey("/annotation2/void"); + + String ANNOTATION_1_CONTENT = "annotation1 content"; + String ANNOTATION_1_BIS_CONTENT = "annotation1 bis content"; + String ANNOTATION_2_CONTENT = "annotation2 content"; + + MailboxAnnotation ANNOTATION_1 = MailboxAnnotation.newInstance(ANNOTATION_1_KEY, ANNOTATION_1_CONTENT); + MailboxAnnotation ANNOTATION_1_BIS = MailboxAnnotation.newInstance(ANNOTATION_1_KEY, ANNOTATION_1_BIS_CONTENT); + List<MailboxAnnotation> WITH_ANNOTATION_1 = ImmutableList.of(ANNOTATION_1); + + MailboxAnnotation ANNOTATION_2 = MailboxAnnotation.newInstance(ANNOTATION_2_KEY, ANNOTATION_2_CONTENT); + List<MailboxAnnotation> WITH_ANNOTATION_1_AND_2 = ImmutableList.of(ANNOTATION_1, ANNOTATION_2); + SimpleMailboxMessage MESSAGE_1 = SimpleMailboxMessage.builder() .messageId(MESSAGE_ID_1) .uid(MESSAGE_UID_1) @@ -92,5 +113,4 @@ public interface MailboxMessageFixture { .propertyBuilder(new PropertyBuilder()) .mailboxId(MAILBOX_ID_1) .build(); - } diff --git a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipperTest.java b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipperTest.java index 103d72d..e6da066 100644 --- a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipperTest.java +++ b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipperTest.java @@ -18,6 +18,12 @@ ****************************************************************/ package org.apache.james.mailbox.backup; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_1; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_1_BIS; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_1_BIS_CONTENT; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_1_CONTENT; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_2; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.ANNOTATION_2_CONTENT; import static org.apache.james.mailbox.backup.MailboxMessageFixture.MAILBOX_1; import static org.apache.james.mailbox.backup.MailboxMessageFixture.MAILBOX_1_SUB_1; import static org.apache.james.mailbox.backup.MailboxMessageFixture.MAILBOX_2; @@ -29,7 +35,10 @@ import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_CONT import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_ID_1; import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_ID_2; import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_UID_1_VALUE; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.NO_ANNOTATION; import static org.apache.james.mailbox.backup.MailboxMessageFixture.SIZE_1; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.WITH_ANNOTATION_1; +import static org.apache.james.mailbox.backup.MailboxMessageFixture.WITH_ANNOTATION_1_AND_2; import static org.apache.james.mailbox.backup.ZipAssert.EntryChecks.hasName; import static org.apache.james.mailbox.backup.ZipAssert.assertThatZip; @@ -39,14 +48,17 @@ import java.util.stream.Stream; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; -import org.apache.james.mailbox.store.mail.model.Mailbox; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import com.google.common.collect.ImmutableList; class ZipperTest { - private static final List<Mailbox> NO_MAILBOXES = ImmutableList.of(); + private static final List<MailboxWithAnnotations> NO_MAILBOXES = ImmutableList.of(); + private static final MailboxWithAnnotations MAILBOX_1_WITHOUT_ANNOTATION = new MailboxWithAnnotations(MAILBOX_1, NO_ANNOTATION); + private static final MailboxWithAnnotations MAILBOX_1_SUB_1_WITHOUT_ANNOTATION = new MailboxWithAnnotations(MAILBOX_1_SUB_1, NO_ANNOTATION); + private static final MailboxWithAnnotations MAILBOX_2_WITHOUT_ANNOTATION = new MailboxWithAnnotations(MAILBOX_2, NO_ANNOTATION); + private Zipper testee; private ByteArrayOutputStream output; @@ -109,7 +121,7 @@ class ZipperTest { @Test void archiveShouldWriteOneMailboxWhenPresent() throws Exception { - testee.archive(ImmutableList.of(MAILBOX_1), Stream.of(), output); + testee.archive(ImmutableList.of(MAILBOX_1_WITHOUT_ANNOTATION), Stream.of(), output); try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { assertThatZip(zipFile) @@ -121,7 +133,7 @@ class ZipperTest { @Test void archiveShouldWriteMailboxesWhenPresent() throws Exception { - testee.archive(ImmutableList.of(MAILBOX_1, MAILBOX_2), Stream.of(), output); + testee.archive(ImmutableList.of(MAILBOX_1_WITHOUT_ANNOTATION, MAILBOX_2_WITHOUT_ANNOTATION), Stream.of(), output); try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { assertThatZip(zipFile) @@ -135,7 +147,7 @@ class ZipperTest { @Test void archiveShouldWriteMailboxHierarchyWhenPresent() throws Exception { - testee.archive(ImmutableList.of(MAILBOX_1, MAILBOX_1_SUB_1, MAILBOX_2), Stream.of(), output); + testee.archive(ImmutableList.of(MAILBOX_1_WITHOUT_ANNOTATION, MAILBOX_1_SUB_1_WITHOUT_ANNOTATION, MAILBOX_2_WITHOUT_ANNOTATION), Stream.of(), output); try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { assertThatZip(zipFile) @@ -151,7 +163,7 @@ class ZipperTest { @Test void archiveShouldWriteMailboxHierarchyWhenMissingParent() throws Exception { - testee.archive(ImmutableList.of(MAILBOX_1_SUB_1, MAILBOX_2), Stream.of(), output); + testee.archive(ImmutableList.of(MAILBOX_1_SUB_1_WITHOUT_ANNOTATION, MAILBOX_2_WITHOUT_ANNOTATION), Stream.of(), output); try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { assertThatZip(zipFile) @@ -165,7 +177,7 @@ class ZipperTest { @Test void archiveShouldWriteMailboxMetadataWhenPresent() throws Exception { - testee.archive(ImmutableList.of(MAILBOX_1), Stream.of(), output); + testee.archive(ImmutableList.of(MAILBOX_1_WITHOUT_ANNOTATION), Stream.of(), output); try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { assertThatZip(zipFile) @@ -177,6 +189,66 @@ class ZipperTest { } } + @Test + void archiveShouldWriteMailBoxWithoutAnAnnotationSubDirWhenEmpty() throws Exception { + testee.archive(ImmutableList.of(MAILBOX_1_WITHOUT_ANNOTATION), Stream.of(), output); + + try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { + assertThatZip(zipFile) + .containsOnlyEntriesMatching( + hasName(MAILBOX_1.getName() + "/") + ); + } + } + + @Test + void archiveShouldWriteMailboxAnnotationsInASubDirWhenPresent() throws Exception { + testee.archive(ImmutableList.of(new MailboxWithAnnotations(MAILBOX_1, WITH_ANNOTATION_1)), Stream.of(), output); + + try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { + assertThatZip(zipFile) + .containsOnlyEntriesMatching( + hasName(MAILBOX_1.getName() + "/"), + hasName(MAILBOX_1.getName() + "/annotations/").isDirectory(), + hasName(MAILBOX_1.getName() + "/annotations/" + ANNOTATION_1.getKey().asString()) + ); + } + } + + @Test + void archiveShouldWriteMailboxAnnotationsInASubDirWhenTwoPresent() throws Exception { + testee.archive(ImmutableList.of(new MailboxWithAnnotations(MAILBOX_1, WITH_ANNOTATION_1_AND_2)), Stream.of(), output); + + try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { + assertThatZip(zipFile) + .containsOnlyEntriesMatching( + hasName(MAILBOX_1.getName() + "/"), + hasName(MAILBOX_1.getName() + "/annotations/").isDirectory(), + hasName(MAILBOX_1.getName() + "/annotations/" + ANNOTATION_1.getKey().asString()) + .hasStringContent(ANNOTATION_1_CONTENT), + hasName(MAILBOX_1.getName() + "/annotations/" + ANNOTATION_2.getKey().asString()) + .hasStringContent(ANNOTATION_2_CONTENT) + ); + } + } + + @Test + void archiveShouldWriteMailboxAnnotationsInASubDirWhenTwoPresentWithTheSameName() throws Exception { + testee.archive(ImmutableList.of(new MailboxWithAnnotations(MAILBOX_1, ImmutableList.of(ANNOTATION_1, ANNOTATION_1_BIS))), Stream.of(), output); + + try (ZipFile zipFile = new ZipFile(toSeekableByteChannel(output))) { + assertThatZip(zipFile) + .containsOnlyEntriesMatching( + hasName(MAILBOX_1.getName() + "/"), + hasName(MAILBOX_1.getName() + "/annotations/").isDirectory(), + hasName(MAILBOX_1.getName() + "/annotations/" + ANNOTATION_1.getKey().asString()) + .hasStringContent(ANNOTATION_1_CONTENT), + hasName(MAILBOX_1.getName() + "/annotations/" + ANNOTATION_1.getKey().asString()) + .hasStringContent(ANNOTATION_1_BIS_CONTENT) + ); + } + } + private SeekableInMemoryByteChannel toSeekableByteChannel(ByteArrayOutputStream output) { return new SeekableInMemoryByteChannel(output.toByteArray()); } --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org