james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject [1/2] james-project git commit: JAMES-1744 Preview should not contain HTML tags
Date Tue, 14 Jun 2016 02:18:44 GMT
Repository: james-project
Updated Branches:
  refs/heads/master b5c125798 -> c4eb1ea91


JAMES-1744 Preview should not contain HTML tags


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

Branch: refs/heads/master
Commit: 7ab817e6031b008038b54baf55d065d5badaf8eb
Parents: b5c1257
Author: Laura Royet <lroyet@linagora.com>
Authored: Fri May 27 14:07:08 2016 +0200
Committer: Laura Royet <lroyet@linagora.com>
Committed: Mon Jun 13 15:14:20 2016 +0200

----------------------------------------------------------------------
 .../org/apache/james/jmap/JMAPCommonModule.java |   4 +
 .../integration/GetMessagesMethodTest.java      |  92 ++++++++-
 server/protocols/jmap/pom.xml                   |   5 +
 .../james/jmap/methods/GetMessagesMethod.java   |   8 +-
 .../methods/SetMessagesCreationProcessor.java   |   8 +-
 .../org/apache/james/jmap/model/Message.java    | 139 --------------
 .../apache/james/jmap/model/MessageFactory.java | 166 ++++++++++++++++
 .../jmap/model/MessagePreviewGenerator.java     |  66 +++++++
 .../jmap/methods/GetMessagesMethodTest.java     |  34 ++--
 .../SetMessagesCreationProcessorTest.java       |  25 ++-
 .../james/jmap/model/MailboxMessageTest.java    |  56 +++---
 .../jmap/model/MessagePreviewGeneratorTest.java | 187 +++++++++++++++++++
 .../apache/james/jmap/send/MailFactoryTest.java |  10 +-
 13 files changed, 601 insertions(+), 199 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
index cb8915e..71e0427 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPCommonModule.java
@@ -28,6 +28,8 @@ import org.apache.james.jmap.crypto.AccessTokenManagerImpl;
 import org.apache.james.jmap.crypto.JamesSignatureHandler;
 import org.apache.james.jmap.crypto.SignatureHandler;
 import org.apache.james.jmap.crypto.SignedContinuationTokenManager;
+import org.apache.james.jmap.model.MessageFactory;
+import org.apache.james.jmap.model.MessagePreviewGenerator;
 import org.apache.james.jmap.send.MailFactory;
 import org.apache.james.jmap.send.MailSpool;
 import org.apache.james.util.date.DefaultZonedDateTimeProvider;
@@ -54,6 +56,8 @@ public class JMAPCommonModule extends AbstractModule {
         bind(MailSpool.class).in(Scopes.SINGLETON);
         bind(MailFactory.class).in(Scopes.SINGLETON);
         bind(AutomaticallySentMailDetectorImpl.class).in(Scopes.SINGLETON);
+        bind(MessageFactory.class).in(Scopes.SINGLETON);
+        bind(MessagePreviewGenerator.class).in(Scopes.SINGLETON);
 
         bind(SignatureHandler.class).to(JamesSignatureHandler.class);
         bind(ZonedDateTimeProvider.class).to(DefaultZonedDateTimeProvider.class);

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java
index cff96a4..09ab9dc 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessagesMethodTest.java
@@ -221,7 +221,93 @@ public abstract class GetMessagesMethodTest {
             .body(FIRST_MESSAGE + ".subject", equalTo("my test subject"))
             .body(FIRST_MESSAGE + ".htmlBody", equalTo("This is a <b>HTML</b> mail"))
             .body(FIRST_MESSAGE + ".isUnread", equalTo(true))
-            .body(FIRST_MESSAGE + ".preview", equalTo("This is a <b>HTML</b> mail"))
+            .body(FIRST_MESSAGE + ".preview", equalTo("This is a HTML mail"))
+            .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("content-type", "text/html", "subject", "my test subject")))
+            .body(FIRST_MESSAGE + ".date", equalTo("2014-10-30T14:12:00Z"));
+    }
+    
+    @Test
+    public void getMessagesShouldReturnMessageWhenComplexHtmlMessage() throws Exception {
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox");
+
+        ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
+        jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"),
+                new ByteArrayInputStream("Content-Type: text/html\r\nSubject: my test subject\r\n\r\nThis is a <b>HTML</b> mail containing <u>underlined part</u>, <i>italic part</i> and <u><i>underlined AND italic part</i></u>".getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+        
+        await();
+        
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1"))
+            .body(FIRST_MESSAGE + ".threadId", equalTo(username + "|inbox|1"))
+            .body(FIRST_MESSAGE + ".subject", equalTo("my test subject"))
+            .body(FIRST_MESSAGE + ".htmlBody", equalTo("This is a <b>HTML</b> mail containing <u>underlined part</u>, <i>italic part</i> and <u><i>underlined AND italic part</i></u>"))
+            .body(FIRST_MESSAGE + ".isUnread", equalTo(true))
+            .body(FIRST_MESSAGE + ".preview", equalTo("This is a HTML mail containing underlined part, italic part and underlined AND italic part"))
+            .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("content-type", "text/html", "subject", "my test subject")))
+            .body(FIRST_MESSAGE + ".date", equalTo("2014-10-30T14:12:00Z"));
+    }
+    
+    @Test
+    public void messagePreviewShouldContainTagsWhenPlainTextMessageContainsTags() throws Exception {
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox");
+
+        ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
+        jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"),
+                new ByteArrayInputStream("Content-Type: text/plain\r\nSubject: my test subject\r\n\r\nHere is a listing of HTML tags : <b>jfjfjfj</b>, <i>jfhdgdgdfj</i>, <u>jfjaaafj</u>".getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+
+        await();
+        
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(FIRST_MESSAGE + ".preview", equalTo("Here is a listing of HTML tags : <b>jfjfjfj</b>, <i>jfhdgdgdfj</i>, <u>jfjaaafj</u>"));
+    }
+    
+    @Test
+    public void getMessagesShouldReturnMessageWhenHtmlMessageWithIncorrectTag() throws Exception {
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "inbox");
+
+        ZonedDateTime dateTime = ZonedDateTime.parse("2014-10-30T14:12:00Z");
+        jmapServer.serverProbe().appendMessage(username, new MailboxPath(MailboxConstants.USER_NAMESPACE, username, "inbox"),
+                new ByteArrayInputStream("Content-Type: text/html\r\nSubject: my test subject\r\n\r\nThis is a <ganan>HTML</b> mail".getBytes()), Date.from(dateTime.toInstant()), false, new Flags());
+        
+        await();
+        
+        given()
+            .accept(ContentType.JSON)
+            .contentType(ContentType.JSON)
+            .header("Authorization", accessToken.serialize())
+            .body("[[\"getMessages\", {\"ids\": [\"" + username + "|inbox|1\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messages"))
+            .body(ARGUMENTS + ".list", hasSize(1))
+            .body(FIRST_MESSAGE + ".id", equalTo(username + "|inbox|1"))
+            .body(FIRST_MESSAGE + ".threadId", equalTo(username + "|inbox|1"))
+            .body(FIRST_MESSAGE + ".subject", equalTo("my test subject"))
+            .body(FIRST_MESSAGE + ".htmlBody", equalTo("This is a <ganan>HTML</b> mail"))
+            .body(FIRST_MESSAGE + ".isUnread", equalTo(true))
+            .body(FIRST_MESSAGE + ".preview", equalTo("This is a HTML mail"))
             .body(FIRST_MESSAGE + ".headers", equalTo(ImmutableMap.of("content-type", "text/html", "subject", "my test subject")))
             .body(FIRST_MESSAGE + ".date", equalTo("2014-10-30T14:12:00Z"));
     }
@@ -391,7 +477,7 @@ public abstract class GetMessagesMethodTest {
             .body(ARGUMENTS + ".list", hasSize(1))
             .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true))
             .body(ATTACHMENTS, hasSize(2))
-            .body(FIRST_MESSAGE + ".preview", equalTo("<b>html</b>\n"))
+            .body(FIRST_MESSAGE + ".preview", equalTo("html\n"))
             .body(FIRST_MESSAGE + ".textBody", nullValue())
             .body(FIRST_MESSAGE + ".htmlBody", equalTo("<b>html</b>\n"));
     }
@@ -443,7 +529,7 @@ public abstract class GetMessagesMethodTest {
             .body(ARGUMENTS + ".list", hasSize(1))
             .body(FIRST_MESSAGE + ".hasAttachment", equalTo(true))
             .body(ATTACHMENTS, hasSize(1))
-            .body(FIRST_MESSAGE + ".preview", equalTo("<i>blabla</i>\n<b>bloblo</b>\n"))
+            .body(FIRST_MESSAGE + ".preview", equalTo("blabla\nbloblo\n"))
             .body(FIRST_MESSAGE + ".textBody", equalTo("/blabla/\n*bloblo*\n"))
             .body(FIRST_MESSAGE + ".htmlBody", equalTo("<i>blabla</i>\n<b>bloblo</b>\n"));
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/pom.xml
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/pom.xml b/server/protocols/jmap/pom.xml
index 8851da0..46f4339 100644
--- a/server/protocols/jmap/pom.xml
+++ b/server/protocols/jmap/pom.xml
@@ -228,6 +228,11 @@
                     <artifactId>james-server-util</artifactId>
                 </dependency>
                 <dependency>
+                    <groupId>org.apache.commons</groupId>
+                    <artifactId>commons-lang3</artifactId>
+                    <version>3.3.2</version>
+                </dependency>
+                <dependency>
                     <groupId>com.fasterxml.jackson.core</groupId>
                     <artifactId>jackson-databind</artifactId>
                 </dependency>

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/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 3dadb56..39319c5 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
@@ -34,6 +34,7 @@ import org.apache.james.jmap.model.ClientId;
 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.MessageId;
 import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.MessageProperties.HeaderProperty;
@@ -68,15 +69,18 @@ public class GetMessagesMethod implements Method {
     private final MessageMapperFactory messageMapperFactory;
     private final MailboxMapperFactory mailboxMapperFactory;
     private final AttachmentMapperFactory attachmentMapperFactory;
+    private final MessageFactory messageFactory;
 
     @Inject
     @VisibleForTesting GetMessagesMethod(
             MessageMapperFactory messageMapperFactory,
             MailboxMapperFactory mailboxMapperFactory,
-            AttachmentMapperFactory attachmentMapperFactory) {
+            AttachmentMapperFactory attachmentMapperFactory,
+            MessageFactory messageFactory) {
         this.messageMapperFactory = messageMapperFactory;
         this.mailboxMapperFactory = mailboxMapperFactory;
         this.attachmentMapperFactory = attachmentMapperFactory;
+        this.messageFactory = messageFactory;
     }
     
     @Override
@@ -135,7 +139,7 @@ public class GetMessagesMethod implements Method {
 
     
     private Function<CompletedMailboxMessage, Message> toJmapMessage(MailboxSession mailboxSession) {
-        return (completedMailboxMessage) -> Message.fromMailboxMessage(
+        return (completedMailboxMessage) -> messageFactory.fromMailboxMessage(
                 completedMailboxMessage.mailboxMessage, 
                 completedMailboxMessage.attachments, 
                 uid -> new MessageId(mailboxSession.getUser(), completedMailboxMessage.mailboxPath , uid));

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
index a5097d7..49f87a9 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
@@ -40,6 +40,7 @@ import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException;
 import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.CreationMessageId;
 import org.apache.james.jmap.model.Message;
+import org.apache.james.jmap.model.MessageFactory;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.MessageProperties.MessageProperty;
@@ -86,6 +87,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
     private final MIMEMessageConverter mimeMessageConverter;
     private final MailSpool mailSpool;
     private final MailFactory mailFactory;
+    private final MessageFactory messageFactory;
 
     @Inject
     @VisibleForTesting
@@ -94,13 +96,15 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
                                  MailboxSessionMapperFactory mailboxSessionMapperFactory,
                                  MIMEMessageConverter mimeMessageConverter,
                                  MailSpool mailSpool,
-                                 MailFactory mailFactory) {
+                                 MailFactory mailFactory,
+                                 MessageFactory messageFactory) {
         this.mailboxMapperFactory = mailboxMapperFactory;
         this.mailboxManager = mailboxManager;
         this.mailboxSessionMapperFactory = mailboxSessionMapperFactory;
         this.mimeMessageConverter = mimeMessageConverter;
         this.mailSpool = mailSpool;
         this.mailFactory = mailFactory;
+        this.messageFactory = messageFactory;
     }
 
     @Override
@@ -195,7 +199,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
             MessageMapper messageMapper = mailboxSessionMapperFactory.createMessageMapper(session);
             MailboxMessage newMailboxMessage = buildMailboxMessage(createdEntry, outbox);
             messageMapper.add(outbox, newMailboxMessage);
-            Message jmapMessage = Message.fromMailboxMessage(newMailboxMessage, ImmutableList.of(), buildMessageIdFromUid);
+            Message jmapMessage = messageFactory.fromMailboxMessage(newMailboxMessage, ImmutableList.of(), buildMessageIdFromUid);
             sendMessage(newMailboxMessage, jmapMessage, session);
             return new MessageWithId<>(createdEntry.getCreationId(), jmapMessage);
         } catch (MailboxException | MessagingException | IOException e) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
index 8e87a1e..1c1cd50 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
@@ -19,24 +19,14 @@
 
 package org.apache.james.jmap.model;
 
-import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.Set;
-import java.util.function.Function;
 import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import org.apache.james.jmap.methods.GetMessagesMethod;
 import org.apache.james.jmap.methods.JmapResponseWriterImpl;
-import org.apache.james.jmap.model.message.EMailer;
-import org.apache.james.jmap.model.message.IndexableMessage;
-import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
-import org.apache.james.mailbox.store.mail.model.MailboxMessage;
-import org.apache.james.util.streams.ImmutableCollectors;
 
 import com.fasterxml.jackson.annotation.JsonFilter;
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@@ -46,144 +36,15 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Multimap;
 
 @JsonDeserialize(builder = Message.Builder.class)
 @JsonFilter(JmapResponseWriterImpl.PROPERTIES_FILTER)
 public class Message {
-    public static final String NO_SUBJECT = "(No subject)";
-    public static final String MULTIVALUED_HEADERS_SEPARATOR = ", ";
-    public static final String NO_BODY = "(Empty)";
-    public static final ZoneId UTC_ZONE_ID = ZoneId.of("Z");
 
     public static Builder builder() {
         return new Builder();
     }
 
-    public static Message fromMailboxMessage(MailboxMessage mailboxMessage,
-            List<org.apache.james.mailbox.store.mail.model.Attachment> attachments, 
-            Function<Long, MessageId> uidToMessageId) {
-        IndexableMessage im = IndexableMessage.from(mailboxMessage, new DefaultTextExtractor(), UTC_ZONE_ID);
-        MessageId messageId = uidToMessageId.apply(im.getId());
-        return builder()
-                .id(messageId)
-                .blobId(String.valueOf(im.getId()))
-                .threadId(messageId.serialize())
-                .mailboxIds(ImmutableList.of(im.getMailboxId()))
-                .inReplyToMessageId(getHeaderAsSingleValue(im, "in-reply-to"))
-                .isUnread(im.isUnRead())
-                .isFlagged(im.isFlagged())
-                .isAnswered(im.isAnswered())
-                .isDraft(im.isDraft())
-                .subject(getSubject(im))
-                .headers(toMap(im.getHeaders()))
-                .from(firstElasticSearchEmailers(im.getFrom()))
-                .to(fromElasticSearchEmailers(im.getTo()))
-                .cc(fromElasticSearchEmailers(im.getCc()))
-                .bcc(fromElasticSearchEmailers(im.getBcc()))
-                .replyTo(fromElasticSearchEmailers(im.getReplyTo()))
-                .size(im.getSize())
-                .date(getInternalDate(mailboxMessage, im))
-                .preview(getPreview(im))
-                .textBody(getTextBody(im))
-                .htmlBody(getHtmlBody(im))
-                .attachments(getAttachments(attachments))
-                .build();
-    }
-
-    private static String getSubject(IndexableMessage im) {
-        return Optional.ofNullable(
-                    Strings.emptyToNull(
-                        im.getSubjects()
-                            .stream()
-                            .collect(Collectors.joining(MULTIVALUED_HEADERS_SEPARATOR))))
-                .orElse(NO_SUBJECT);
-    }
-    
-    private static Emailer firstElasticSearchEmailers(Set<EMailer> emailers) {
-        return emailers.stream()
-                    .findFirst()
-                    .map(Message::fromElasticSearchEmailer)
-                    .orElse(null);
-    }
-    
-    private static ImmutableList<Emailer> fromElasticSearchEmailers(Set<EMailer> emailers) {
-        return emailers.stream()
-                    .map(Message::fromElasticSearchEmailer)
-                    .collect(ImmutableCollectors.toImmutableList());
-    }
-    
-    private static Emailer fromElasticSearchEmailer(EMailer emailer) {
-        return Emailer.builder()
-                    .name(emailer.getName())
-                    .email(emailer.getAddress())
-                    .build();
-    }
-    
-    private static String getPreview(IndexableMessage im) {
-        Stream<Optional<String>> htmlThenTextStream = Stream.of(
-                im.getBodyHtml(),
-                im.getBodyText());
-
-        String body = htmlThenTextStream
-            .filter(Optional::isPresent)
-            .map(Optional::get)
-            .filter(stringBody -> !stringBody.isEmpty())
-            .findFirst()
-            .orElse(NO_BODY);
-        
-        return computePreview(body);
-    }
-
-    @VisibleForTesting static String computePreview(String body) {
-        if (body.length() <= 256) {
-            return body;
-        }
-        return body.substring(0, 253) + "...";
-    }
-    
-    private static ImmutableMap<String, String> toMap(Multimap<String, String> multimap) {
-        return multimap
-                .asMap()
-                .entrySet()
-                .stream()
-                .collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, x -> joinOnComma(x.getValue())));
-    }
-    
-    private static String getHeaderAsSingleValue(IndexableMessage im, String header) {
-        return Strings.emptyToNull(joinOnComma(im.getHeaders().get(header)));
-    }
-    
-    private static String joinOnComma(Iterable<String> iterable) {
-        return String.join(MULTIVALUED_HEADERS_SEPARATOR, iterable);
-    }
-    
-    private static ZonedDateTime getInternalDate(MailboxMessage mailboxMessage, IndexableMessage im) {
-        return ZonedDateTime.ofInstant(mailboxMessage.getInternalDate().toInstant(), UTC_ZONE_ID);
-    }
-
-    private static String getTextBody(IndexableMessage im) {
-        return im.getBodyText().map(Strings::emptyToNull).orElse(null);
-    }
-
-    private static String getHtmlBody(IndexableMessage im) {
-        return im.getBodyHtml().map(Strings::emptyToNull).orElse(null);
-    }
-
-    private static List<Attachment> getAttachments(List<org.apache.james.mailbox.store.mail.model.Attachment> attachments) {
-        return attachments.stream()
-                .map(Message::fromMailboxAttachment)
-                .collect(ImmutableCollectors.toImmutableList());
-    }
-
-    private static Attachment fromMailboxAttachment(org.apache.james.mailbox.store.mail.model.Attachment attachment) {
-        return Attachment.builder()
-                    .blobId(attachment.getAttachmentId().getId())
-                    .type(attachment.getType())
-                    .size(attachment.getSize())
-                    .build();
-    }
-
     @JsonPOJOBuilder(withPrefix = "")
     public static class Builder {
         private MessageId id;

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/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
new file mode 100644
index 0000000..8a5a277
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java
@@ -0,0 +1,166 @@
+/****************************************************************
+ * 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 java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.model.message.EMailer;
+import org.apache.james.jmap.model.message.IndexableMessage;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.util.streams.ImmutableCollectors;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Multimap;
+
+public class MessageFactory {
+
+    public static final String NO_SUBJECT = "(No subject)";
+    public static final String MULTIVALUED_HEADERS_SEPARATOR = ", ";
+    public static final ZoneId UTC_ZONE_ID = ZoneId.of("Z");
+
+    private final MessagePreviewGenerator messagePreview;
+
+    @Inject
+    public MessageFactory(MessagePreviewGenerator messagePreview) {
+        this.messagePreview = messagePreview;
+    }
+
+    public Message fromMailboxMessage(MailboxMessage mailboxMessage,
+            List<org.apache.james.mailbox.store.mail.model.Attachment> attachments,
+            Function<Long, MessageId> uidToMessageId) {
+
+        IndexableMessage im = IndexableMessage.from(mailboxMessage, new DefaultTextExtractor(), UTC_ZONE_ID);
+        MessageId messageId = uidToMessageId.apply(im.getId());
+        return Message.builder()
+                .id(messageId)
+                .blobId(String.valueOf(im.getId()))
+                .threadId(messageId.serialize())
+                .mailboxIds(ImmutableList.of(im.getMailboxId()))
+                .inReplyToMessageId(getHeaderAsSingleValue(im, "in-reply-to"))
+                .isUnread(im.isUnRead())
+                .isFlagged(im.isFlagged())
+                .isAnswered(im.isAnswered())
+                .isDraft(im.isDraft())
+                .subject(getSubject(im))
+                .headers(toMap(im.getHeaders()))
+                .from(firstElasticSearchEmailers(im.getFrom()))
+                .to(fromElasticSearchEmailers(im.getTo()))
+                .cc(fromElasticSearchEmailers(im.getCc()))
+                .bcc(fromElasticSearchEmailers(im.getBcc()))
+                .replyTo(fromElasticSearchEmailers(im.getReplyTo()))
+                .size(im.getSize())
+                .date(getInternalDate(mailboxMessage, im))
+                .preview(getPreview(im))
+                .textBody(getTextBody(im))
+                .htmlBody(getHtmlBody(im))
+                .attachments(getAttachments(attachments))
+                .build();
+    }
+
+    private String getPreview(IndexableMessage im) {
+        Optional<String> bodyHtml = im.getBodyHtml();
+        if (bodyHtml.isPresent()) {
+            return messagePreview.forHTMLBody(bodyHtml);
+        }
+        return messagePreview.forTextBody(im.getBodyText());
+    }
+
+    private String getSubject(IndexableMessage im) {
+        return Optional.ofNullable(
+                    Strings.emptyToNull(
+                        im.getSubjects()
+                            .stream()
+                            .collect(Collectors.joining(MULTIVALUED_HEADERS_SEPARATOR))))
+                .orElse(NO_SUBJECT);
+    }
+    
+    private Emailer firstElasticSearchEmailers(Set<EMailer> emailers) {
+        return emailers.stream()
+                    .findFirst()
+                    .map(this::fromElasticSearchEmailer)
+                    .orElse(null);
+    }
+    
+    private ImmutableList<Emailer> fromElasticSearchEmailers(Set<EMailer> emailers) {
+        return emailers.stream()
+                    .map(this::fromElasticSearchEmailer)
+                    .collect(ImmutableCollectors.toImmutableList());
+    }
+    
+    private Emailer fromElasticSearchEmailer(EMailer emailer) {
+        return Emailer.builder()
+                    .name(emailer.getName())
+                    .email(emailer.getAddress())
+                    .build();
+    }
+    
+    private ImmutableMap<String, String> toMap(Multimap<String, String> multimap) {
+        return multimap
+                .asMap()
+                .entrySet()
+                .stream()
+                .collect(ImmutableCollectors.toImmutableMap(Map.Entry::getKey, x -> joinOnComma(x.getValue())));
+    }
+    
+    private String getHeaderAsSingleValue(IndexableMessage im, String header) {
+        return Strings.emptyToNull(joinOnComma(im.getHeaders().get(header)));
+    }
+    
+    private String joinOnComma(Iterable<String> iterable) {
+        return String.join(MULTIVALUED_HEADERS_SEPARATOR, iterable);
+    }
+    
+    private ZonedDateTime getInternalDate(MailboxMessage mailboxMessage, IndexableMessage im) {
+        return ZonedDateTime.ofInstant(mailboxMessage.getInternalDate().toInstant(), UTC_ZONE_ID);
+    }
+
+    private String getTextBody(IndexableMessage im) {
+        return im.getBodyText().map(Strings::emptyToNull).orElse(null);
+    }
+
+    private String getHtmlBody(IndexableMessage im) {
+        return im.getBodyHtml().map(Strings::emptyToNull).orElse(null);
+    }
+
+    private List<Attachment> getAttachments(List<org.apache.james.mailbox.store.mail.model.Attachment> attachments) {
+        return attachments.stream()
+                .map(this::fromMailboxAttachment)
+                .collect(ImmutableCollectors.toImmutableList());
+    }
+
+    private Attachment fromMailboxAttachment(org.apache.james.mailbox.store.mail.model.Attachment attachment) {
+        return Attachment.builder()
+                    .blobId(attachment.getAttachmentId().getId())
+                    .type(attachment.getType())
+                    .size(attachment.getSize())
+                    .build();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessagePreviewGenerator.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessagePreviewGenerator.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessagePreviewGenerator.java
new file mode 100644
index 0000000..5a162cd
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessagePreviewGenerator.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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 java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+
+public class MessagePreviewGenerator {
+    
+    public static final String NO_BODY = "(Empty)";
+    public static final int MAX_PREVIEW_LENGTH = 256;
+
+    private final HtmlTextExtractor htmlTextExtractor;
+
+    @Inject
+    public MessagePreviewGenerator(HtmlTextExtractor htmlTextExtractor) {
+        this.htmlTextExtractor = htmlTextExtractor;
+    }
+
+    public String forHTMLBody(Optional<String> body) {
+        return body.filter(text -> !text.isEmpty())
+                .map(this::asText)
+                .map(this::abbreviate)
+                .orElse(NO_BODY);
+    }
+
+    public String forTextBody(Optional<String> body) {
+        return body.filter(text -> !text.isEmpty())
+                .map(this::abbreviate)
+                .orElse(NO_BODY);
+    }
+
+    @VisibleForTesting String asText(String body) throws IllegalArgumentException {
+       Preconditions.checkArgument(body != null);
+       return htmlTextExtractor.toPlainText(body);
+    }
+
+    @VisibleForTesting String abbreviate(String body) {
+        return StringUtils.abbreviate(body, MAX_PREVIEW_LENGTH);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
index ce86d03..9bfafe7 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/GetMessagesMethodTest.java
@@ -37,8 +37,12 @@ import org.apache.james.jmap.model.ClientId;
 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.MessageId;
+import org.apache.james.jmap.model.MessagePreviewGenerator;
 import org.apache.james.jmap.model.MessageProperties.MessageProperty;
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
@@ -48,6 +52,7 @@ import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.MockAuthenticator;
 import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
 import org.assertj.core.api.Condition;
 import org.assertj.core.data.MapEntry;
@@ -102,11 +107,16 @@ public class GetMessagesMethodTest {
     private MailboxSession session;
     private MailboxPath inboxPath;
     private ClientId clientId;
+    
+    private MessageFactory messageFactory;
 
     @Before
     public void setup() throws MailboxException {
         clientId = ClientId.of("#0");
         mailboxSessionMapperFactory = new InMemoryMailboxSessionMapperFactory();
+        HtmlTextExtractor htmlTextExtractor = new MailboxBasedHtmlTextExtractor(new DefaultTextExtractor());
+        MessagePreviewGenerator messagePreview = new MessagePreviewGenerator(htmlTextExtractor);
+        messageFactory = new MessageFactory(messagePreview);
         MockAuthenticator authenticator = new MockAuthenticator();
         authenticator.addUser(ROBERT.username, ROBERT.password);
         UnionMailboxACLResolver aclResolver = new UnionMailboxACLResolver();
@@ -123,28 +133,28 @@ public class GetMessagesMethodTest {
     
     @Test
     public void processShouldThrowWhenNullRequest() {
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         GetMessagesRequest request = null;
         assertThatThrownBy(() -> testee.process(request, mock(ClientId.class), mock(MailboxSession.class))).isInstanceOf(NullPointerException.class);
     }
 
     @Test
     public void processShouldThrowWhenNullSession() {
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         MailboxSession mailboxSession = null;
         assertThatThrownBy(() -> testee.process(mock(GetMessagesRequest.class), mock(ClientId.class), mailboxSession)).isInstanceOf(NullPointerException.class);
     }
 
     @Test
     public void processShouldThrowWhenNullClientId() {
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         ClientId clientId = null;
         assertThatThrownBy(() -> testee.process(mock(GetMessagesRequest.class), clientId, mock(MailboxSession.class))).isInstanceOf(NullPointerException.class);
     }
 
     @Test
     public void processShouldThrowWhenRequestHasAccountId() {
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         assertThatThrownBy(() -> testee.process(
                 GetMessagesRequest.builder().accountId("abc").build(), mock(ClientId.class), mock(MailboxSession.class))).isInstanceOf(NotImplementedException.class);
     }
@@ -166,7 +176,7 @@ public class GetMessagesMethodTest {
                           new MessageId(ROBERT, inboxPath, message3Uid)))
                 .build();
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
         
         assertThat(result).hasSize(1)
@@ -195,7 +205,7 @@ public class GetMessagesMethodTest {
                 .ids(ImmutableList.of(new MessageId(ROBERT, inboxPath, messageUid)))
                 .build();
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
         
         assertThat(result).hasSize(1)
@@ -219,7 +229,7 @@ public class GetMessagesMethodTest {
                 .properties(ImmutableList.of())
                 .build();
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
 
         assertThat(result).hasSize(1)
@@ -240,7 +250,7 @@ public class GetMessagesMethodTest {
                 .ids(ImmutableList.of(new MessageId(ROBERT, inboxPath, message1Uid)))
                 .build();
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         Stream<JmapResponse> result = testee.process(request, clientId, session);
 
         assertThat(result).hasSize(1)
@@ -264,7 +274,7 @@ public class GetMessagesMethodTest {
 
         Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.subject);
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
 
         assertThat(result).hasSize(1)
@@ -288,7 +298,7 @@ public class GetMessagesMethodTest {
 
         Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.textBody);
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
 
         assertThat(result).hasSize(1)
@@ -315,7 +325,7 @@ public class GetMessagesMethodTest {
 
         Set<MessageProperty> expected = Sets.newHashSet(MessageProperty.id, MessageProperty.headers);
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
 
         assertThat(result)
@@ -341,7 +351,7 @@ public class GetMessagesMethodTest {
                 .properties(ImmutableList.of("headers.from", "headers.heADER2"))
                 .build();
 
-        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory);
+        GetMessagesMethod testee = new GetMessagesMethod(mailboxSessionMapperFactory, mailboxSessionMapperFactory, mailboxSessionMapperFactory, messageFactory);
         List<JmapResponse> result = testee.process(request, clientId, session).collect(Collectors.toList());
 
         assertThat(result)

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
index b8ec9e6..f28630b 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
@@ -35,26 +35,34 @@ import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
 import org.apache.james.jmap.model.CreationMessageId;
 import org.apache.james.jmap.model.Message;
+import org.apache.james.jmap.model.MessageFactory;
 import org.apache.james.jmap.model.MessageId;
+import org.apache.james.jmap.model.MessagePreviewGenerator;
 import org.apache.james.jmap.model.SetMessagesRequest;
 import org.apache.james.jmap.model.SetMessagesResponse;
 import org.apache.james.jmap.send.MailFactory;
 import org.apache.james.jmap.send.MailMetadata;
 import org.apache.james.jmap.send.MailSpool;
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
 import org.apache.james.mailbox.store.TestId;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.mailet.Mail;
+import org.junit.Before;
 import org.junit.Test;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
 public class SetMessagesCreationProcessorTest {
+    
+    private MessageFactory messageFactory;
 
     private static final Message FAKE_MESSAGE = Message.builder()
             .id(MessageId.of("user|outbox|1"))
@@ -67,10 +75,17 @@ public class SetMessagesCreationProcessorTest {
             .date(ZonedDateTime.now())
             .preview("anything")
             .build();
+    
+    @Before
+    public void setup() {
+        HtmlTextExtractor htmlTextExtractor = new MailboxBasedHtmlTextExtractor(new DefaultTextExtractor());
+        MessagePreviewGenerator messagePreview = new MessagePreviewGenerator(htmlTextExtractor);
+        messageFactory = new MessageFactory(messagePreview);
+    }
 
     @Test
     public void processShouldReturnEmptyCreatedWhenRequestHasEmptyCreate() {
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null) {
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null, messageFactory) {
             @Override
             protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
 				Mailbox fakeOutbox = (Mailbox) mock(Mailbox.class);
@@ -104,7 +119,7 @@ public class SetMessagesCreationProcessorTest {
         when(mockSessionMapperFactory.createMessageMapper(any(MailboxSession.class)))
                 .thenReturn(stubMapper);
 
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, mockSessionMapperFactory, null, null, null) {
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, mockSessionMapperFactory, null, null, null, messageFactory) {
             @Override
             protected MessageWithId<Message> createMessageInOutboxAndSend(MessageWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox outbox, Function<Long, MessageId> buildMessageIdFromUid) {
                 return new MessageWithId<>(createdEntry.getCreationId(), FAKE_MESSAGE);
@@ -127,7 +142,7 @@ public class SetMessagesCreationProcessorTest {
     @Test(expected = MailboxRoleNotFoundException.class)
     public void processShouldThrowWhenOutboxNotFound() {
         // Given
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null) {
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null, messageFactory) {
             @Override
             protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
                 return Optional.empty();
@@ -149,7 +164,7 @@ public class SetMessagesCreationProcessorTest {
         MailFactory mockedMailFactory = mock(MailFactory.class);
 
         SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null,
-                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory) {
+                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory, messageFactory) {
             @Override
             protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
                 TestId stubMailboxId = mock(TestId.class);
@@ -178,7 +193,7 @@ public class SetMessagesCreationProcessorTest {
         MailFactory mockedMailFactory = mock(MailFactory.class);
 
         SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null,
-                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory) {
+                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory, messageFactory) {
             @Override
             protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
                 TestId stubMailboxId = mock(TestId.class);

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/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
index d34d7a9..f356460 100644
--- 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
@@ -19,6 +19,7 @@
 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;
@@ -30,11 +31,13 @@ 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.store.TestId;
 import org.apache.james.mailbox.store.mail.model.AttachmentId;
 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;
@@ -47,6 +50,17 @@ public class MailboxMessageTest {
     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);
+        messageFactory = new MessageFactory(messagePreview);
+    }
+    
     @Test(expected=IllegalStateException.class)
     public void buildShouldThrowWhenIdIsNull() {
         Message.builder().build();
@@ -241,8 +255,7 @@ public class MailboxMessageTest {
                 new PropertyBuilder(),
                 MAILBOX_ID);
         testMail.setModSeq(MOD_SEQ);
-        
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        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, "(No subject)", ImmutableMap.of(), ZONED_DATE);
@@ -263,8 +276,7 @@ public class MailboxMessageTest {
                 new PropertyBuilder(),
                 MAILBOX_ID);
         testMail.setModSeq(MOD_SEQ);
-        
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        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);
@@ -306,7 +318,7 @@ public class MailboxMessageTest {
                 .put("in-reply-to", "<SNT124-W2664003139C1E520CF4F6787D30@phx.gbl>")
                 .put("other-header", "other header value")
                 .build();
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
         Message expected = Message.builder()
                 .id(MessageId.of("user|box|0"))
                 .blobId("0")
@@ -341,32 +353,9 @@ public class MailboxMessageTest {
                 new PropertyBuilder(),
                 MAILBOX_ID);
         testMail.setModSeq(MOD_SEQ);
-        
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
         assertThat(testee.getTextBody()).hasValue("Mail body");
     }
-    
-    @Test
-    public void bodyWith256LengthShouldNotBeTruncated() {
-        String body256 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "00000000001111111111222222222233333333334444444444555555";
-        assertThat(body256.length()).isEqualTo(256);
-        assertThat(Message.computePreview(body256)).isEqualTo(body256);
-    }
-    
-    @Test
-    public void bodyWith257LengthShouldBeTruncated() {
-        String body257 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "000000000011111111112222222222333333333344444444445555555";
-        String expected = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
-                + "00000000001111111111222222222233333333334444444444555...";
-        assertThat(body257.length()).isEqualTo(257);
-        assertThat(expected.length()).isEqualTo(256);
-        assertThat(Message.computePreview(body257)).isEqualTo(expected);
-    }
 
     @Test
     public void previewShouldBeLimitedTo256Length() throws Exception {
@@ -389,8 +378,7 @@ public class MailboxMessageTest {
                 new PropertyBuilder(),
                 MAILBOX_ID);
         testMail.setModSeq(MOD_SEQ);
-        
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
         assertThat(testee.getPreview()).isEqualTo(expectedPreview);
     }
     
@@ -405,8 +393,7 @@ public class MailboxMessageTest {
                 new PropertyBuilder(),
                 MAILBOX_ID);
         testMail.setModSeq(MOD_SEQ);
-        
-        Message testee = Message.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
+        Message testee = messageFactory.fromMailboxMessage(testMail, ImmutableList.of(), x -> MessageId.of("user|box|" + x));
         assertThat(testee.getAttachments()).isEmpty();
     }
     
@@ -430,8 +417,7 @@ public class MailboxMessageTest {
                 .size(payload.length())
                 .type(type)
                 .build();
-
-        Message testee = Message.fromMailboxMessage(testMail, 
+        Message testee = messageFactory.fromMailboxMessage(testMail,
                 ImmutableList.of(org.apache.james.mailbox.store.mail.model.Attachment.builder()
                         .attachmentId(AttachmentId.from(blodId))
                         .bytes(payload.getBytes())

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePreviewGeneratorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePreviewGeneratorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePreviewGeneratorTest.java
new file mode 100644
index 0000000..f9ae3b3
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePreviewGeneratorTest.java
@@ -0,0 +1,187 @@
+/****************************************************************
+ * 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 static org.mockito.Mockito.when;
+
+import java.util.Optional;
+
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MessagePreviewGeneratorTest {
+    
+    private MessagePreviewGenerator testee;
+    private HtmlTextExtractor htmlTextExtractor;
+    
+    @Before
+    public void setUp() {
+        htmlTextExtractor = mock(HtmlTextExtractor.class);
+        testee = new MessagePreviewGenerator(htmlTextExtractor);
+    }
+
+    @Test
+    public void forHTMLBodyShouldReturnTruncatedStringWithoutHtmlTagWhenStringContainTagsAndIsLongerThan256Characters() {
+        //Given
+        String body = "This is a <b>HTML</b> mail containing <u>underlined part</u>, <i>italic part</i> and <u><i>underlined AND italic part</i></u>9999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "000000000011111111112222222222333333333344444444445555555";
+        String bodyWithoutTags = "This is a HTML mail containing underlined part, italic part and underlined AND italic part9999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "000000000011111111112222222222333333333344444444445555555";
+        String expected = "This is a HTML mail containing underlined part, italic part and underlined AND italic part9999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "00000000001111111111222222222233333333334444444444555...";
+        //When
+        when(htmlTextExtractor.toPlainText(body))
+            .thenReturn(bodyWithoutTags);
+        String actual = testee.forHTMLBody(Optional.of(body));
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void forHTMLBodyShouldReturnStringContainingEmptyWhenEmptyString() {
+        //Given
+        String body = "" ;
+        String expected = "(Empty)" ;
+        //When
+        when(htmlTextExtractor.toPlainText(body))
+            .thenReturn(expected);
+        String actual = testee.forHTMLBody(Optional.of(body));
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void forTextBodyShouldReturnTruncatedStringWhenStringContainTagsAndIsLongerThan256Characters() {
+        //Given
+        String body = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "This is a <b>HTML</b> mail containing <u>underlined part</u>, <i>italic part</i>88888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "000000000011111111112222222222333333333344444444445555555";
+        String expected = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "This is a <b>HTML</b> mail containing <u>underlined part</u>, <i>italic part</i>88888888889999999999"
+                + "00000000001111111111222222222233333333334444444444555...";
+        //When
+        String actual = testee.forTextBody(Optional.of(body));
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void forTextBodyShouldReturnStringContainingEmptyWhenEmptyString() {
+        //Given
+        String expected = "(Empty)" ;
+        //When
+        String actual = testee.forTextBody(Optional.empty());
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void asTextShouldReturnStringWithoutHtmlTag() {
+        //Given
+        String body = "This is a <b>HTML</b> mail";
+        String expected = "This is a HTML mail";
+        //When
+        when(htmlTextExtractor.toPlainText(body))
+            .thenReturn(expected);
+        String actual = testee.asText(body);
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void asTextShouldThrowWhenNullString () {
+        testee.asText(null);
+    }
+
+    @Test
+    public void asTextShouldReturnEmptyStringWhenEmptyString() {
+        //Given
+        String body = "";
+        String expected = "";
+        //When
+        when(htmlTextExtractor.toPlainText(body))
+            .thenReturn(expected);
+        String actual = testee.asText(body);
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void abbreviateShouldNotTruncateAbodyWith256Length() {
+        //Given
+        String body256 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "00000000001111111111222222222233333333334444444444555555";
+        //When
+        String actual = testee.abbreviate(body256);
+        //Then
+        assertThat(body256.length()).isEqualTo(256);
+        assertThat(actual).isEqualTo(body256);
+    }
+
+    @Test
+    public void abbreviateShouldTruncateAbodyWith257Length() {
+        //Given
+        String body257 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "000000000011111111112222222222333333333344444444445555555";
+        String expected = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+                + "00000000001111111111222222222233333333334444444444555...";
+        //When
+        String actual = testee.abbreviate(body257);
+        //Then
+        assertThat(body257.length()).isEqualTo(257);
+        assertThat(expected.length()).isEqualTo(256);
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void abbreviateShouldReturnNullStringWhenNullString() {
+        //Given
+        String body = null;
+        String expected = null;
+        //When
+        String actual = testee.abbreviate(body);
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void abbreviateShouldReturnEmptyStringWhenEmptyString() {
+        //Given
+        String body = "";
+        String expected = "";
+        //When
+        String actual = testee.abbreviate(body);
+        //Then
+        assertThat(actual).isEqualTo(expected);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/7ab817e6/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
index 98cd1d0..f5128a6 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/MailFactoryTest.java
@@ -27,9 +27,14 @@ import javax.mail.Flags;
 import javax.mail.util.SharedByteArrayInputStream;
 
 import org.apache.james.jmap.model.Message;
+import org.apache.james.jmap.model.MessageFactory;
 import org.apache.james.jmap.model.MessageId;
+import org.apache.james.jmap.model.MessagePreviewGenerator;
+import org.apache.james.jmap.utils.HtmlTextExtractor;
+import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
 import org.apache.james.mailbox.FlagsBuilder;
 import org.apache.james.mailbox.store.TestId;
+import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 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;
@@ -69,7 +74,10 @@ public class MailFactoryTest {
                 new FlagsBuilder().add(Flags.Flag.SEEN).build(),
                 propertyBuilder,
                 TestId.of(2));
-        jmapMessage = Message.fromMailboxMessage(mailboxMessage, ImmutableList.of(), x -> MessageId.of("test|test|" + x));
+        HtmlTextExtractor htmlTextExtractor = new MailboxBasedHtmlTextExtractor(new DefaultTextExtractor());
+        MessagePreviewGenerator messagePreview = new MessagePreviewGenerator(htmlTextExtractor);
+        MessageFactory messageFactory = new MessageFactory(messagePreview) ;
+        jmapMessage = messageFactory.fromMailboxMessage(mailboxMessage, ImmutableList.of(), x -> MessageId.of("test|test|" + x));
     }
 
     @Test(expected=NullPointerException.class)


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


Mime
View raw message