You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ro...@apache.org on 2019/05/02 12:43:01 UTC
[james-project] 03/06: JAMES-2694 zip archive loader - read
mailboxes
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 aa1540fb41b2d54e3c0edba95673e6c512dcfe37
Author: RĂ©mi Kowalski <rk...@linagora.com>
AuthorDate: Mon Apr 8 12:02:11 2019 +0200
JAMES-2694 zip archive loader - read mailboxes
---
.../james/mailbox/model/search/MailboxQuery.java | 7 +-
.../james/mailbox/backup/MailArchiveEntry.java | 30 ++++
.../james/mailbox/backup/MailArchiveIterator.java | 26 ++++
.../james/mailbox/backup/MailArchivesLoader.java | 26 ++++
.../backup/MailboxWithAnnotationsArchiveEntry.java | 77 +++++++++++
.../james/mailbox/backup/MessageArchiveEntry.java | 72 ++++++++++
.../james/mailbox/backup/SerializedMailboxId.java | 54 ++++++++
.../james/mailbox/backup/SerializedMessageId.java | 54 ++++++++
.../james/mailbox/backup/UnknownArchiveEntry.java | 38 ++++++
.../mailbox/backup/zip/ExtraFieldExtractor.java | 56 ++++++++
.../mailbox/backup/zip/ZipArchivesLoader.java | 35 +++++
.../james/mailbox/backup/zip/ZipEntryIterator.java | 80 +++++++++++
.../backup/zip/ZippedMailAccountIterator.java | 110 +++++++++++++++
.../apache/james/mailbox/backup/zip/Zipper.java | 2 +-
.../mailbox/backup/MailboxMessageFixture.java | 16 ++-
.../mailbox/backup/ZipArchivesLoaderTest.java | 151 +++++++++++++++++++++
16 files changed, 828 insertions(+), 6 deletions(-)
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/MailboxQuery.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/MailboxQuery.java
index ed2f747..6c10eac 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/MailboxQuery.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/MailboxQuery.java
@@ -76,6 +76,11 @@ public final class MailboxQuery {
return this;
}
+ public Builder user(User user) {
+ this.username(user.asString());
+ return this;
+ }
+
public Builder namespace(String namespace) {
Preconditions.checkState(!this.namespace.isPresent());
@@ -89,7 +94,7 @@ public final class MailboxQuery {
this.namespace = Optional.of(MailboxConstants.USER_NAMESPACE);
return this;
}
-
+
public Builder expression(MailboxNameExpression expression) {
this.mailboxNameExpression = Optional.of(expression);
return this;
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveEntry.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveEntry.java
new file mode 100644
index 0000000..672c4d2
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveEntry.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * 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;
+
+public interface MailArchiveEntry {
+ enum ArchiveEntryType {
+ MAILBOX,
+ MESSAGE,
+ UNKNOWN
+ }
+
+ ArchiveEntryType getType();
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveIterator.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveIterator.java
new file mode 100644
index 0000000..43b0966
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchiveIterator.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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.Closeable;
+import java.util.Iterator;
+
+public interface MailArchiveIterator extends Iterator<MailArchiveEntry>, Closeable {
+
+}
\ No newline at end of file
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchivesLoader.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchivesLoader.java
new file mode 100644
index 0000000..5616d16
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailArchivesLoader.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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;
+
+public interface MailArchivesLoader {
+ MailArchiveIterator load(InputStream inputStream) throws IOException;
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailboxWithAnnotationsArchiveEntry.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailboxWithAnnotationsArchiveEntry.java
new file mode 100644
index 0000000..77c3bfc
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MailboxWithAnnotationsArchiveEntry.java
@@ -0,0 +1,77 @@
+/****************************************************************
+ * 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.util.List;
+import java.util.Objects;
+
+import org.apache.james.mailbox.model.MailboxAnnotation;
+
+import com.google.common.collect.ImmutableList;
+
+public class MailboxWithAnnotationsArchiveEntry implements MailArchiveEntry {
+ private final String mailboxName;
+ private final SerializedMailboxId mailboxId;
+
+ private final ImmutableList<MailboxAnnotation> annotations;
+
+ public MailboxWithAnnotationsArchiveEntry(String mailboxName, SerializedMailboxId mailboxId, List<MailboxAnnotation> annotations) {
+ this.mailboxName = mailboxName;
+ this.mailboxId = mailboxId;
+ this.annotations = ImmutableList.copyOf(annotations);
+ }
+
+ public String getMailboxName() {
+ return mailboxName;
+ }
+
+ public SerializedMailboxId getMailboxId() {
+ return mailboxId;
+ }
+
+ public List<MailboxAnnotation> getAnnotations() {
+ return annotations;
+ }
+
+ public MailboxWithAnnotationsArchiveEntry appendAnnotation(MailboxAnnotation annotation) {
+ ImmutableList<MailboxAnnotation> newAnnotations = ImmutableList.<MailboxAnnotation>builder().addAll(annotations).add(annotation).build();
+ return new MailboxWithAnnotationsArchiveEntry(mailboxName, mailboxId, newAnnotations);
+ }
+
+ @Override
+ public ArchiveEntryType getType() {
+ return ArchiveEntryType.MAILBOX;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof MailboxWithAnnotationsArchiveEntry) {
+ MailboxWithAnnotationsArchiveEntry that = (MailboxWithAnnotationsArchiveEntry) o;
+ return Objects.equals(mailboxName, that.mailboxName) &&
+ Objects.equals(mailboxId, that.mailboxId) &&
+ Objects.equals(annotations, that.annotations);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mailboxName, mailboxId, annotations);
+ }
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MessageArchiveEntry.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MessageArchiveEntry.java
new file mode 100644
index 0000000..3b3a6f9
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/MessageArchiveEntry.java
@@ -0,0 +1,72 @@
+/****************************************************************
+ * 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.InputStream;
+import java.util.Date;
+
+import javax.mail.Flags;
+
+public class MessageArchiveEntry implements MailArchiveEntry {
+
+ private final SerializedMessageId messageId;
+ private final SerializedMailboxId mailboxId;
+ private final long size;
+ private final Date internalDate;
+ private final Flags flags;
+ private final InputStream content;
+
+ public MessageArchiveEntry(SerializedMessageId messageId, SerializedMailboxId mailboxId, long size, Date internalDate, Flags flags, InputStream content) {
+ this.messageId = messageId;
+ this.mailboxId = mailboxId;
+ this.size = size;
+ this.internalDate = internalDate;
+ this.flags = flags;
+ this.content = content;
+ }
+
+ public SerializedMessageId getMessageId() {
+ return messageId;
+ }
+
+ public SerializedMailboxId getMailboxId() {
+ return mailboxId;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public Date getInternalDate() {
+ return internalDate;
+ }
+
+ public Flags getFlags() {
+ return flags;
+ }
+
+ public InputStream getContent() {
+ return content;
+ }
+
+ @Override
+ public ArchiveEntryType getType() {
+ return ArchiveEntryType.MESSAGE;
+ }
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMailboxId.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMailboxId.java
new file mode 100644
index 0000000..dc0fd6b
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMailboxId.java
@@ -0,0 +1,54 @@
+/****************************************************************
+ * 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 org.apache.james.mailbox.model.MailboxId;
+
+import com.google.common.base.Objects;
+
+public class SerializedMailboxId {
+ private final String value;
+
+ public SerializedMailboxId(String value) {
+ this.value = value;
+ }
+
+ public SerializedMailboxId(MailboxId mailboxId) {
+ this.value = mailboxId.serialize();
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof SerializedMailboxId) {
+ SerializedMailboxId that = (SerializedMailboxId) o;
+ return Objects.equal(value, that.value);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMessageId.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMessageId.java
new file mode 100644
index 0000000..76b4c6f
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/SerializedMessageId.java
@@ -0,0 +1,54 @@
+/****************************************************************
+ * 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 org.apache.james.mailbox.model.MessageId;
+
+import com.google.common.base.Objects;
+
+public class SerializedMessageId {
+ private final String value;
+
+ public SerializedMessageId(String value) {
+ this.value = value;
+ }
+
+ public SerializedMessageId(MessageId messageId) {
+ this.value = messageId.serialize();
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof SerializedMessageId) {
+ SerializedMessageId that = (SerializedMessageId) o;
+ return Objects.equal(value, that.value);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(value);
+ }
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/UnknownArchiveEntry.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/UnknownArchiveEntry.java
new file mode 100644
index 0000000..88c858c
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/UnknownArchiveEntry.java
@@ -0,0 +1,38 @@
+/****************************************************************
+ * 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;
+
+public class UnknownArchiveEntry implements MailArchiveEntry {
+
+ private final String entryName;
+
+ public UnknownArchiveEntry(String entryName) {
+ this.entryName = entryName;
+ }
+
+ public String getEntryName() {
+ return entryName;
+ }
+
+ @Override
+ public ArchiveEntryType getType() {
+ return ArchiveEntryType.UNKNOWN;
+ }
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ExtraFieldExtractor.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ExtraFieldExtractor.java
new file mode 100644
index 0000000..abc0c28
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ExtraFieldExtractor.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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.zip;
+
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+
+import org.apache.commons.compress.archivers.zip.ExtraFieldUtils;
+import org.apache.commons.compress.archivers.zip.ZipExtraField;
+import org.apache.commons.compress.archivers.zip.ZipShort;
+import org.apache.james.util.OptionalUtils;
+
+public class ExtraFieldExtractor {
+
+ public static Optional<String> getStringExtraField(ZipShort id, ZipEntry entry) throws ZipException {
+ ZipExtraField[] extraFields = ExtraFieldUtils.parse(entry.getExtra());
+ return Arrays.stream(extraFields)
+ .filter(field -> field.getHeaderId().equals(id))
+ .map(extraField -> ((StringExtraField) extraField).getValue())
+ .findFirst()
+ .flatMap(Function.identity());
+ }
+
+ public static Optional<ZipEntryType> getEntryType(ZipEntry entry) {
+ try {
+ ZipExtraField[] extraFields = ExtraFieldUtils.parse(entry.getExtra());
+ return Arrays.stream(extraFields)
+ .filter(field -> field.getHeaderId().equals(EntryTypeExtraField.ID_AQ))
+ .flatMap(extraField ->
+ OptionalUtils.toStream(((EntryTypeExtraField) extraField).getEnumValue()))
+ .findFirst();
+ } catch (Exception e) {
+ return Optional.empty();
+ }
+ }
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipArchivesLoader.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipArchivesLoader.java
new file mode 100644
index 0000000..14e6838
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipArchivesLoader.java
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.zip;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipInputStream;
+
+import org.apache.james.mailbox.backup.MailArchiveIterator;
+import org.apache.james.mailbox.backup.MailArchivesLoader;
+
+public class ZipArchivesLoader implements MailArchivesLoader {
+ @Override
+ public MailArchiveIterator load(InputStream inputStream) throws IOException {
+ ZipInputStream zipInputStream = new ZipInputStream(inputStream);
+ ZipEntryIterator zipEntryIterator = new ZipEntryIterator(zipInputStream);
+ return new ZippedMailAccountIterator(zipEntryIterator);
+ }
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipEntryIterator.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipEntryIterator.java
new file mode 100644
index 0000000..160bf74
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZipEntryIterator.java
@@ -0,0 +1,80 @@
+/****************************************************************
+ * 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.zip;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ZipEntryIterator implements Iterator<ZipEntry>, Closeable {
+ private final ZipInputStream zipInputStream;
+ private Optional<ZipEntry> next;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ZipEntryIterator.class);
+
+ public ZipEntryIterator(ZipInputStream inputStream) {
+ zipInputStream = inputStream;
+ try {
+ next = Optional.ofNullable(zipInputStream.getNextEntry());
+ } catch (IOException e) {
+ //EMPTY STREAM
+ next = Optional.empty();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next.isPresent();
+ }
+
+ @Override
+ public ZipEntry next() {
+ Optional<ZipEntry> current = next;
+ if (!current.isPresent()) {
+ return null;
+ }
+
+ ZipEntry currentEntry = current.get();
+
+ advanceToNextEntry();
+
+ return currentEntry;
+ }
+
+ private void advanceToNextEntry() {
+ try {
+ next = Optional.ofNullable(zipInputStream.getNextEntry());
+ } catch (IOException e) {
+ LOGGER.error("Error when reading archive", e);
+ next = Optional.empty();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ zipInputStream.close();
+ }
+
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZippedMailAccountIterator.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZippedMailAccountIterator.java
new file mode 100644
index 0000000..5b1d5c2
--- /dev/null
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/ZippedMailAccountIterator.java
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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.zip;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.james.mailbox.backup.MailArchiveEntry;
+import org.apache.james.mailbox.backup.MailArchiveIterator;
+import org.apache.james.mailbox.backup.MailboxWithAnnotationsArchiveEntry;
+import org.apache.james.mailbox.backup.SerializedMailboxId;
+import org.apache.james.mailbox.backup.UnknownArchiveEntry;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+import com.google.common.collect.ImmutableList;
+
+public class ZippedMailAccountIterator implements MailArchiveIterator {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ZippedMailAccountIterator.class);
+ private static final List<MailboxAnnotation> NO_ANNOTATION = ImmutableList.of();
+ private final ZipEntryIterator zipEntryIterator;
+ private Optional<MailboxWithAnnotationsArchiveEntry> currentMailBox;
+ private Optional<ZipEntry> next;
+
+ public ZippedMailAccountIterator(ZipEntryIterator zipEntryIterator) {
+ this.zipEntryIterator = zipEntryIterator;
+ next = Optional.ofNullable(zipEntryIterator.next());
+ }
+
+ @Override
+ public void close() throws IOException {
+ zipEntryIterator.close();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next.isPresent();
+ }
+
+ @Override
+ public MailArchiveEntry next() {
+ return next.map(this::doNext).orElseThrow(() -> new NoSuchElementException());
+ }
+
+ private MailArchiveEntry doNext(ZipEntry currentElement) {
+ next = Optional.ofNullable(zipEntryIterator.next());
+ try {
+ return getMailArchiveEntry(currentElement);
+ } catch (Exception e) {
+ LOGGER.error("Error when reading archive on entry : " + currentElement.getName(), e);
+ next = Optional.empty();
+ return new UnknownArchiveEntry(currentElement.getName());
+ }
+ }
+
+ private MailArchiveEntry getMailArchiveEntry(ZipEntry currentElement) throws Exception {
+ Optional<ZipEntryType> entryType = ExtraFieldExtractor.getEntryType(currentElement);
+ return entryType
+ .map(Throwing.<ZipEntryType, MailArchiveEntry>function(type ->
+ from(currentElement, type)).sneakyThrow()
+ )
+ .orElseGet(() -> new UnknownArchiveEntry(currentElement.getName()));
+ }
+
+ private Optional<SerializedMailboxId> getMailBoxId(ZipEntry entry) throws ZipException {
+ return ExtraFieldExtractor.getStringExtraField(MailboxIdExtraField.ID_AM, entry)
+ .map(SerializedMailboxId::new);
+ }
+
+ private String getMailboxName(ZipEntry current) {
+ return StringUtils.chop(current.getName());
+ }
+
+ private MailArchiveEntry fromMailboxEntry(ZipEntry current) throws ZipException {
+ return new MailboxWithAnnotationsArchiveEntry(getMailboxName(current), getMailBoxId(current).get(), NO_ANNOTATION);
+
+ }
+
+ private MailArchiveEntry from(ZipEntry current, ZipEntryType currentEntryType) throws ZipException {
+ switch (currentEntryType) {
+ case MAILBOX:
+ return fromMailboxEntry(current);
+ default:
+ return new UnknownArchiveEntry(current.getName());
+ }
+ }
+}
diff --git a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/Zipper.java b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/Zipper.java
index b0d2e63..0600b4b 100644
--- a/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/Zipper.java
+++ b/mailbox/backup/src/main/java/org/apache/james/mailbox/backup/zip/Zipper.java
@@ -47,7 +47,7 @@ import com.google.common.base.Charsets;
public class Zipper implements ArchiveService {
- private static final String ANNOTATION_DIRECTORY = "annotations";
+ public static final String ANNOTATION_DIRECTORY = "annotations";
private static final boolean AUTO_FLUSH = true;
private static final Logger LOGGER = LoggerFactory.getLogger(Zipper.class);
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 257a9f8..9be370a 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
@@ -51,6 +51,7 @@ public interface MailboxMessageFixture {
String OTHER_USER = "otherUser";
User USER1 = User.fromUsername(USER);
+ User USER2 = User.fromUsername(OTHER_USER);
String DATE_STRING_1 = "2018-02-15T15:54:02Z";
String DATE_STRING_2 = "2018-03-15T15:54:02Z";
@@ -60,16 +61,19 @@ public interface MailboxMessageFixture {
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));
+ byte[] MESSAGE_CONTENT_BYTES_1 = MESSAGE_CONTENT_1.getBytes(MESSAGE_CHARSET);
+ SharedByteArrayInputStream CONTENT_STREAM_1 = new SharedByteArrayInputStream(MESSAGE_CONTENT_BYTES_1);
String MESSAGE_CONTENT_2 = "Other message content";
- SharedByteArrayInputStream CONTENT_STREAM_2 = new SharedByteArrayInputStream(MESSAGE_CONTENT_2.getBytes(MESSAGE_CHARSET));
+
+ byte[] MESSAGE_CONTENT_BYTES_2 = MESSAGE_CONTENT_2.getBytes(MESSAGE_CHARSET);
+ SharedByteArrayInputStream CONTENT_STREAM_2 = new SharedByteArrayInputStream(MESSAGE_CONTENT_BYTES_2);
MessageId MESSAGE_ID_1 = MESSAGE_ID_FACTORY.generate();
MessageId MESSAGE_ID_2 = MESSAGE_ID_FACTORY.generate();
MessageId MESSAGE_ID_OTHER_USER_1 = MESSAGE_ID_FACTORY.generate();
- long SIZE_1 = 1000;
- long SIZE_2 = 2000;
+ long SIZE_1 = MESSAGE_CONTENT_BYTES_1.length;
+ long SIZE_2 = MESSAGE_CONTENT_BYTES_2.length;
long MESSAGE_UID_1_VALUE = 1111L;
long MESSAGE_UID_2_VALUE = 2222L;
long MESSAGE_UID_OTHER_USER_1_VALUE = 1111L;
@@ -79,6 +83,10 @@ public interface MailboxMessageFixture {
MailboxId MAILBOX_ID_1 = TestId.of(1L);
MailboxId MAILBOX_ID_2 = TestId.of(2L);
MailboxId MAILBOX_ID_11 = TestId.of(11L);
+
+ SerializedMailboxId SERIALIZED_MAILBOX_ID_1 = new SerializedMailboxId(MAILBOX_ID_1);
+ SerializedMailboxId SERIALIZED_MAILBOX_ID_2 = new SerializedMailboxId(MAILBOX_ID_2);
+
Flags flags1 = new Flags("myFlags");
MailboxSession MAILBOX_SESSION = MailboxSessionUtil.create(USER);
diff --git a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchivesLoaderTest.java b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchivesLoaderTest.java
new file mode 100644
index 0000000..a4054d3
--- /dev/null
+++ b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/ZipArchivesLoaderTest.java
@@ -0,0 +1,151 @@
+/****************************************************************
+ * 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.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+
+import javax.mail.Flags;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.backup.zip.ZipArchivesLoader;
+import org.apache.james.mailbox.backup.zip.Zipper;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+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 com.github.fge.lambdas.Throwing;
+
+public class ZipArchivesLoaderTest implements MailboxMessageFixture {
+ private static final int BUFFER_SIZE = 4096;
+
+ private final ArchiveService archiveService = new Zipper();
+ private final MailArchivesLoader archiveLoader = new ZipArchivesLoader();
+
+ private MailboxManager mailboxManager;
+ private DefaultMailboxBackup backup;
+
+ @BeforeEach
+ void beforeEach() {
+ mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
+ backup = new DefaultMailboxBackup(mailboxManager, archiveService);
+ }
+
+ 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 ->
+ {
+ MessageManager.AppendCommand appendCommand = MessageManager.AppendCommand.builder()
+ .withFlags(message.createFlags())
+ .build(message.getFullContent());
+ mailboxManager.getMailbox(mailboxId, session).appendMessage(appendCommand, session);
+ }
+ )
+ );
+ }
+
+ @Test
+ void mailAccountIteratorFromEmptyArchiveShouldThrowNoSuchElementException() throws Exception {
+ ByteArrayOutputStream destination = new ByteArrayOutputStream(BUFFER_SIZE);
+ backup.backupAccount(USER1, destination);
+
+ InputStream source = new ByteArrayInputStream(destination.toByteArray());
+ MailArchiveIterator mailArchiveIterator = archiveLoader.load(source);
+
+ assertThat(mailArchiveIterator.hasNext()).isEqualTo(false);
+ assertThatThrownBy(() -> mailArchiveIterator.next()).isInstanceOf(NoSuchElementException.class);
+ }
+
+ @Test
+ void callingNextSeveralTimeOnAnEmptyIteratorShouldThrowNoSuchElementException() throws Exception {
+ ByteArrayOutputStream destination = new ByteArrayOutputStream(BUFFER_SIZE);
+ backup.backupAccount(USER1, destination);
+
+ InputStream source = new ByteArrayInputStream(destination.toByteArray());
+ MailArchiveIterator mailArchiveIterator = archiveLoader.load(source);
+
+ assertThat(mailArchiveIterator.hasNext()).isEqualTo(false);
+ assertThatThrownBy(() -> mailArchiveIterator.next()).isInstanceOf(NoSuchElementException.class);
+ assertThatThrownBy(() -> mailArchiveIterator.next()).isInstanceOf(NoSuchElementException.class);
+ assertThatThrownBy(() -> mailArchiveIterator.next()).isInstanceOf(NoSuchElementException.class);
+ }
+
+ @Test
+ void mailAccountIteratorFromArchiveWithOneMailboxShouldContainOneMailbox() throws Exception {
+ MailboxSession session = mailboxManager.createSystemSession(USER);
+ createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX1);
+
+ ByteArrayOutputStream destination = new ByteArrayOutputStream(BUFFER_SIZE);
+ backup.backupAccount(USER1, destination);
+
+ InputStream source = new ByteArrayInputStream(destination.toByteArray());
+ MailArchiveIterator mailArchiveIterator = archiveLoader.load(source);
+ assertThat(mailArchiveIterator.hasNext()).isEqualTo(true);
+
+ MailboxWithAnnotationsArchiveEntry expectedMailbox = new MailboxWithAnnotationsArchiveEntry(MAILBOX_1_NAME, SERIALIZED_MAILBOX_ID_1, NO_ANNOTATION);
+ MailboxWithAnnotationsArchiveEntry resultMailbox = (MailboxWithAnnotationsArchiveEntry) mailArchiveIterator.next();
+ verifyMailboxArchiveEntry(mailArchiveIterator, expectedMailbox, resultMailbox, false);
+ }
+
+ @Test
+ void mailAccountIteratorFromArchiveWithTwoMailboxesShouldContainTwoMailboxes() throws Exception {
+ MailboxSession session = mailboxManager.createSystemSession(USER);
+ createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX1);
+ createMailBoxWithMessage(session, MAILBOX_PATH_USER1_MAILBOX2);
+
+ ByteArrayOutputStream destination = new ByteArrayOutputStream(BUFFER_SIZE);
+ backup.backupAccount(USER1, destination);
+
+ InputStream source = new ByteArrayInputStream(destination.toByteArray());
+ MailArchiveIterator mailArchiveIterator = archiveLoader.load(source);
+ assertThat(mailArchiveIterator.hasNext()).isEqualTo(true);
+
+ MailboxWithAnnotationsArchiveEntry expectedMailbox = new MailboxWithAnnotationsArchiveEntry(MAILBOX_1_NAME, SERIALIZED_MAILBOX_ID_1, NO_ANNOTATION);
+ MailboxWithAnnotationsArchiveEntry resultMailbox = (MailboxWithAnnotationsArchiveEntry) mailArchiveIterator.next();
+ verifyMailboxArchiveEntry(mailArchiveIterator, expectedMailbox, resultMailbox, true);
+
+ MailboxWithAnnotationsArchiveEntry expectedSecondMailbox = new MailboxWithAnnotationsArchiveEntry(MAILBOX_2_NAME, SERIALIZED_MAILBOX_ID_2, NO_ANNOTATION);
+ MailboxWithAnnotationsArchiveEntry resultSecondMailbox = (MailboxWithAnnotationsArchiveEntry) mailArchiveIterator.next();
+ verifyMailboxArchiveEntry(mailArchiveIterator, expectedSecondMailbox, resultSecondMailbox, false);
+ }
+
+ private void verifyMailboxArchiveEntry(MailArchiveIterator mailArchiveIterator, MailboxWithAnnotationsArchiveEntry expectedMailbox,
+ MailboxWithAnnotationsArchiveEntry resultMailbox, boolean iteratorHasNextElement) {
+ assertThat(resultMailbox.getMailboxId()).isEqualTo(expectedMailbox.getMailboxId());
+ assertThat(resultMailbox.getMailboxName()).isEqualTo(expectedMailbox.getMailboxName());
+ assertThat(resultMailbox.getAnnotations()).isEqualTo(expectedMailbox.getAnnotations());
+ assertThat(resultMailbox).isEqualTo(expectedMailbox);
+ assertThat(mailArchiveIterator.hasNext()).isEqualTo(iteratorHasNextElement);
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org