JAMES-2426 Allow to backup messages as ZIP files

Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/84338dc4
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/84338dc4
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/84338dc4

Branch: refs/heads/master
Commit: 84338dc4c71c61e1da26ad9c8f8740d13bc0042d
Parents: ecb7c94
Author: Raphael Ouazana <[email protected]>
Authored: Wed Jun 13 18:17:26 2018 +0200
Committer: benwa <[email protected]>
Committed: Tue Jun 19 15:07:55 2018 +0700

----------------------------------------------------------------------
 mailbox/backup/pom.xml                          |  6 ++
 .../org/apache/james/mailbox/backup/Backup.java |  4 +-
 .../org/apache/james/mailbox/backup/Zipper.java | 16 +++-
 .../mailbox/backup/MailboxMessageFixture.java   | 83 ++++++++++++++++++++
 .../mailbox/backup/ZipArchiveEntryAssert.java   | 79 +++++++++++++++++++
 .../apache/james/mailbox/backup/ZipAssert.java  | 76 ++++++++++++++++++
 .../apache/james/mailbox/backup/ZipperTest.java | 62 +++++++++++++--
 7 files changed, 317 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/backup/pom.xml b/mailbox/backup/pom.xml
index 613dcfc..2628ca3 100644
--- a/mailbox/backup/pom.xml
+++ b/mailbox/backup/pom.xml
@@ -39,6 +39,12 @@
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>apache-james-mailbox-store</artifactId>
         </dependency>
         <dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Backup.java
----------------------------------------------------------------------
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 2f2d6a4..80f246f 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
@@ -18,13 +18,13 @@
  ****************************************************************/
 package org.apache.james.mailbox.backup;
 
-import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.List;
 
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 
 public interface Backup {
 
-    void archive(List<MailboxMessage> messages, File destination) throws 
IOException;
+    void archive(List<MailboxMessage> messages, OutputStream destination) 
throws IOException;
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/Zipper.java
----------------------------------------------------------------------
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 c3ee4fb..c6d95ad 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
@@ -20,17 +20,31 @@ package org.apache.james.mailbox.backup;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.List;
 
+import org.apache.commons.compress.archivers.ArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
+import org.apache.commons.io.IOUtils;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 
 public class Zipper implements Backup {
 
     @Override
-    public void archive(List<MailboxMessage> messages, File destination) 
throws IOException {
+    public void archive(List<MailboxMessage> messages, OutputStream 
destination) throws IOException {
         try (ZipArchiveOutputStream archiveOutputStream = new 
ZipArchiveOutputStream(destination)) {
+            for (MailboxMessage message: messages) {
+                storeInArchive(message, archiveOutputStream);
+            }
             archiveOutputStream.finish();
         }
     }
+
+    private void storeInArchive(MailboxMessage message, ZipArchiveOutputStream 
archiveOutputStream) throws IOException {
+        String entryId = message.getMessageId().serialize();
+        ArchiveEntry archiveEntry = archiveOutputStream.createArchiveEntry(new 
File(entryId), entryId);
+        archiveOutputStream.putArchiveEntry(archiveEntry);
+        IOUtils.copy(message.getFullContent(), archiveOutputStream);
+        archiveOutputStream.closeArchiveEntry();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..8012e78
--- /dev/null
+++ 
b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.mail.Flags;
+import javax.mail.util.SharedByteArrayInputStream;
+
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.TestId;
+import org.apache.james.mailbox.model.TestMessageId;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+
+public interface MailboxMessageFixture {
+
+    SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd 
hh:mm:ss");
+
+    Date DATE_1 = parseDate("2018-02-15 15:54:02");
+    Date DATE_2 = parseDate("2018-03-15 15:54:02");
+
+    MessageId.Factory MESSAGE_ID_FACTORY = new TestMessageId.Factory();
+    Charset MESSAGE_CHARSET = StandardCharsets.UTF_8;
+    String MESSAGE_CONTENT_1 = "Simple message content";
+    SharedByteArrayInputStream CONTENT_STREAM_1 = new 
SharedByteArrayInputStream(MESSAGE_CONTENT_1.getBytes(MESSAGE_CHARSET));
+    String MESSAGE_CONTENT_2 = "Other message content";
+    SharedByteArrayInputStream CONTENT_STREAM_2 = new 
SharedByteArrayInputStream(MESSAGE_CONTENT_2.getBytes(MESSAGE_CHARSET));
+    MessageId MESSAGE_ID_1 = MESSAGE_ID_FACTORY.generate();
+    MessageId MESSAGE_ID_2 = MESSAGE_ID_FACTORY.generate();
+    int SIZE_1 = 1000;
+    int SIZE_2 = 2000;
+
+    SimpleMailboxMessage MESSAGE_1 = SimpleMailboxMessage.builder()
+        .messageId(MESSAGE_ID_1)
+        .content(CONTENT_STREAM_1)
+        .size(SIZE_1)
+        .internalDate(DATE_1)
+        .bodyStartOctet(0)
+        .flags(new Flags())
+        .propertyBuilder(new PropertyBuilder())
+        .mailboxId(TestId.of(1L))
+        .build();
+    SimpleMailboxMessage MESSAGE_2 = SimpleMailboxMessage.builder()
+        .messageId(MESSAGE_ID_2)
+        .content(CONTENT_STREAM_2)
+        .size(SIZE_2)
+        .internalDate(DATE_2)
+        .bodyStartOctet(0)
+        .flags(new Flags())
+        .propertyBuilder(new PropertyBuilder())
+        .mailboxId(TestId.of(1L))
+        .build();
+
+    static Date parseDate(String input) {
+        try {
+            return SIMPLE_DATE_FORMAT.parse(input);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchiveEntryAssert.java
----------------------------------------------------------------------
diff --git 
a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchiveEntryAssert.java
 
b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchiveEntryAssert.java
new file mode 100644
index 0000000..f3d1b89
--- /dev/null
+++ 
b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchiveEntryAssert.java
@@ -0,0 +1,79 @@
+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.io.IOUtils;
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.error.BasicErrorMessageFactory;
+
+public class ZipArchiveEntryAssert extends 
AbstractAssert<ZipArchiveEntryAssert, ZipArchiveEntry> {
+
+    public static ZipArchiveEntryAssert assertThatZipEntry(ZipFile zipFile, 
ZipArchiveEntry zipArchiveEntry) {
+        return new ZipArchiveEntryAssert(zipFile, zipArchiveEntry);
+    }
+
+    private static BasicErrorMessageFactory shouldHaveName(ZipArchiveEntry 
zipArchiveEntry, String expected) {
+        return new BasicErrorMessageFactory("%nExpecting %s to have name %s 
but was %s", zipArchiveEntry, expected, zipArchiveEntry.getName());
+    }
+
+    private static BasicErrorMessageFactory 
contentShouldBePresent(ZipArchiveEntry zipArchiveEntry) {
+        return new BasicErrorMessageFactory("%nCould not retrieve %s content", 
zipArchiveEntry);
+    }
+
+    private static BasicErrorMessageFactory shouldHaveContent(ZipArchiveEntry 
zipArchiveEntry, String expectedContent, String actualContent) {
+        return new BasicErrorMessageFactory("%nExpecting %s to have content %s 
but was %s", zipArchiveEntry, expectedContent, actualContent);
+    }
+
+    private final ZipFile zipFile;
+    private final ZipArchiveEntry actual;
+
+    private ZipArchiveEntryAssert(ZipFile zipFile, ZipArchiveEntry 
zipArchiveEntry) {
+        super(zipArchiveEntry, ZipArchiveEntryAssert.class);
+        this.zipFile = zipFile;
+        this.actual = zipArchiveEntry;
+    }
+
+    public ZipArchiveEntryAssert hasName(String name) {
+        isNotNull();
+        if (!actual.getName().equals(name)) {
+            throwAssertionError(shouldHaveName(actual, name));
+        }
+        return myself;
+    }
+
+    public ZipArchiveEntryAssert hasStringContent(String content) throws 
IOException {
+        isNotNull();
+        InputStream inputStream = zipFile.getInputStream(actual);
+        if (inputStream == null) {
+            throwAssertionError(contentShouldBePresent(actual));
+        }
+        String actualContentAsString = IOUtils.toString(inputStream, 
StandardCharsets.UTF_8);
+        if (!actualContentAsString.equals(content)) {
+            throwAssertionError(shouldHaveContent(actual, content, 
actualContentAsString));
+        }
+        return myself;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipAssert.java
----------------------------------------------------------------------
diff --git 
a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipAssert.java 
b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipAssert.java
new file mode 100644
index 0000000..f521b20
--- /dev/null
+++ 
b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipAssert.java
@@ -0,0 +1,76 @@
+/****************************************************************
+ * 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 static 
org.apache.james.mailbox.backup.ZipArchiveEntryAssert.assertThatZipEntry;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.error.BasicErrorMessageFactory;
+
+public class ZipAssert extends AbstractAssert<ZipAssert, ZipFile> {
+    interface EntryCheck {
+        void test(ZipArchiveEntryAssert assertion) throws Exception;
+    }
+
+    public static ZipAssert assertThatZip(ZipFile zipFile) {
+        return new ZipAssert(zipFile);
+    }
+
+    private static BasicErrorMessageFactory shouldHaveSize(ZipFile zipFile, 
int expected, int actual) {
+        return new BasicErrorMessageFactory("%nExpecting %s to have side %d 
but was %d", zipFile, expected, actual);
+    }
+
+    private static BasicErrorMessageFactory shouldBeEmpty(ZipFile zipFile) {
+        return new BasicErrorMessageFactory("%nExpecting %s to be empty", 
zipFile);
+    }
+
+    private final ZipFile zipFile;
+
+    private ZipAssert(ZipFile zipFile) {
+        super(zipFile, ZipAssert.class);
+        this.zipFile = zipFile;
+    }
+
+    public ZipAssert containsExactlyEntriesMatching(EntryCheck... entryChecks) 
throws Exception {
+        isNotNull();
+        List<ZipArchiveEntry> entries = Collections.list(zipFile.getEntries());
+        if (entries.size() != entryChecks.length) {
+            throwAssertionError(shouldHaveSize(zipFile, entryChecks.length, 
entries.size()));
+        }
+        for (int i = 0; i < entries.size(); i++) {
+            entryChecks[i].test(assertThatZipEntry(zipFile, entries.get(i)));
+        }
+        return myself;
+    }
+
+    public ZipAssert hasNoEntry() {
+        isNotNull();
+        if (zipFile.getEntries().hasMoreElements()) {
+            throwAssertionError(shouldBeEmpty(zipFile));
+        }
+        return myself;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/84338dc4/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipperTest.java
----------------------------------------------------------------------
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 25d2fbb..888fed6 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,9 +18,16 @@
  ****************************************************************/
 package org.apache.james.mailbox.backup;
 
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_1;
+import static org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_2;
+import static 
org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_CONTENT_1;
+import static 
org.apache.james.mailbox.backup.MailboxMessageFixture.MESSAGE_CONTENT_2;
+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.ZipAssert.assertThatZip;
 
 import java.io.File;
+import java.io.FileOutputStream;
 
 import org.apache.commons.compress.archivers.zip.ZipFile;
 import org.apache.james.junit.TemporaryFolderExtension;
@@ -34,20 +41,63 @@ import com.google.common.collect.ImmutableList;
 @ExtendWith(TemporaryFolderExtension.class)
 public class ZipperTest {
     private Zipper testee;
+    private File destination;
 
     @BeforeEach
-    void beforeEach() {
+    void beforeEach(TemporaryFolder temporaryFolder) throws Exception {
         testee = new Zipper();
+        destination = File.createTempFile("backup-test", ".zip", 
temporaryFolder.getTempDir());
     }
 
     @Test
-    void archiveShouldWriteEmptyValidArchiveWhenNoMessage(TemporaryFolder 
temporaryFolder) throws Exception {
-        File destination = File.createTempFile("backup-test", ".zip", 
temporaryFolder.getTempDir());
-        testee.archive(ImmutableList.of(), destination);
+    void archiveShouldWriteEmptyValidArchiveWhenNoMessage() throws Exception {
+        testee.archive(ImmutableList.of(), new FileOutputStream(destination));
 
         try (ZipFile zipFile = new ZipFile(destination)) {
-            assertThat(zipFile.getEntries().hasMoreElements()).isFalse();
+            assertThatZip(zipFile).hasNoEntry();
         }
     }
 
+    @Test
+    void archiveShouldWriteOneMessageWhenOne() throws Exception {
+        testee.archive(ImmutableList.of(MESSAGE_1), new 
FileOutputStream(destination));
+
+        try (ZipFile zipFile = new ZipFile(destination)) {
+            assertThatZip(zipFile)
+                .containsExactlyEntriesMatching(
+                    zipEntryAssert -> zipEntryAssert
+                        .hasName(MESSAGE_ID_1.serialize())
+                        .hasStringContent(MESSAGE_CONTENT_1));
+        }
+    }
+
+    @Test
+    void archiveShouldWriteTwoMessagesWhenTwo() throws Exception {
+        testee.archive(ImmutableList.of(MESSAGE_1, MESSAGE_2), new 
FileOutputStream(destination));
+
+        try (ZipFile zipFile = new ZipFile(destination)) {
+            assertThatZip(zipFile)
+                .containsExactlyEntriesMatching(
+                    zipEntryAssert -> zipEntryAssert
+                        .hasName(MESSAGE_ID_1.serialize())
+                        .hasStringContent(MESSAGE_CONTENT_1),
+                    zipEntryAssert -> zipEntryAssert
+                        .hasName(MESSAGE_ID_2.serialize())
+                        .hasStringContent(MESSAGE_CONTENT_2));
+        }
+    }
+
+    @Test
+    void archiveShouldOverwriteContent() throws Exception {
+        testee.archive(ImmutableList.of(MESSAGE_1), new 
FileOutputStream(destination));
+        testee.archive(ImmutableList.of(MESSAGE_2), new 
FileOutputStream(destination));
+
+        try (ZipFile zipFile = new ZipFile(destination)) {
+            assertThatZip(zipFile)
+                .containsExactlyEntriesMatching(
+                    zipEntryAssert -> zipEntryAssert
+                        .hasName(MESSAGE_ID_2.serialize())
+                        .hasStringContent(MESSAGE_CONTENT_2));
+        }
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to