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 2016/08/29 13:28:10 UTC

[13/17] james-project git commit: JAMES-1818 Simplify loadMessage by introducing MetaDataWithContent

JAMES-1818 Simplify loadMessage by introducing MetaDataWithContent


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

Branch: refs/heads/master
Commit: 7bfb0cd84ceb8cf8a0093de9e1b83862dae820df
Parents: 844a740
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Wed Aug 24 12:01:17 2016 +0200
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Mon Aug 29 15:16:28 2016 +0200

----------------------------------------------------------------------
 .../cucumber/GetMessagesMethodStepdefs.java     |   2 +-
 .../james/jmap/methods/GetMessagesMethod.java   | 131 +----
 .../apache/james/jmap/model/MessageFactory.java | 246 ++++++++--
 .../james/jmap/model/MailboxMessageTest.java    | 480 -------------------
 .../james/jmap/model/MessageFactoryTest.java    | 262 ++++++++++
 .../apache/james/jmap/model/MessageTest.java    | 256 ++++++++++
 6 files changed, 743 insertions(+), 634 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
index e12a0f4..437078d 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
@@ -281,7 +281,7 @@ public class GetMessagesMethodStepdefs {
 
     @Then("^the headers of the message contains:$")
     public void assertHeadersOfTheFirstMessage(DataTable headers) throws Throwable {
-        assertThat(jsonPath.<Map<String, String>>read(FIRST_MESSAGE + ".headers")).isEqualTo(headers.asMap(String.class, String.class));
+        assertThat(jsonPath.<Map<String, String>>read(FIRST_MESSAGE + ".headers")).containsAllEntriesOf(headers.asMap(String.class, String.class));
     }
 
     @Then("^the date of the message is \"([^\"]*)\"$")

http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
index 1ae5a60..d2789b6 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMessagesMethod.java
@@ -23,7 +23,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 
 import javax.inject.Inject;
 
@@ -34,20 +33,17 @@ import org.apache.james.jmap.model.GetMessagesRequest;
 import org.apache.james.jmap.model.GetMessagesResponse;
 import org.apache.james.jmap.model.Message;
 import org.apache.james.jmap.model.MessageFactory;
+import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
-import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.FetchGroupImpl;
-import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.MessageAttachment;
-import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MessageResultIterator;
-import org.javatuples.Triplet;
+import org.apache.james.util.streams.Iterators;
 
 import com.fasterxml.jackson.databind.ser.PropertyFilter;
 import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
@@ -113,8 +109,8 @@ public class GetMessagesMethod implements Method {
     private GetMessagesResponse getMessagesResponse(MailboxSession mailboxSession, GetMessagesRequest getMessagesRequest) {
         getMessagesRequest.getAccountId().ifPresent(GetMessagesMethod::notImplemented);
         
-        Function<MessageId, Stream<CompletedMessageResult>> loadMessages = loadMessage(mailboxSession);
-        Function<CompletedMessageResult, Message> convertToJmapMessage = toJmapMessage(mailboxSession);
+        Function<MessageId, Stream<MetaDataWithContent>> loadMessages = loadMessage(mailboxSession);
+        Function<MetaDataWithContent, Message> convertToJmapMessage = Throwing.function(messageFactory::fromMetaDataWithContent).sneakyThrow();
         
         List<Message> result = getMessagesRequest.getIds().stream()
             .flatMap(loadMessages)
@@ -128,110 +124,17 @@ public class GetMessagesMethod implements Method {
         throw new NotImplementedException();
     }
 
-    
-    private Function<CompletedMessageResult, Message> toJmapMessage(MailboxSession mailboxSession) {
-        ThrowingFunction<CompletedMessageResult, Message> function = (completedMessageResult) -> messageFactory.fromMessageResult(
-                completedMessageResult.messageResult,
-                completedMessageResult.attachments,
-                completedMessageResult.mailboxId,
-                uid -> new MessageId(mailboxSession.getUser(), completedMessageResult.mailboxPath , uid));
-        return Throwing.function(function).sneakyThrow();
-    }
-
-    private Function<MessageId, Stream<CompletedMessageResult>> 
-                loadMessage(MailboxSession mailboxSession) {
-
-        return Throwing
-                .function((MessageId messageId) -> {
-                     MailboxPath mailboxPath = messageId.getMailboxPath();
-                     MessageManager messageManager = mailboxManager.getMailbox(messageId.getMailboxPath(), mailboxSession);
-                     return Triplet.with(
-                             messageManager.getMessages(messageId.getUidAsRange(), FetchGroupImpl.FULL_CONTENT, mailboxSession),
-                             mailboxPath,
-                             messageManager.getId()
-                             );
-                })
-                .andThen(Throwing.function((triplet) -> retrieveCompleteMessageResults(triplet, mailboxSession)));
-    }
-    
-    private Stream<CompletedMessageResult> retrieveCompleteMessageResults(Triplet<MessageResultIterator, MailboxPath, MailboxId> value, MailboxSession mailboxSession) throws MailboxException {
-        Iterable<MessageResult> iterable = () -> value.getValue0();
-        Stream<MessageResult> targetStream = StreamSupport.stream(iterable.spliterator(), false);
-
-        MailboxPath mailboxPath = value.getValue1();
-        MailboxId mailboxId = value.getValue2();
-        return targetStream
-                .map(Throwing.function(this::initializeBuilder).sneakyThrow())
-                .map(builder -> builder.mailboxId(mailboxId))
-                .map(builder -> builder.mailboxPath(mailboxPath))
-                .map(builder -> builder.build()); 
-    }
-    
-    private CompletedMessageResult.Builder initializeBuilder(MessageResult message) throws MailboxException {
-        return CompletedMessageResult.builder()
-                .messageResult(message)
-                .attachments(message.getAttachments());
-    }
-
-    private static class CompletedMessageResult {
-
-        public static Builder builder() {
-            return new Builder();
-        }
-
-        public static class Builder {
-
-            private MessageResult messageResult;
-            private List<MessageAttachment> attachments;
-            private MailboxPath mailboxPath;
-            private MailboxId mailboxId;
-
-            private Builder() {
-            }
-
-            public Builder messageResult(MessageResult messageResult) {
-                Preconditions.checkArgument(messageResult != null);
-                this.messageResult = messageResult;
-                return this;
-            }
-
-            public Builder attachments(List<MessageAttachment> attachments) {
-                Preconditions.checkArgument(attachments != null);
-                this.attachments = attachments;
-                return this;
-            }
-
-            public Builder mailboxPath(MailboxPath mailboxPath) {
-                Preconditions.checkArgument(mailboxPath != null);
-                this.mailboxPath = mailboxPath;
-                return this;
-            }
-
-            public Builder mailboxId(MailboxId mailboxId) {
-                Preconditions.checkArgument(mailboxId != null);
-                this.mailboxId = mailboxId;
-                return this;
-            }
-
-            public CompletedMessageResult build() {
-                Preconditions.checkState(messageResult != null);
-                Preconditions.checkState(attachments != null);
-                Preconditions.checkState(mailboxPath != null);
-                Preconditions.checkState(mailboxId != null);
-                return new CompletedMessageResult(messageResult, attachments, mailboxPath, mailboxId);
-            }
-        }
-
-        private final MessageResult messageResult;
-        private final List<MessageAttachment> attachments;
-        private final MailboxPath mailboxPath;
-        private final MailboxId mailboxId;
-
-        public CompletedMessageResult(MessageResult messageResult, List<MessageAttachment> attachments, MailboxPath mailboxPath, MailboxId mailboxId) {
-            this.messageResult = messageResult;
-            this.attachments = attachments;
-            this.mailboxPath = mailboxPath;
-            this.mailboxId = mailboxId;
-        }
+    private Function<MessageId, Stream<MetaDataWithContent>> loadMessage(MailboxSession mailboxSession) {
+        ThrowingFunction<MessageId, Stream<MetaDataWithContent>> toMetaDataWithContentStream = (MessageId messageId) -> {
+            MailboxPath mailboxPath = messageId.getMailboxPath();
+            MessageManager messageManager = mailboxManager.getMailbox(mailboxPath, mailboxSession);
+            MessageResultIterator messageResultIterator = messageManager.getMessages(messageId.getUidAsRange(), FetchGroupImpl.FULL_CONTENT, mailboxSession);
+            return Iterators.toStream(messageResultIterator)
+                    .map(Throwing.function(MetaDataWithContent::builderFromMessageResult).sneakyThrow())
+                    .map(builder -> builder.mailboxId(messageManager.getId()))
+                    .map(builder -> builder.messageId(messageId))
+                    .map(MetaDataWithContent.Builder::build);
+        };
+        return Throwing.function(toMetaDataWithContentStream).sneakyThrow();
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
index b401b62..c92293c 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
@@ -19,6 +19,7 @@
 package org.apache.james.jmap.model;
 
 import java.io.IOException;
+import java.io.InputStream;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.Collection;
@@ -28,6 +29,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -41,6 +43,7 @@ import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Cid;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
@@ -51,6 +54,7 @@ import org.apache.james.mime4j.message.MessageBuilder;
 import org.apache.james.mime4j.stream.Field;
 
 import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -71,47 +75,54 @@ public class MessageFactory {
         this.messageContentExtractor = messageContentExtractor;
     }
 
-    public Message fromMessageResult(MessageResult messageResult,
-            List<MessageAttachment> attachments,
-            MailboxId mailboxId,
-            Function<Long, MessageId> uidToMessageId) throws MailboxException {
-        MessageId messageId = uidToMessageId.apply(messageResult.getUid());
-
-        MessageBuilder parsedMessageResult;
-        MessageContent messageContent;
-        try {
-            parsedMessageResult = MessageBuilder.read(messageResult.getFullContent().getInputStream());
-            messageContent = messageContentExtractor.extract(parsedMessageResult.build());
-        } catch (IOException e) {
-            throw new MailboxException("Unable to parse message: " + e.getMessage(), e);
-        }
+    public Message fromMetaDataWithContent(MetaDataWithContent message) throws MailboxException {
+        org.apache.james.mime4j.dom.Message mimeMessage = parse(message);
+        MessageContent messageContent = extractContent(mimeMessage);
 
         return Message.builder()
-                .id(messageId)
-                .blobId(BlobId.of(String.valueOf(messageResult.getUid())))
-                .threadId(messageId.serialize())
-                .mailboxIds(ImmutableList.of(mailboxId.serialize()))
-                .inReplyToMessageId(getHeader(parsedMessageResult, "in-reply-to"))
-                .isUnread(! messageResult.getFlags().contains(Flags.Flag.SEEN))
-                .isFlagged(messageResult.getFlags().contains(Flags.Flag.FLAGGED))
-                .isAnswered(messageResult.getFlags().contains(Flags.Flag.ANSWERED))
-                .isDraft(messageResult.getFlags().contains(Flags.Flag.DRAFT))
-                .subject(Strings.nullToEmpty(parsedMessageResult.getSubject()))
-                .headers(toMap(parsedMessageResult.getFields()))
-                .from(firstFromMailboxList(parsedMessageResult.getFrom()))
-                .to(fromAddressList(parsedMessageResult.getTo()))
-                .cc(fromAddressList(parsedMessageResult.getCc()))
-                .bcc(fromAddressList(parsedMessageResult.getBcc()))
-                .replyTo(fromAddressList(parsedMessageResult.getReplyTo()))
-                .size(parsedMessageResult.getSize())
-                .date(toZonedDateTime(messageResult.getInternalDate()))
+                .id(message.getMessageId())
+                .blobId(BlobId.of(String.valueOf(message.getUid())))
+                .threadId(message.getMessageId().serialize())
+                .mailboxIds(ImmutableList.of(message.getMailboxId().serialize()))
+                .inReplyToMessageId(getHeader(mimeMessage, "in-reply-to"))
+                .isUnread(! message.getFlags().contains(Flags.Flag.SEEN))
+                .isFlagged(message.getFlags().contains(Flags.Flag.FLAGGED))
+                .isAnswered(message.getFlags().contains(Flags.Flag.ANSWERED))
+                .isDraft(message.getFlags().contains(Flags.Flag.DRAFT))
+                .subject(Strings.nullToEmpty(mimeMessage.getSubject()).trim())
+                .headers(toMap(mimeMessage.getHeader().getFields()))
+                .from(firstFromMailboxList(mimeMessage.getFrom()))
+                .to(fromAddressList(mimeMessage.getTo()))
+                .cc(fromAddressList(mimeMessage.getCc()))
+                .bcc(fromAddressList(mimeMessage.getBcc()))
+                .replyTo(fromAddressList(mimeMessage.getReplyTo()))
+                .size(message.getSize())
+                .date(message.getInternalDateAsZonedDateTime())
                 .textBody(messageContent.getTextBody().orElse(null))
                 .htmlBody(messageContent.getHtmlBody().orElse(null))
                 .preview(getPreview(messageContent))
-                .attachments(getAttachments(attachments))
+                .attachments(getAttachments(message.getAttachments()))
                 .build();
     }
 
+    private org.apache.james.mime4j.dom.Message parse(MetaDataWithContent message) throws MailboxException {
+        try {
+            return MessageBuilder.read(message.getContent())
+                    .setDate(message.getInternalDate(), TimeZone.getTimeZone(UTC_ZONE_ID))
+                    .build();
+        } catch (IOException e) {
+            throw new MailboxException("Unable to parse message: " + e.getMessage(), e);
+        }
+    }
+
+    private MessageContent extractContent(org.apache.james.mime4j.dom.Message mimeMessage) throws MailboxException {
+        try {
+            return messageContentExtractor.extract(mimeMessage);
+        } catch (IOException e) {
+            throw new MailboxException("Unable to extract content: " + e.getMessage(), e);
+        }
+    }
+
     public Message fromMailboxMessage(MailboxMessage mailboxMessage,
             List<MessageAttachment> attachments,
             Function<Long, MessageId> uidToMessageId) {
@@ -242,8 +253,8 @@ public class MessageFactory {
                 .collect(Guavate.toImmutableMap(Map.Entry::getKey, bodyConcatenator));
     }
     
-    private String getHeader(MessageBuilder message, String header) {
-        Field field = message.getField(header);
+    private String getHeader(org.apache.james.mime4j.dom.Message message, String header) {
+        Field field = message.getHeader().getField(header);
         if (field == null) {
             return null;
         }
@@ -262,10 +273,6 @@ public class MessageFactory {
         return ZonedDateTime.ofInstant(mailboxMessage.getInternalDate().toInstant(), UTC_ZONE_ID);
     }
 
-    private ZonedDateTime toZonedDateTime(Date date) {
-        return ZonedDateTime.ofInstant(date.toInstant(), UTC_ZONE_ID);
-    }
-
     private String getTextBody(IndexableMessage im) {
         return im.getBodyText().map(Strings::emptyToNull).orElse(null);
     }
@@ -290,4 +297,165 @@ public class MessageFactory {
                     .isInline(attachment.isInline())
                     .build();
     }
+
+    public static class MetaDataWithContent implements MessageMetaData {
+        public static Builder builder() {
+            return new Builder();
+        }
+        
+        public static Builder builderFromMessageResult(MessageResult messageResult) throws MailboxException {
+            Builder builder = builder()
+                .uid(messageResult.getUid())
+                .modSeq(messageResult.getModSeq())
+                .flags(messageResult.getFlags())
+                .size(messageResult.getSize())
+                .internalDate(messageResult.getInternalDate())
+                .attachments(messageResult.getAttachments());
+            try {
+                return builder.content(messageResult.getFullContent().getInputStream());
+            } catch (IOException e) {
+                throw new MailboxException("Can't get message full content: " + e.getMessage(), e);
+            }
+        }
+        
+        public static class Builder {
+            private Long uid;
+            private Long modSeq;
+            private Flags flags;
+            private Long size;
+            private Date internalDate;
+            private InputStream content;
+            private List<MessageAttachment> attachments;
+            private MailboxId mailboxId;
+            private MessageId messageId;
+
+            public Builder uid(long uid) {
+                this.uid = uid;
+                return this;
+            }
+            
+            public Builder modSeq(long modSeq) {
+                this.modSeq = modSeq;
+                return this;
+            }
+            
+            public Builder flags(Flags flags) {
+                this.flags = flags;
+                return this;
+            }
+            
+            public Builder size(long size) {
+                this.size = size;
+                return this;
+            }
+            
+            public Builder internalDate(Date internalDate) {
+                this.internalDate = internalDate;
+                return this;
+            }
+            
+            public Builder content(InputStream content) {
+                this.content = content;
+                return this;
+            }
+            
+            public Builder attachments(List<MessageAttachment> attachments) {
+                this.attachments = attachments;
+                return this;
+            }
+            
+            public Builder mailboxId(MailboxId mailboxId) {
+                this.mailboxId = mailboxId;
+                return this;
+            }
+            
+            public Builder messageId(MessageId messageId) {
+                this.messageId = messageId;
+                return this;
+            }
+            
+            public MetaDataWithContent build() {
+                Preconditions.checkArgument(uid != null);
+                if (modSeq == null) {
+                    modSeq = -1L;
+                }
+                Preconditions.checkArgument(flags != null);
+                Preconditions.checkArgument(size != null);
+                Preconditions.checkArgument(internalDate != null);
+                Preconditions.checkArgument(content != null);
+                Preconditions.checkArgument(attachments != null);
+                Preconditions.checkArgument(mailboxId != null);
+                Preconditions.checkArgument(messageId != null);
+                return new MetaDataWithContent(uid, modSeq, flags, size, internalDate, content, attachments, mailboxId, messageId);
+            }
+        }
+
+        private final long uid;
+        private final long modSeq;
+        private final Flags flags;
+        private final long size;
+        private final Date internalDate;
+        private final InputStream content;
+        private final List<MessageAttachment> attachments;
+        private final MailboxId mailboxId;
+        private final MessageId messageId;
+
+        private MetaDataWithContent(long uid, long modSeq, Flags flags, long size, Date internalDate, InputStream content, List<MessageAttachment> attachments, MailboxId mailboxId, MessageId messageId) {
+            this.uid = uid;
+            this.modSeq = modSeq;
+            this.flags = flags;
+            this.size = size;
+            this.internalDate = internalDate;
+            this.content = content;
+            this.attachments = attachments;
+            this.mailboxId = mailboxId;
+            this.messageId = messageId;
+        }
+
+        @Override
+        public long getUid() {
+            return uid;
+        }
+
+        @Override
+        public long getModSeq() {
+            return modSeq;
+        }
+
+        @Override
+        public Flags getFlags() {
+            return flags;
+        }
+
+        @Override
+        public long getSize() {
+            return size;
+        }
+
+        @Override
+        public Date getInternalDate() {
+            return internalDate;
+        }
+
+        public ZonedDateTime getInternalDateAsZonedDateTime() {
+            return ZonedDateTime.ofInstant(internalDate.toInstant(), UTC_ZONE_ID);
+        }
+
+        public InputStream getContent() {
+            return content;
+        }
+
+        public List<MessageAttachment> getAttachments() {
+            return attachments;
+        }
+
+        public MailboxId getMailboxId() {
+            return mailboxId;
+        }
+
+        public MessageId getMessageId() {
+            return messageId;
+        }
+
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MailboxMessageTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MailboxMessageTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MailboxMessageTest.java
deleted file mode 100644
index da0f386..0000000
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MailboxMessageTest.java
+++ /dev/null
@@ -1,480 +0,0 @@
-/****************************************************************
- * 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.jmap.model;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
-import java.util.Date;
-import java.util.Optional;
-
-import javax.mail.Flags;
-import javax.mail.Flags.Flag;
-import javax.mail.util.SharedByteArrayInputStream;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.james.jmap.utils.HtmlTextExtractor;
-import org.apache.james.mailbox.model.AttachmentId;
-import org.apache.james.mailbox.model.Cid;
-import org.apache.james.mailbox.model.MessageAttachment;
-import org.apache.james.mailbox.store.TestId;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-
-public class MailboxMessageTest {
-    private static final TestId MAILBOX_ID = TestId.of(18L);
-    private static final long MOD_SEQ = 42L;
-    private static final ZoneId UTC_ZONE_ID = ZoneId.of("Z");
-    private static final ZonedDateTime ZONED_DATE = ZonedDateTime.of(2015, 07, 14, 12, 30, 42, 0, UTC_ZONE_ID);
-    private static final Date INTERNAL_DATE = Date.from(ZONED_DATE.toInstant());
-
-    private MessageFactory messageFactory;
-    private MessagePreviewGenerator messagePreview ;
-    private HtmlTextExtractor htmlTextExtractor;
-    
-    @Before
-    public void setUp() {
-        htmlTextExtractor = mock(HtmlTextExtractor.class);
-        messagePreview = new MessagePreviewGenerator(htmlTextExtractor);
-        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
-        messageFactory = new MessageFactory(messagePreview, messageContentExtractor);
-    }
-    
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenIdIsNull() {
-        Message.builder().build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenBlobIdIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenThreadIdIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenThreadIdIsEmpty() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("").build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenMailboxIdsIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenHeadersIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenSubjectIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of()).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenSubjectIsEmpty() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
-            .subject("").build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenSizeIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
-            .subject("subject").build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenDateIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
-            .subject("subject").size(123).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenPreviewIsNull() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
-            .subject("subject").size(123).date(ZonedDateTime.now()).build();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenPreviewIsEmpty() {
-        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
-            .subject("subject").size(123).date(ZonedDateTime.now()).preview("").build();
-    }
-
-    @Test
-    public void buildShouldWorkWhenMandatoryFieldsArePresent() {
-        ZonedDateTime currentDate = ZonedDateTime.now();
-        Message expected = new Message(MessageId.of("user|box|1"), BlobId.of("blobId"), "threadId", ImmutableList.of("mailboxId"), Optional.empty(), false, false, false, false, false, ImmutableMap.of("key", "value"), Optional.empty(),
-                ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), "subject", currentDate, 123, "preview", Optional.empty(), Optional.empty(), ImmutableList.of(), ImmutableMap.of());
-        Message tested = Message.builder()
-                .id(MessageId.of("user|box|1"))
-                .blobId(BlobId.of("blobId"))
-                .threadId("threadId")
-                .mailboxIds(ImmutableList.of("mailboxId"))
-                .headers(ImmutableMap.of("key", "value"))
-                .subject("subject")
-                .size(123)
-                .date(currentDate)
-                .preview("preview")
-                .build();
-        assertThat(tested).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void buildShouldThrowWhenAttachedMessageIsNotMatchingAttachments() {
-        Attachment simpleAttachment = Attachment.builder().blobId(BlobId.of("blobId")).type("type").name("name").size(123).build();
-        ImmutableList<Attachment> attachments = ImmutableList.of(simpleAttachment);
-        SubMessage simpleMessage = SubMessage.builder()
-                .headers(ImmutableMap.of("key", "value"))
-                .subject("subject")
-                .date(ZonedDateTime.now())
-                .build();
-        ImmutableMap<BlobId, SubMessage> attachedMessages = ImmutableMap.of(BlobId.of("differentBlobId"), simpleMessage);
-        Message.builder()
-            .id(MessageId.of("user|box|1"))
-            .blobId(BlobId.of("blobId"))
-            .threadId("threadId")
-            .mailboxIds(ImmutableList.of("mailboxId"))
-            .headers(ImmutableMap.of("key", "value"))
-            .subject("subject")
-            .size(123)
-            .date(ZonedDateTime.now())
-            .preview("preview")
-            .attachments(attachments)
-            .attachedMessages(attachedMessages)
-            .build();
-    }
-
-    @Test
-    public void buildShouldWorkWhenAllFieldsArePresent() {
-        Emailer from = Emailer.builder().name("from").email("from@domain").build();
-        ImmutableList<Emailer> to = ImmutableList.of(Emailer.builder().name("to").email("to@domain").build());
-        ImmutableList<Emailer> cc = ImmutableList.of(Emailer.builder().name("cc").email("cc@domain").build());
-        ImmutableList<Emailer> bcc = ImmutableList.of(Emailer.builder().name("bcc").email("bcc@domain").build());
-        ImmutableList<Emailer> replyTo = ImmutableList.of(Emailer.builder().name("replyTo").email("replyTo@domain").build());
-        ZonedDateTime currentDate = ZonedDateTime.now();
-        Attachment simpleAttachment = Attachment.builder().blobId(BlobId.of("blobId")).type("type").name("name").size(123).build();
-        ImmutableList<Attachment> attachments = ImmutableList.of(simpleAttachment);
-        SubMessage simpleMessage = SubMessage.builder()
-                .headers(ImmutableMap.of("key", "value"))
-                .subject("subject")
-                .date(currentDate)
-                .build();
-        ImmutableMap<BlobId, SubMessage> attachedMessages = ImmutableMap.of(BlobId.of("blobId"), simpleMessage);
-        Message expected = new Message(
-                MessageId.of("user|box|1"),
-                BlobId.of("blobId"),
-                "threadId",
-                ImmutableList.of("mailboxId"),
-                Optional.of("inReplyToMessageId"), 
-                true,
-                true,
-                true,
-                true,
-                true,
-                ImmutableMap.of("key", "value"),
-                Optional.of(from),
-                to,
-                cc,
-                bcc,
-                replyTo,
-                "subject",
-                currentDate,
-                123,
-                "preview",
-                Optional.of("textBody"), 
-                Optional.of("htmlBody"),
-                attachments,
-                attachedMessages);
-        Message tested = Message.builder()
-            .id(MessageId.of("user|box|1"))
-            .blobId(BlobId.of("blobId"))
-            .threadId("threadId")
-            .mailboxIds(ImmutableList.of("mailboxId"))
-            .inReplyToMessageId("inReplyToMessageId")
-            .isUnread(true)
-            .isFlagged(true)
-            .isAnswered(true)
-            .isDraft(true)
-            .headers(ImmutableMap.of("key", "value"))
-            .from(from)
-            .to(to)
-            .cc(cc)
-            .bcc(bcc)
-            .replyTo(replyTo)
-            .subject("subject")
-            .date(currentDate)
-            .size(123)
-            .preview("preview")
-            .textBody("textBody")
-            .htmlBody("htmlBody")
-            .attachments(attachments)
-            .attachedMessages(attachedMessages)
-            .build();
-        assertThat(tested).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void emptyMailShouldBeLoadedIntoMessage() throws Exception {
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                0,
-                0,
-                new SharedByteArrayInputStream("".getBytes()),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        assertThat(testee)
-            .extracting(Message::getPreview, Message::getSize, Message::getSubject, Message::getHeaders, Message::getDate)
-            .containsExactly("(Empty)", 0L, "", ImmutableMap.of(), ZONED_DATE);
-    }
-
-    @Test
-    public void flagsShouldBeSetIntoMessage() throws Exception {
-        Flags flags = new Flags();
-        flags.add(Flag.ANSWERED);
-        flags.add(Flag.FLAGGED);
-        flags.add(Flag.DRAFT);
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                0,
-                0,
-                new SharedByteArrayInputStream("".getBytes()),
-                flags,
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        assertThat(testee)
-            .extracting(Message::isIsUnread, Message::isIsFlagged, Message::isIsAnswered, Message::isIsDraft)
-            .containsExactly(true, true, true, true);
-    }
-
-    @Test
-    public void headersShouldBeSetIntoMessage() throws Exception {
-        String headers = "From: user <us...@domain>\n"
-                + "Subject: test subject\n"
-                + "To: user1 <us...@domain>, user2 <us...@domain>\n"
-                + "Cc: usercc <us...@domain>\n"
-                + "Bcc: userbcc <us...@domain>\n"
-                + "Reply-To: \"user to reply to\" <us...@domain>\n"
-                + "In-Reply-To: <SN...@phx.gbl>\n"
-                + "Other-header: other header value";
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                headers.length(),
-                headers.length(),
-                new SharedByteArrayInputStream(headers.getBytes()),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-
-        Emailer user = Emailer.builder().name("user").email("user@domain").build();
-        Emailer user1 = Emailer.builder().name("user1").email("user1@domain").build();
-        Emailer user2 = Emailer.builder().name("user2").email("user2@domain").build();
-        Emailer usercc = Emailer.builder().name("usercc").email("usercc@domain").build();
-        Emailer userbcc = Emailer.builder().name("userbcc").email("userbcc@domain").build();
-        Emailer userRT = Emailer.builder().name("user to reply to").email("user.reply.to@domain").build();
-        ImmutableMap<String, String> headersMap = ImmutableMap.<String, String>builder()
-                .put("cc", "usercc <us...@domain>")
-                .put("bcc", "userbcc <us...@domain>")
-                .put("subject", "test subject")
-                .put("from", "user <us...@domain>")
-                .put("to", "user1 <us...@domain>, user2 <us...@domain>")
-                .put("reply-to", "\"user to reply to\" <us...@domain>")
-                .put("in-reply-to", "<SN...@phx.gbl>")
-                .put("other-header", "other header value")
-                .build();
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        Message expected = Message.builder()
-                .id(MessageId.of("user|box|0"))
-                .blobId(BlobId.of("0"))
-                .threadId("user|box|0")
-                .mailboxIds(ImmutableList.of(MAILBOX_ID.serialize()))
-                .inReplyToMessageId("<SN...@phx.gbl>")
-                .headers(headersMap)
-                .from(user)
-                .to(ImmutableList.of(user1, user2))
-                .cc(ImmutableList.of(usercc))
-                .bcc(ImmutableList.of(userbcc))
-                .replyTo(ImmutableList.of(userRT))
-                .subject("test subject")
-                .date(ZONED_DATE)
-                .size(headers.length())
-                .preview("(Empty)")
-                .build();
-        assertThat(testee).isEqualToComparingFieldByField(expected);
-    }
-
-    @Test
-    public void textBodyShouldBeSetIntoMessage() throws Exception {
-        String headers = "Subject: test subject\n";
-        String body = "Mail body";
-        String mail = headers + "\n" + body;
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                mail.length(),
-                headers.length(),
-                new SharedByteArrayInputStream(mail.getBytes()),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        assertThat(testee.getTextBody()).hasValue("Mail body");
-    }
-
-    @Test
-    public void previewShouldBeLimitedTo256Length() throws Exception {
-        String headers = "Subject: test subject\n";
-        String body300 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999";
-        String expectedPreview = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" 
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" 
-                + "00000000001111111111222222222233333333334444444444555...";
-        assertThat(body300.length()).isEqualTo(300);
-        assertThat(expectedPreview.length()).isEqualTo(256);
-        String mail = headers + "\n" + body300;
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                mail.length(),
-                headers.length(),
-                new SharedByteArrayInputStream(mail.getBytes()),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        assertThat(testee.getPreview()).isEqualTo(expectedPreview);
-    }
-    
-    @Test
-    public void attachmentsShouldBeEmptyWhenNone() throws Exception {
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                0,
-                0,
-                new SharedByteArrayInputStream(IOUtils.toByteArray(ClassLoader.getSystemResourceAsStream("spamMail.eml"))),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
-        assertThat(testee.getAttachments()).isEmpty();
-    }
-    
-    @Test
-    public void attachmentsShouldBeRetrievedWhenSome() throws Exception {
-        MailboxMessage testMail = new SimpleMailboxMessage(
-                INTERNAL_DATE,
-                0,
-                0,
-                new SharedByteArrayInputStream(IOUtils.toByteArray(ClassLoader.getSystemResourceAsStream("spamMail.eml"))),
-                new Flags(Flag.SEEN),
-                new PropertyBuilder(),
-                MAILBOX_ID);
-        testMail.setModSeq(MOD_SEQ);
-        
-        String payload = "payload";
-        BlobId blodId = BlobId.of("id1");
-        String type = "content";
-        Attachment expectedAttachment = Attachment.builder()
-                .blobId(blodId)
-                .size(payload.length())
-                .type(type)
-                .cid("cid")
-                .isInline(true)
-                .build();
-        Message testee = messageFactory.fromMailboxMessage(testMail,
-                ImmutableList.of(MessageAttachment.builder()
-                        .attachment(org.apache.james.mailbox.model.Attachment.builder()
-                            .attachmentId(AttachmentId.from(blodId.getRawValue()))
-                            .bytes(payload.getBytes())
-                            .type(type)
-                            .build())
-                        .cid(Cid.from("cid"))
-                        .isInline(true)
-                        .build()), 
-                x -> MessageId.of("user|box|" + x));
-
-        assertThat(testee.getAttachments()).hasSize(1);
-        assertThat(testee.getAttachments().get(0)).isEqualToComparingFieldByField(expectedAttachment);
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void buildShouldThrowWhenOneAttachedMessageIsNotInAttachments() throws Exception {
-        Message.builder()
-            .id(MessageId.of("user|box|1"))
-            .blobId(BlobId.of("blobId"))
-            .threadId("threadId")
-            .mailboxIds(ImmutableList.of("mailboxId"))
-            .headers(ImmutableMap.of("key", "value"))
-            .subject("subject")
-            .size(1)
-            .date(ZonedDateTime.now())
-            .preview("preview")
-            .attachedMessages(ImmutableMap.of(BlobId.of("key"), SubMessage.builder()
-                    .headers(ImmutableMap.of("key", "value"))
-                    .subject("subject")
-                    .date(ZonedDateTime.now())
-                    .build()))
-            .build();
-    }
-
-    @Test
-    public void buildShouldNotThrowWhenOneAttachedMessageIsInAttachments() throws Exception {
-        Message.builder()
-            .id(MessageId.of("user|box|1"))
-            .blobId(BlobId.of("blobId"))
-            .threadId("threadId")
-            .mailboxIds(ImmutableList.of("mailboxId"))
-            .headers(ImmutableMap.of("key", "value"))
-            .subject("subject")
-            .size(1)
-            .date(ZonedDateTime.now())
-            .preview("preview")
-            .attachments(ImmutableList.of(Attachment.builder()
-                    .blobId(BlobId.of("key"))
-                    .size(1)
-                    .type("type")
-                    .build()))
-            .attachedMessages(ImmutableMap.of(BlobId.of("key"), SubMessage.builder()
-                    .headers(ImmutableMap.of("key", "value"))
-                    .subject("subject")
-                    .date(ZonedDateTime.now())
-                    .build()))
-            .build();
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
new file mode 100644
index 0000000..5a22883
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java
@@ -0,0 +1,262 @@
+/****************************************************************
+ * 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.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.io.ByteArrayInputStream;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
+
+import javax.mail.Flags;
+import javax.mail.Flags.Flag;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.Cid;
+import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.store.TestId;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class MessageFactoryTest {
+    private static final TestId MAILBOX_ID = TestId.of(18L);
+    private static final ZoneId UTC_ZONE_ID = ZoneId.of("Z");
+    private static final ZonedDateTime ZONED_DATE = ZonedDateTime.of(2015, 07, 14, 12, 30, 42, 0, UTC_ZONE_ID);
+    private static final Date INTERNAL_DATE = Date.from(ZONED_DATE.toInstant());
+
+    private MessageFactory messageFactory;
+    private MessagePreviewGenerator messagePreview ;
+    private HtmlTextExtractor htmlTextExtractor;
+    
+    @Before
+    public void setUp() {
+        htmlTextExtractor = mock(HtmlTextExtractor.class);
+        messagePreview = new MessagePreviewGenerator(htmlTextExtractor);
+        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
+        messageFactory = new MessageFactory(messagePreview, messageContentExtractor);
+    }
+    @Test
+    public void emptyMailShouldBeLoadedIntoMessage() throws Exception {
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(0)
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream("".getBytes(Charsets.UTF_8)))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("test|test|2"))
+                .build();
+
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        assertThat(testee)
+            .extracting(Message::getPreview, Message::getSize, Message::getSubject, Message::getHeaders, Message::getDate)
+            .containsExactly("(Empty)", 0L, "", ImmutableMap.of("Date", "Tue, 14 Jul 2015 12:30:42 +0000", "MIME-Version", "1.0"), ZONED_DATE);
+    }
+
+    @Test
+    public void flagsShouldBeSetIntoMessage() throws Exception {
+        Flags flags = new Flags();
+        flags.add(Flag.ANSWERED);
+        flags.add(Flag.FLAGGED);
+        flags.add(Flag.DRAFT);
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(flags)
+                .size(0)
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream("".getBytes(Charsets.UTF_8)))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("test|test|2"))
+                .build();
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        assertThat(testee)
+            .extracting(Message::isIsUnread, Message::isIsFlagged, Message::isIsAnswered, Message::isIsDraft)
+            .containsExactly(true, true, true, true);
+    }
+
+    @Test
+    public void headersShouldBeSetIntoMessage() throws Exception {
+        String headers = "From: user <us...@domain>\n"
+                + "Subject: test subject\n"
+                + "To: user1 <us...@domain>, user2 <us...@domain>\n"
+                + "Cc: usercc <us...@domain>\n"
+                + "Bcc: userbcc <us...@domain>\n"
+                + "Reply-To: \"user to reply to\" <us...@domain>\n"
+                + "In-Reply-To: <SN...@phx.gbl>\n"
+                + "Other-header: other header value";
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(headers.length())
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream(headers.getBytes(Charsets.UTF_8)))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("user|box|2"))
+                .build();
+
+        Emailer user = Emailer.builder().name("user").email("user@domain").build();
+        Emailer user1 = Emailer.builder().name("user1").email("user1@domain").build();
+        Emailer user2 = Emailer.builder().name("user2").email("user2@domain").build();
+        Emailer usercc = Emailer.builder().name("usercc").email("usercc@domain").build();
+        Emailer userbcc = Emailer.builder().name("userbcc").email("userbcc@domain").build();
+        Emailer userRT = Emailer.builder().name("user to reply to").email("user.reply.to@domain").build();
+        ImmutableMap<String, String> headersMap = ImmutableMap.<String, String>builder()
+                .put("Cc", "usercc <us...@domain>")
+                .put("Bcc", "userbcc <us...@domain>")
+                .put("Subject", "test subject")
+                .put("From", "user <us...@domain>")
+                .put("To", "user1 <us...@domain>, user2 <us...@domain>")
+                .put("Reply-To", "\"user to reply to\" <us...@domain>")
+                .put("In-Reply-To", "<SN...@phx.gbl>")
+                .put("Other-header", "other header value")
+                .put("Date", "Tue, 14 Jul 2015 12:30:42 +0000")
+                .put("MIME-Version", "1.0")
+                .build();
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        Message expected = Message.builder()
+                .id(MessageId.of("user|box|2"))
+                .blobId(BlobId.of("2"))
+                .threadId("user|box|2")
+                .mailboxIds(ImmutableList.of(MAILBOX_ID.serialize()))
+                .inReplyToMessageId("<SN...@phx.gbl>")
+                .headers(headersMap)
+                .from(user)
+                .to(ImmutableList.of(user1, user2))
+                .cc(ImmutableList.of(usercc))
+                .bcc(ImmutableList.of(userbcc))
+                .replyTo(ImmutableList.of(userRT))
+                .subject("test subject")
+                .date(ZONED_DATE)
+                .size(headers.length())
+                .preview("(Empty)")
+                .textBody("")
+                .build();
+        assertThat(testee).isEqualToComparingFieldByField(expected);
+    }
+
+    @Test
+    public void textBodyShouldBeSetIntoMessage() throws Exception {
+        String headers = "Subject: test subject\n";
+        String body = "Mail body";
+        String mail = headers + "\n" + body;
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(mail.length())
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream(mail.getBytes(Charsets.UTF_8)))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("user|box|2"))
+                .build();
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        assertThat(testee.getTextBody()).hasValue("Mail body");
+    }
+
+    @Test
+    public void previewShouldBeLimitedTo256Length() throws Exception {
+        String headers = "Subject: test subject\n";
+        String body300 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999";
+        String expectedPreview = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" 
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999" 
+                + "00000000001111111111222222222233333333334444444444555...";
+        assertThat(body300.length()).isEqualTo(300);
+        assertThat(expectedPreview.length()).isEqualTo(256);
+        String mail = headers + "\n" + body300;
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(mail.length())
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream(mail.getBytes(Charsets.UTF_8)))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("user|box|2"))
+                .build();
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        assertThat(testee.getPreview()).isEqualTo(expectedPreview);
+    }
+    
+    @Test
+    public void attachmentsShouldBeEmptyWhenNone() throws Exception {
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(0)
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream(IOUtils.toByteArray(ClassLoader.getSystemResourceAsStream("spamMail.eml"))))
+                .attachments(ImmutableList.of())
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("user|box|2"))
+                .build();
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+        assertThat(testee.getAttachments()).isEmpty();
+    }
+    
+    @Test
+    public void attachmentsShouldBeRetrievedWhenSome() throws Exception {
+        String payload = "payload";
+        BlobId blodId = BlobId.of("id1");
+        String type = "content";
+        Attachment expectedAttachment = Attachment.builder()
+                .blobId(blodId)
+                .size(payload.length())
+                .type(type)
+                .cid("cid")
+                .isInline(true)
+                .build();
+        MetaDataWithContent testMail = MetaDataWithContent.builder()
+                .uid(2)
+                .flags(new Flags(Flag.SEEN))
+                .size(0)
+                .internalDate(INTERNAL_DATE)
+                .content(new ByteArrayInputStream(IOUtils.toByteArray(ClassLoader.getSystemResourceAsStream("spamMail.eml"))))
+                .attachments(ImmutableList.of(MessageAttachment.builder()
+                        .attachment(org.apache.james.mailbox.model.Attachment.builder()
+                                .attachmentId(AttachmentId.from(blodId.getRawValue()))
+                                .bytes(payload.getBytes())
+                                .type(type)
+                                .build())
+                        .cid(Cid.from("cid"))
+                        .isInline(true)
+                        .build()))
+                .mailboxId(MAILBOX_ID)
+                .messageId(MessageId.of("user|box|2"))
+                .build();
+
+        Message testee = messageFactory.fromMetaDataWithContent(testMail);
+
+        assertThat(testee.getAttachments()).hasSize(1);
+        assertThat(testee.getAttachments().get(0)).isEqualToComparingFieldByField(expectedAttachment);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7bfb0cd8/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java
new file mode 100644
index 0000000..8953bc1
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java
@@ -0,0 +1,256 @@
+/****************************************************************
+ * 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.jmap.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.time.ZonedDateTime;
+import java.util.Optional;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class MessageTest {
+
+    
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenIdIsNull() {
+        Message.builder().build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenBlobIdIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenThreadIdIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenThreadIdIsEmpty() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("").build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenMailboxIdsIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenHeadersIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenSubjectIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of()).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenSubjectIsEmpty() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
+            .subject("").build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenSizeIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
+            .subject("subject").build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenDateIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
+            .subject("subject").size(123).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenPreviewIsNull() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
+            .subject("subject").size(123).date(ZonedDateTime.now()).build();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenPreviewIsEmpty() {
+        Message.builder().id(MessageId.of("user|box|1")).blobId(BlobId.of("blobId")).threadId("threadId").mailboxIds(ImmutableList.of()).headers(ImmutableMap.of())
+            .subject("subject").size(123).date(ZonedDateTime.now()).preview("").build();
+    }
+
+    @Test
+    public void buildShouldWorkWhenMandatoryFieldsArePresent() {
+        ZonedDateTime currentDate = ZonedDateTime.now();
+        Message expected = new Message(MessageId.of("user|box|1"), BlobId.of("blobId"), "threadId", ImmutableList.of("mailboxId"), Optional.empty(), false, false, false, false, false, ImmutableMap.of("key", "value"), Optional.empty(),
+                ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), "subject", currentDate, 123, "preview", Optional.empty(), Optional.empty(), ImmutableList.of(), ImmutableMap.of());
+        Message tested = Message.builder()
+                .id(MessageId.of("user|box|1"))
+                .blobId(BlobId.of("blobId"))
+                .threadId("threadId")
+                .mailboxIds(ImmutableList.of("mailboxId"))
+                .headers(ImmutableMap.of("key", "value"))
+                .subject("subject")
+                .size(123)
+                .date(currentDate)
+                .preview("preview")
+                .build();
+        assertThat(tested).isEqualToComparingFieldByField(expected);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void buildShouldThrowWhenAttachedMessageIsNotMatchingAttachments() {
+        Attachment simpleAttachment = Attachment.builder().blobId(BlobId.of("blobId")).type("type").name("name").size(123).build();
+        ImmutableList<Attachment> attachments = ImmutableList.of(simpleAttachment);
+        SubMessage simpleMessage = SubMessage.builder()
+                .headers(ImmutableMap.of("key", "value"))
+                .subject("subject")
+                .date(ZonedDateTime.now())
+                .build();
+        ImmutableMap<BlobId, SubMessage> attachedMessages = ImmutableMap.of(BlobId.of("differentBlobId"), simpleMessage);
+        Message.builder()
+            .id(MessageId.of("user|box|1"))
+            .blobId(BlobId.of("blobId"))
+            .threadId("threadId")
+            .mailboxIds(ImmutableList.of("mailboxId"))
+            .headers(ImmutableMap.of("key", "value"))
+            .subject("subject")
+            .size(123)
+            .date(ZonedDateTime.now())
+            .preview("preview")
+            .attachments(attachments)
+            .attachedMessages(attachedMessages)
+            .build();
+    }
+
+    @Test
+    public void buildShouldWorkWhenAllFieldsArePresent() {
+        Emailer from = Emailer.builder().name("from").email("from@domain").build();
+        ImmutableList<Emailer> to = ImmutableList.of(Emailer.builder().name("to").email("to@domain").build());
+        ImmutableList<Emailer> cc = ImmutableList.of(Emailer.builder().name("cc").email("cc@domain").build());
+        ImmutableList<Emailer> bcc = ImmutableList.of(Emailer.builder().name("bcc").email("bcc@domain").build());
+        ImmutableList<Emailer> replyTo = ImmutableList.of(Emailer.builder().name("replyTo").email("replyTo@domain").build());
+        ZonedDateTime currentDate = ZonedDateTime.now();
+        Attachment simpleAttachment = Attachment.builder().blobId(BlobId.of("blobId")).type("type").name("name").size(123).build();
+        ImmutableList<Attachment> attachments = ImmutableList.of(simpleAttachment);
+        SubMessage simpleMessage = SubMessage.builder()
+                .headers(ImmutableMap.of("key", "value"))
+                .subject("subject")
+                .date(currentDate)
+                .build();
+        ImmutableMap<BlobId, SubMessage> attachedMessages = ImmutableMap.of(BlobId.of("blobId"), simpleMessage);
+        Message expected = new Message(
+                MessageId.of("user|box|1"),
+                BlobId.of("blobId"),
+                "threadId",
+                ImmutableList.of("mailboxId"),
+                Optional.of("inReplyToMessageId"), 
+                true,
+                true,
+                true,
+                true,
+                true,
+                ImmutableMap.of("key", "value"),
+                Optional.of(from),
+                to,
+                cc,
+                bcc,
+                replyTo,
+                "subject",
+                currentDate,
+                123,
+                "preview",
+                Optional.of("textBody"), 
+                Optional.of("htmlBody"),
+                attachments,
+                attachedMessages);
+        Message tested = Message.builder()
+            .id(MessageId.of("user|box|1"))
+            .blobId(BlobId.of("blobId"))
+            .threadId("threadId")
+            .mailboxIds(ImmutableList.of("mailboxId"))
+            .inReplyToMessageId("inReplyToMessageId")
+            .isUnread(true)
+            .isFlagged(true)
+            .isAnswered(true)
+            .isDraft(true)
+            .headers(ImmutableMap.of("key", "value"))
+            .from(from)
+            .to(to)
+            .cc(cc)
+            .bcc(bcc)
+            .replyTo(replyTo)
+            .subject("subject")
+            .date(currentDate)
+            .size(123)
+            .preview("preview")
+            .textBody("textBody")
+            .htmlBody("htmlBody")
+            .attachments(attachments)
+            .attachedMessages(attachedMessages)
+            .build();
+        assertThat(tested).isEqualToComparingFieldByField(expected);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void buildShouldThrowWhenOneAttachedMessageIsNotInAttachments() throws Exception {
+        Message.builder()
+            .id(MessageId.of("user|box|1"))
+            .blobId(BlobId.of("blobId"))
+            .threadId("threadId")
+            .mailboxIds(ImmutableList.of("mailboxId"))
+            .headers(ImmutableMap.of("key", "value"))
+            .subject("subject")
+            .size(1)
+            .date(ZonedDateTime.now())
+            .preview("preview")
+            .attachedMessages(ImmutableMap.of(BlobId.of("key"), SubMessage.builder()
+                    .headers(ImmutableMap.of("key", "value"))
+                    .subject("subject")
+                    .date(ZonedDateTime.now())
+                    .build()))
+            .build();
+    }
+
+    @Test
+    public void buildShouldNotThrowWhenOneAttachedMessageIsInAttachments() throws Exception {
+        Message.builder()
+            .id(MessageId.of("user|box|1"))
+            .blobId(BlobId.of("blobId"))
+            .threadId("threadId")
+            .mailboxIds(ImmutableList.of("mailboxId"))
+            .headers(ImmutableMap.of("key", "value"))
+            .subject("subject")
+            .size(1)
+            .date(ZonedDateTime.now())
+            .preview("preview")
+            .attachments(ImmutableList.of(Attachment.builder()
+                    .blobId(BlobId.of("key"))
+                    .size(1)
+                    .type("type")
+                    .build()))
+            .attachedMessages(ImmutableMap.of(BlobId.of("key"), SubMessage.builder()
+                    .headers(ImmutableMap.of("key", "value"))
+                    .subject("subject")
+                    .date(ZonedDateTime.now())
+                    .build()))
+            .build();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org