Repository: james-project
Updated Branches:
  refs/heads/master ce9321a4c -> b03574767


JAMES-2390 Avoid de-duplicating Attachment meta-data

This avoids a costly byte concatenation.

However the main benefit is upon attachment right setting.
Given an attachment id, we will locate the containing messages, then the 
containing mailboxes then resolve rights.
If an attachmentId is shared for several attachments being the same file, then 
there could be a lot of users owning this attachment.
Resolving rights will mechanically be more expensive. (Think IE pictures in 
signatures of email)
This meta-data sharing can be abandoned with no harm, and deduplication pushed 
down to the attachment level.
The consequence is that attachment right resolution is done independently, thus 
for high frequency attachment, no penalty is payed.

This change is motivated by analyses of production generated logs:
 - They show systematic failure upon specific attachments upon cassandra reads.
 - These failures were due to a bad paging
 - However, even if this issue was fixed separately, this still denotates a 
high cardinality in attachmentIds index table heavily penalizing reads.

Please note that this change will only improve performance of newly generated 
attachment.


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

Branch: refs/heads/master
Commit: bfa5e426ac73e1da5866e0b7e772507204b3adcb
Parents: 3081d9ab
Author: benwa <btell...@linagora.com>
Authored: Sun May 6 20:07:58 2018 +0700
Committer: benwa <btell...@linagora.com>
Committed: Tue May 8 09:15:36 2018 +0700

----------------------------------------------------------------------
 mailbox/api/pom.xml                             |   4 +
 .../apache/james/mailbox/model/Attachment.java  |   4 +-
 .../james/mailbox/model/AttachmentId.java       |  36 +---
 .../org/apache/james/mailbox/model/BlobId.java  |   8 +-
 .../james/mailbox/model/AttachmentIdTest.java   |  66 +-----
 .../james/mailbox/model/AttachmentTest.java     |  25 +--
 .../apache/james/mailbox/model/BlobIdTest.java  |  16 ++
 .../AbstractMailboxManagerAttachmentTest.java   |  29 ---
 .../mailbox/store/StoreBlobManagerTest.java     |   2 +-
 .../store/mail/model/AttachmentMapperTest.java  |   6 +-
 .../org/apache/james/utils/MessageIdProbe.java  |  17 ++
 .../integration/SetMessagesMethodTest.java      | 204 +++++++++++--------
 .../integration/cucumber/DownloadStepdefs.java  |  28 ++-
 .../integration/cucumber/UploadStepdefs.java    |   6 +-
 .../test/resources/cucumber/GetMessages.feature |   2 -
 15 files changed, 205 insertions(+), 248 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/pom.xml
----------------------------------------------------------------------
diff --git a/mailbox/api/pom.xml b/mailbox/api/pom.xml
index c7008e1..867178e 100644
--- a/mailbox/api/pom.xml
+++ b/mailbox/api/pom.xml
@@ -79,6 +79,10 @@
             <artifactId>commons-lang3</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-text</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/main/java/org/apache/james/mailbox/model/Attachment.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/Attachment.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/Attachment.java
index cbe9113..47b2f67 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/Attachment.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/Attachment.java
@@ -71,7 +71,7 @@ public class Attachment {
             if (attachmentId != null) {
                 return attachmentId;
             }
-            return AttachmentId.forPayloadAndType(bytes, type);
+            return AttachmentId.random();
         }
 
         private long size() {
@@ -118,7 +118,7 @@ public class Attachment {
 
     public Blob toBlob() {
         return Blob.builder()
-            .id(BlobId.fromString(attachmentId.getId()))
+            .id(BlobId.fromBytes(bytes))
             .payload(bytes)
             .contentType(type)
             .build();

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentId.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentId.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentId.java
index 21b94a7..792b588 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentId.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/AttachmentId.java
@@ -19,50 +19,26 @@
 package org.apache.james.mailbox.model;
 
 import java.nio.charset.StandardCharsets;
-import java.util.Optional;
 import java.util.UUID;
 
-import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.james.mime4j.codec.DecodeMonitor;
-import org.apache.james.mime4j.field.ContentTypeFieldImpl;
-import org.apache.james.mime4j.stream.RawField;
+import org.apache.commons.text.RandomStringGenerator;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.primitives.Bytes;
 
 public class AttachmentId {
 
-    private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
-
-    public static AttachmentId forPayloadAndType(byte[] payload, String 
contentType) {
-        Preconditions.checkNotNull(payload);
-        Preconditions.checkArgument(!Strings.isNullOrEmpty(contentType));
-
-        return new AttachmentId(computeRawId(payload, contentType));
-    }
-
-    private static String computeRawId(final byte[] payload, final String 
contentType) {
-        return DigestUtils.sha256Hex(
-            Bytes.concat(
-                asMimeType(contentType).getBytes(StandardCharsets.UTF_8),
-                
DigestUtils.sha256Hex(payload).getBytes(StandardCharsets.UTF_8)));
-    }
-
-    @VisibleForTesting static String asMimeType(String contentType) {
-        return Optional.ofNullable(ContentTypeFieldImpl.PARSER
-                    .parse(new RawField("ContentType", contentType), 
DecodeMonitor.SILENT)
-                    .getMimeType())
-                .orElse(DEFAULT_MIME_TYPE);
-    }
+    public static final RandomStringGenerator RANDOM_STRING_GENERATOR = new 
RandomStringGenerator.Builder().withinRange('a', 'z').build();
 
     public static AttachmentId from(BlobId blobId) {
         return new AttachmentId(blobId.asString());
     }
 
+    public static AttachmentId random() {
+        return new AttachmentId(RANDOM_STRING_GENERATOR.generate(20));
+    }
+
     public static AttachmentId from(String id) {
         Preconditions.checkNotNull(id);
         Preconditions.checkArgument(!id.isEmpty());

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/main/java/org/apache/james/mailbox/model/BlobId.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/main/java/org/apache/james/mailbox/model/BlobId.java 
b/mailbox/api/src/main/java/org/apache/james/mailbox/model/BlobId.java
index 476eca2..34a62e3 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/BlobId.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/BlobId.java
@@ -21,11 +21,18 @@ package org.apache.james.mailbox.model;
 
 import java.util.Objects;
 
+import org.apache.commons.codec.digest.DigestUtils;
+
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 
 public class BlobId {
+    public static BlobId fromBytes(byte[] bytes) {
+        Preconditions.checkNotNull(bytes);
+        return new BlobId(DigestUtils.sha256Hex(bytes));
+    }
+
     public static BlobId fromString(String raw) {
         Preconditions.checkArgument(!Strings.isNullOrEmpty(raw));
         return new BlobId(raw);
@@ -56,7 +63,6 @@ public class BlobId {
         return Objects.hash(id);
     }
 
-
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentIdTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentIdTest.java
 
b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentIdTest.java
index 092d739..c69e9ce 100644
--- 
a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentIdTest.java
+++ 
b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentIdTest.java
@@ -24,48 +24,18 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.UUID;
 
-import org.apache.james.util.ClassLoaderUtils;
 import org.junit.Test;
 
 public class AttachmentIdTest {
 
     @Test
-    public void forPayloadAndTypeShouldCalculateTheUnderlyingSha256() {
-        AttachmentId attachmentId = 
AttachmentId.forPayloadAndType("payload".getBytes(), "text/plain");
-        String expectedId = 
"d3a2642ee092a1b32c0a83cf94fc2499f7495b7b91b1bd434302a0a4c2aa4278";
-        assertThat(attachmentId.getId()).isEqualTo(expectedId);
-    }
-
-    @Test
-    public void 
forPayloadAndTypeShouldCalculateDifferentSha256WhenContentTypeIsDifferent() {
-        AttachmentId attachmentId = 
AttachmentId.forPayloadAndType("payload".getBytes(), "text/plain");
-        AttachmentId attachmentId2 = 
AttachmentId.forPayloadAndType("payload".getBytes(), "text/html");
+    public void randomShouldGenerateDifferentIds() {
+        AttachmentId attachmentId = AttachmentId.random();
+        AttachmentId attachmentId2 = AttachmentId.random();
         assertThat(attachmentId.getId()).isNotEqualTo(attachmentId2.getId());
     }
 
     @Test
-    public void 
forPayloadAndTypeShouldCalculateSameSha256WhenMimeTypeIsSameButNotParameters() {
-        AttachmentId attachmentId = 
AttachmentId.forPayloadAndType("payload".getBytes(), "text/html; 
charset=UTF-8");
-        AttachmentId attachmentId2 = 
AttachmentId.forPayloadAndType("payload".getBytes(), "text/html; 
charset=UTF-16");
-        assertThat(attachmentId.getId()).isEqualTo(attachmentId2.getId());
-    }
-    
-    @Test
-    public void forPayloadAndTypeShouldThrowWhenPayloadIsNull() {
-        assertThatThrownBy(() -> AttachmentId.forPayloadAndType(null, 
"text/plain")).isInstanceOf(NullPointerException.class);
-    }
-
-    @Test
-    public void forPayloadAndTypeShouldThrowWhenTypeIsNull() {
-        assertThatThrownBy(() -> 
AttachmentId.forPayloadAndType("payload".getBytes(), 
null)).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
-    public void forPayloadAndTypeShouldThrowWhenTypeIsEmpty() {
-        assertThatThrownBy(() -> 
AttachmentId.forPayloadAndType("payload".getBytes(), 
"")).isInstanceOf(IllegalArgumentException.class);
-    }
-
-    @Test
     public void fromShouldThrowWhenIdIsNull() {
         String value = null;
         assertThatThrownBy(() -> 
AttachmentId.from(value)).isInstanceOf(NullPointerException.class);
@@ -103,34 +73,4 @@ public class AttachmentIdTest {
         assertThat(attachmentId.asUUID())
             
.isEqualTo(UUID.fromString("2f3a4fcc-ca64-36e3-9bcf-33e92dd93135"));
     }
-
-    @Test
-    public void 
asMimeTypeShouldReturnOnlyMimeTypeFromContentTypeWhenContainingParameters() {
-        String mimeType = AttachmentId.asMimeType("text/html; charset=UTF-8");
-        
-        assertThat(mimeType).isEqualTo("text/html");
-    }
-
-    @Test
-    public void 
asMimeTypeShouldReturnOnlyMimeTypeFromContentTypeWhenNoParameters() {
-        String mimeType = AttachmentId.asMimeType("text/html");
-        
-        assertThat(mimeType).isEqualTo("text/html");
-    }
-
-    @Test
-    public void 
asMimeTypeShouldReturnDefaultMimeTypeWhenContentTypeIsUnparsable() {
-        String mimeType = AttachmentId.asMimeType("text");
-        
-        assertThat(mimeType).isEqualTo("application/octet-stream");
-    }
-
-    @Test
-    public void 
forPayloadAndTypeShouldCalculateDifferentHashesWhenCraftedSha1Collision() 
throws Exception {
-        byte[] payload1 = 
ClassLoaderUtils.getSystemResourceAsByteArray("shattered-1.pdf");
-        byte[] payload2 = 
ClassLoaderUtils.getSystemResourceAsByteArray("shattered-2.pdf");
-        AttachmentId attachmentId1 = AttachmentId.forPayloadAndType(payload1, 
"application/pdf");
-        AttachmentId attachmentId2 = AttachmentId.forPayloadAndType(payload2, 
"application/pdf");
-        assertThat(attachmentId1).isNotEqualTo(attachmentId2);
-    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentTest.java 
b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentTest.java
index 5a7fa24..d73e073 100644
--- 
a/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentTest.java
+++ 
b/mailbox/api/src/test/java/org/apache/james/mailbox/model/AttachmentTest.java
@@ -47,7 +47,7 @@ public class AttachmentTest {
     }
 
     @Test
-    public void getByteShouldReturnByteArrayRepresentingTheAttachment() throws 
Exception {
+    public void getByteShouldReturnByteArrayRepresentingTheAttachment() {
         String input = "mystream";
         Attachment attachment = Attachment.builder()
             .bytes(input.getBytes(CHARSET))
@@ -104,33 +104,20 @@ public class AttachmentTest {
     @Test (expected = IllegalStateException.class)
     public void buildShouldThrowWhenBytesIsNotProvided() {
         Attachment.builder()
-            
.attachmentId(AttachmentId.forPayloadAndType("mystream".getBytes(CHARSET), 
"type"))
+            .attachmentId(AttachmentId.random())
             .build();
     }
 
     @Test (expected = IllegalStateException.class)
     public void buildShouldThrowWhenTypeIsNotProvided() {
         Attachment.builder()
-            
.attachmentId(AttachmentId.forPayloadAndType("mystream".getBytes(CHARSET), 
"type"))
+            .attachmentId(AttachmentId.random())
             .bytes("mystream".getBytes(CHARSET))
             .build();
     }
 
     @Test
-    public void buildShouldSetTheAttachmentId() throws Exception {
-        byte[] bytes = "mystream".getBytes(CHARSET);
-        String type = "content";
-        Attachment attachment = Attachment.builder()
-                .bytes(bytes)
-                .type(type)
-                .build();
-        AttachmentId expected = AttachmentId.forPayloadAndType(bytes, type);
-
-        assertThat(attachment.getAttachmentId()).isEqualTo(expected);
-    }
-
-    @Test
-    public void buildShouldSetTheSize() throws Exception {
+    public void buildShouldSetTheSize() {
         String input = "mystream";
         Attachment attachment = Attachment.builder()
                 .bytes(input.getBytes(CHARSET))
@@ -141,7 +128,7 @@ public class AttachmentTest {
     }
 
     @Test
-    public void toBlobShouldGenerateTheAttachmentBlob() throws Exception {
+    public void toBlobShouldGenerateTheAttachmentBlob() {
         byte[] bytes = "mystream".getBytes(CHARSET);
         String content = "content";
         Attachment attachment = Attachment.builder()
@@ -149,7 +136,7 @@ public class AttachmentTest {
             .type(content)
             .build();
         Blob expected = Blob.builder()
-            .id(BlobId.fromString(attachment.getAttachmentId().getId()))
+            .id(BlobId.fromBytes(bytes))
             .contentType(content)
             .payload(bytes)
             .build();

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobIdTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobIdTest.java 
b/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobIdTest.java
index fefeed0..c5a5740 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobIdTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/BlobIdTest.java
@@ -22,6 +22,8 @@ package org.apache.james.mailbox.model;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import java.nio.charset.StandardCharsets;
+
 import org.junit.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
@@ -52,4 +54,18 @@ public class BlobIdTest {
         assertThat(BlobId.fromString("abc").asString())
             .isEqualTo("abc");
     }
+
+    @Test
+    public void fromBytesShouldProduceASHA256() {
+        
assertThat(BlobId.fromBytes("abc".getBytes(StandardCharsets.UTF_8)).asString())
+            
.isEqualTo("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+    }
+
+    @Test
+    public void fromBytesShouldCalculateSameSha256() {
+        byte[] bytes = "abc".getBytes(StandardCharsets.UTF_8);
+
+        assertThat(BlobId.fromBytes(bytes))
+            .isEqualTo(BlobId.fromBytes(bytes));
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMailboxManagerAttachmentTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMailboxManagerAttachmentTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMailboxManagerAttachmentTest.java
index 99e9566..562066a 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMailboxManagerAttachmentTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMailboxManagerAttachmentTest.java
@@ -181,34 +181,5 @@ public abstract class AbstractMailboxManagerAttachmentTest 
{
         List<MessageAttachment> attachments = messages.next().getAttachments();
         assertThat(attachments).hasSize(0);
     }
-
-    @Test
-    public void appendMessageShouldStoreOnceWhenDuplicateAttachment() throws 
Exception {
-        InputStream mailInputStream = 
ClassLoader.getSystemResourceAsStream("eml/oneAttachmentAndSomeTextInlined.eml");
-        InputStream mailInputStream2 = 
ClassLoader.getSystemResourceAsStream("eml/oneAttachmentAndSomeTextInlined.eml");
-        String user2 = "us...@domain.tld";
-        MailboxSession user2MailboxSession = new MockMailboxSession(user2);
-        MessageMapper user2MessageMapper = 
getMailboxSessionMapperFactory().getMessageMapper(user2MailboxSession);
-        MailboxMapper user2MailboxMapper = 
getMailboxSessionMapperFactory().getMailboxMapper(user2MailboxSession);
-        MailboxPath user2InboxPath = MailboxPath.forUser(user2, "INBOX");
-        mailboxManager.createMailbox(user2InboxPath, user2MailboxSession);
-        Mailbox user2Inbox = 
user2MailboxMapper.findMailboxByPath(user2InboxPath);
-        MessageManager user2InboxMessageManager = 
mailboxManager.getMailbox(user2InboxPath, user2MailboxSession);
-
-        
inboxMessageManager.appendMessage(MessageManager.AppendCommand.builder()
-            .build(mailInputStream),
-            mailboxSession);
-        
user2InboxMessageManager.appendMessage(MessageManager.AppendCommand.builder()
-                .build(mailInputStream2),
-            user2MailboxSession);
-        Iterator<MailboxMessage> messages = messageMapper.findInMailbox(inbox, 
MessageRange.all(), FetchType.Full, 1);
-        Iterator<MailboxMessage> user2Messages = 
user2MessageMapper.findInMailbox(user2Inbox, MessageRange.all(), 
FetchType.Full, 1);
-        assertThat(messages.hasNext()).isTrue();
-        List<MessageAttachment> attachments = messages.next().getAttachments();
-        assertThat(attachments).hasSize(1);
-        assertThat(user2Messages.hasNext()).isTrue();
-        List<MessageAttachment> user2Attachments = 
user2Messages.next().getAttachments();
-        assertThat(attachments.equals(user2Attachments)).isTrue();
-    }
 }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
index 197d8d7..fc387bb 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreBlobManagerTest.java
@@ -82,7 +82,7 @@ public class StoreBlobManagerTest {
 
         assertThat(blobManager.retrieve(BLOB_ID_ATTACHMENT, session))
             .isEqualTo(Blob.builder()
-                .id(BLOB_ID_ATTACHMENT)
+                
.id(BlobId.fromString("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"))
                 .contentType(CONTENT_TYPE)
                 .payload(BYTES)
                 .build());

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
----------------------------------------------------------------------
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
index 03b27ce..5aea667 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AttachmentMapperTest.java
@@ -39,7 +39,7 @@ import org.junit.rules.ExpectedException;
 import com.google.common.collect.ImmutableList;
 
 public abstract class AttachmentMapperTest {
-    private static final AttachmentId UNKNOWN_ATTACHMENT_ID = 
AttachmentId.forPayloadAndType("unknown".getBytes(StandardCharsets.UTF_8), 
"type");
+    private static final AttachmentId UNKNOWN_ATTACHMENT_ID = 
AttachmentId.from("unknown");
     public static final Username OWNER = Username.fromRawValue("owner");
     public static final Username ADDITIONAL_OWNER = 
Username.fromRawValue("additionalOwner");
 
@@ -108,13 +108,13 @@ public abstract class AttachmentMapperTest {
     }
 
     @Test
-    public void getAttachmentsShouldThrowWhenNullAttachmentId() throws 
Exception {
+    public void getAttachmentsShouldThrowWhenNullAttachmentId() {
         expected.expect(IllegalArgumentException.class);
         attachmentMapper.getAttachments(null);
     }
 
     @Test
-    public void 
getAttachmentsShouldReturnEmptyListWhenNonReferencedAttachmentId() throws 
Exception {
+    public void 
getAttachmentsShouldReturnEmptyListWhenNonReferencedAttachmentId() {
         List<Attachment> attachments = 
attachmentMapper.getAttachments(ImmutableList.of(UNKNOWN_ATTACHMENT_ID));
 
         assertThat(attachments).isEmpty();

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/server/container/guice/protocols/jmap/src/main/java/org/apache/james/utils/MessageIdProbe.java
----------------------------------------------------------------------
diff --git 
a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/utils/MessageIdProbe.java
 
b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/utils/MessageIdProbe.java
index 66d05ae..5bfb6a7 100644
--- 
a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/utils/MessageIdProbe.java
+++ 
b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/utils/MessageIdProbe.java
@@ -29,11 +29,15 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageIdManager;
 import org.apache.james.mailbox.MessageManager.FlagsUpdateMode;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.FetchGroupImpl;
 import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageAttachment;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageResult;
 
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
 import com.google.common.collect.ImmutableList;
 
 public class MessageIdProbe implements GuiceProbe {
@@ -57,4 +61,17 @@ public class MessageIdProbe implements GuiceProbe {
 
         messageIdManager.setFlags(newFlags, FlagsUpdateMode.REPLACE, 
messageId, mailboxIds, mailboxSession);
     }
+
+    public List<AttachmentId> retrieveAttachmentIds(MessageId messageId, 
String username) throws MailboxException {
+        MailboxSession mailboxSession = 
mailboxManager.createSystemSession(username);
+        List<MessageResult> messages = messageIdManager.getMessages(
+            ImmutableList.of(messageId),
+            FetchGroupImpl.MINIMAL,
+            mailboxSession);
+
+        return messages.stream()
+            .flatMap(Throwing.function(messageResult -> 
messageResult.getAttachments().stream()))
+            .map(MessageAttachment::getAttachmentId)
+            .collect(Guavate.toImmutableList());
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index ea5321a..8dda617 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -1910,7 +1910,7 @@ public abstract class SetMessagesMethodTest {
             .bytes("attachment".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
+        String uploadedBlobId = uploadAttachment(attachment);
         String requestBody = "[" +
             "  [" +
             "    \"setMessages\"," +
@@ -1921,7 +1921,7 @@ public abstract class SetMessagesMethodTest {
             "        \"subject\": \"subject\"," +
             "        \"keywords\": {\"$Draft\": true}," +
             "        \"attachments\": [" +
-            "                {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "                {\"blobId\" : \"" + uploadedBlobId + "\", " +
             "                 \"type\" : \"" + attachment.getType() + "\"," +
             "                 \"size\" : " + attachment.getSize() + "}" +
             "             ]," +
@@ -1949,11 +1949,6 @@ public abstract class SetMessagesMethodTest {
     @Test
     public void setMessagesShouldNotAllowDraftCreationInSomeoneElseMailbox() 
throws Exception {
         String messageCreationId = "creationId1337";
-        Attachment attachment = Attachment.builder()
-            .bytes("attachment".getBytes(StandardCharsets.UTF_8))
-            .type("application/octet-stream")
-            .build();
-        uploadAttachment(attachment);
         String requestBody = "[" +
             "  [" +
             "    \"setMessages\"," +
@@ -1987,11 +1982,6 @@ public abstract class SetMessagesMethodTest {
     @Test
     public void setMessagesShouldNotAllowDraftCreationInADelegatedMailbox() 
throws Exception {
         String messageCreationId = "creationId1337";
-        Attachment attachment = Attachment.builder()
-            .bytes("attachment".getBytes(StandardCharsets.UTF_8))
-            .type("application/octet-stream")
-            .build();
-        uploadAttachment(attachment);
 
         jmapServer.getProbe(ACLProbeImpl.class)
             .addRights(
@@ -3664,12 +3654,12 @@ public abstract class SetMessagesMethodTest {
             .bytes("attachment".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
+        String uploadedAttachment1 = uploadAttachment(attachment);
         Attachment attachment2 = Attachment.builder()
             .bytes("attachment2".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment2);
+        String uploadedAttachment2 = uploadAttachment(attachment2);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -3685,10 +3675,10 @@ public abstract class SetMessagesMethodTest {
             "        \"textBody\": \"Test body\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment1 + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + "}," +
-            "               {\"blobId\" : \"" + 
attachment2.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment2 + "\", " +
             "               \"type\" : \"" + attachment2.getType() + "\", " +
             "               \"size\" : " + attachment2.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
@@ -3715,12 +3705,12 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
             .body(ARGUMENTS + ".created", aMapWithSize(1))
             .body(createdPath + ".attachments", hasSize(2))
-            .body(firstAttachment + ".blobId", 
equalTo(attachment.getAttachmentId().getId()))
+            .body(firstAttachment + ".blobId", equalTo(uploadedAttachment1))
             .body(firstAttachment + ".type", 
equalTo("application/octet-stream; charset=UTF-8"))
             .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
             .body(firstAttachment + ".cid", nullValue())
             .body(firstAttachment + ".isInline", equalTo(false))
-            .body(secondAttachment + ".blobId", 
equalTo(attachment2.getAttachmentId().getId()))
+            .body(secondAttachment + ".blobId", equalTo(uploadedAttachment2))
             .body(secondAttachment + ".type", 
equalTo("application/octet-stream; charset=UTF-8"))
             .body(secondAttachment + ".size", equalTo((int) 
attachment2.getSize()))
             .body(secondAttachment + ".cid", equalTo("123456789"))
@@ -3733,17 +3723,17 @@ public abstract class SetMessagesMethodTest {
             .bytes("attachment".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
+        String uploadedAttachment1 = uploadAttachment(attachment);
         Attachment attachment2 = Attachment.builder()
             .bytes("attachment2".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment2);
+        String uploadedAttachment2 = uploadAttachment(attachment2);
         Attachment attachment3 = Attachment.builder()
             .bytes("attachment3".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment3);
+        String uploadedAttachment3 = uploadAttachment(attachment3);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -3764,21 +3754,21 @@ public abstract class SetMessagesMethodTest {
             "          \"attachments\":" +
             "          [" +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment1 + "\", " +
             "              \"type\" : \"" + attachment.getType() + "\", " +
             "              \"size\" : " + attachment.getSize() + "," +
             "              \"name\" : \"ديناصور.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment2.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment2 + "\", " +
             "              \"type\" : \"" + attachment2.getType() + "\", " +
             "              \"size\" : " + attachment2.getSize() + "," +
             "              \"name\" : 
\"эволюционировать.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment3.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment3 + "\", " +
             "              \"type\" : \"" + attachment3.getType() + "\", " +
             "              \"size\" : " + attachment3.getSize() + "," +
             "              \"name\" : \"进化还是不.png\"," +
@@ -3819,19 +3809,19 @@ public abstract class SetMessagesMethodTest {
             .bytes("attachment".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
+        String uploadedAttachment1 = uploadAttachment(attachment);
 
         Attachment attachment2 = Attachment.builder()
             .bytes("attachment2".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment2);
+        String uploadedAttachment2 = uploadAttachment(attachment2);
 
         Attachment attachment3 = Attachment.builder()
             .bytes("attachment3".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment3);
+        String uploadedAttachment3 = uploadAttachment(attachment3);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -3852,21 +3842,21 @@ public abstract class SetMessagesMethodTest {
             "          \"attachments\":" +
             "          [" +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment1 + "\", " +
             "              \"type\" : \"" + attachment.getType() + "\", " +
             "              \"size\" : " + attachment.getSize() + "," +
             "              \"name\" : \"ديناصور.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment2.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment2 + "\", " +
             "              \"type\" : \"" + attachment2.getType() + "\", " +
             "              \"size\" : " + attachment2.getSize() + "," +
             "              \"name\" : 
\"эволюционировать.png\", " +
             "              \"isInline\" : false" +
             "            }," +
             "            {" +
-            "              \"blobId\" : \"" + 
attachment3.getAttachmentId().getId() + "\", " +
+            "              \"blobId\" : \"" + uploadedAttachment3 + "\", " +
             "              \"type\" : \"" + attachment3.getType() + "\", " +
             "              \"size\" : " + attachment3.getSize() + "," +
             "              \"name\" : \"进化还是不.png\"," +
@@ -3919,20 +3909,30 @@ public abstract class SetMessagesMethodTest {
             .body(thirdAttachment + ".name", equalTo("进化还是不.png"));
     }
 
-    private void uploadAttachment(Attachment attachment) throws IOException {
-        with()
+    private String uploadAttachment(Attachment attachment) throws IOException {
+        return with()
             .header("Authorization", accessToken.serialize())
             .contentType(attachment.getType())
             .content(attachment.getStream())
-            .post("/upload");
+            .post("/upload")
+        .then()
+            .extract()
+            .body()
+            .jsonPath()
+            .getString("blobId");
     }
 
-    private void uploadTextAttachment(Attachment attachment) throws 
IOException {
-        with()
+    private String uploadTextAttachment(Attachment attachment) throws 
IOException {
+        return with()
             .header("Authorization", accessToken.serialize())
             .contentType(attachment.getType())
             .content(new String(IOUtils.toByteArray(attachment.getStream()), 
StandardCharsets.UTF_8))
-            .post("/upload");
+            .post("/upload")
+        .then()
+            .extract()
+            .body()
+            .jsonPath()
+            .getString("blobId");
     }
 
     @Test
@@ -3948,8 +3948,7 @@ public abstract class SetMessagesMethodTest {
             .bytes(rawBytes)
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
-        String expectedBlobId = attachment.getAttachmentId().getId();
+        String uploadedAttachment = uploadAttachment(attachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -3965,7 +3964,7 @@ public abstract class SetMessagesMethodTest {
             "        \"textBody\": \"Test body\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
@@ -3996,7 +3995,7 @@ public abstract class SetMessagesMethodTest {
 
         String firstMessage = ARGUMENTS + ".list[0]";
         String firstAttachment = firstMessage + ".attachments[0]";
-        given()
+        String blobId = given()
             .header("Authorization", accessToken.serialize())
             .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + 
"\"]}, \"#0\"]]")
         .when()
@@ -4006,21 +4005,25 @@ public abstract class SetMessagesMethodTest {
             .body(NAME, equalTo("messages"))
             .body(ARGUMENTS + ".list", hasSize(1))
             .body(firstMessage + ".attachments", hasSize(1))
-            .body(firstAttachment + ".blobId", equalTo(expectedBlobId))
             .body(firstAttachment + ".type", 
equalTo("application/octet-stream"))
             .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
             .body(firstAttachment + ".cid", equalTo("123456789"))
-            .body(firstAttachment + ".isInline", equalTo(true));
+            .body(firstAttachment + ".isInline", equalTo(true))
+            .extract()
+            .jsonPath()
+            .getString(firstAttachment + ".blobId");
+
+        checkBlobContent(blobId, rawBytes);
     }
 
     @Test
     public void 
attachmentsShouldBeRetrievedWhenChainingSetMessagesAndGetMessagesTextAttachment()
 throws Exception {
+        byte[] rawBytes = ByteStreams.toByteArray(new ZeroedInputStream(_1MB));
         Attachment attachment = Attachment.builder()
-            .bytes(ByteStreams.toByteArray(new ZeroedInputStream(_1MB)))
+            .bytes(rawBytes)
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
-        String expectedBlobId = attachment.getAttachmentId().getId();
+        String uploadedAttachment = uploadAttachment(attachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -4036,7 +4039,7 @@ public abstract class SetMessagesMethodTest {
             "        \"textBody\": \"Test body\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + ", " +
             "               \"cid\" : \"123456789\", " +
@@ -4068,7 +4071,7 @@ public abstract class SetMessagesMethodTest {
 
         String firstMessage = ARGUMENTS + ".list[0]";
         String firstAttachment = firstMessage + ".attachments[0]";
-        given()
+        String blobId = given()
             .header("Authorization", accessToken.serialize())
             .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + 
"\"]}, \"#0\"]]")
         .when()
@@ -4078,11 +4081,15 @@ public abstract class SetMessagesMethodTest {
             .body(NAME, equalTo("messages"))
             .body(ARGUMENTS + ".list", hasSize(1))
             .body(firstMessage + ".attachments", hasSize(1))
-            .body(firstAttachment + ".blobId", equalTo(expectedBlobId))
             .body(firstAttachment + ".type", 
equalTo("application/octet-stream"))
             .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
             .body(firstAttachment + ".cid", equalTo("123456789"))
-            .body(firstAttachment + ".isInline", equalTo(true));
+            .body(firstAttachment + ".isInline", equalTo(true))
+            .extract()
+            .jsonPath()
+            .getString(firstAttachment + ".blobId");
+
+        checkBlobContent(blobId, rawBytes);
     }
 
     private boolean isAnyMessageFoundInInbox(AccessToken recipientToken) {
@@ -4106,13 +4113,14 @@ public abstract class SetMessagesMethodTest {
 
     @Test
     public void 
attachmentsAndBodysShouldBeRetrievedWhenChainingSetMessagesAndGetMessagesWithMixedTextAndHtmlBodyAndHtmlAttachment()
 throws Exception {
+        byte[] rawBytes = ("<html>\n" +
+            "  <body>attachment</body>\n" + // needed indentation, else 
restassured is adding some
+            "</html>").getBytes(StandardCharsets.UTF_8);
         Attachment attachment = Attachment.builder()
-            .bytes(("<html>\n" +
-                    "  <body>attachment</body>\n" + // needed indentation, 
else restassured is adding some
-                    "</html>").getBytes(StandardCharsets.UTF_8))
+            .bytes(rawBytes)
             .type("text/html; charset=UTF-8")
             .build();
-        uploadTextAttachment(attachment);
+        String uploadedBlobId = uploadTextAttachment(attachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -4129,7 +4137,7 @@ public abstract class SetMessagesMethodTest {
             "        \"htmlBody\": \"Test <b>body</b>, HTML version\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + ", " +
             "               \"isInline\" : false }" +
@@ -4160,7 +4168,7 @@ public abstract class SetMessagesMethodTest {
 
         String firstMessage = ARGUMENTS + ".list[0]";
         String firstAttachment = firstMessage + ".attachments[0]";
-        given()
+        String blobId = given()
             .header("Authorization", accessToken.serialize())
             .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + 
"\"]}, \"#0\"]]")
         .when()
@@ -4173,20 +4181,25 @@ public abstract class SetMessagesMethodTest {
             .body(firstMessage + ".textBody", equalTo("Test body, plain text 
version"))
             .body(firstMessage + ".htmlBody", equalTo("Test <b>body</b>, HTML 
version"))
             .body(firstMessage + ".attachments", hasSize(1))
-            .body(firstAttachment + ".blobId", 
equalTo(attachment.getAttachmentId().getId()))
             .body(firstAttachment + ".type", equalTo("text/html"))
-            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()));
+            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
+            .extract()
+            .jsonPath()
+            .getString(firstAttachment + ".blobId");
+
+        checkBlobContent(blobId, rawBytes);
     }
 
     @Test
     public void 
attachmentsAndBodyShouldBeRetrievedWhenChainingSetMessagesAndGetMessagesWithTextBodyAndHtmlAttachment()
 throws Exception {
+        byte[] rawBytes = ("<html>\n" +
+            "  <body>attachment</body>\n" + // needed indentation, else 
restassured is adding some
+            "</html>").getBytes(StandardCharsets.UTF_8);
         Attachment attachment = Attachment.builder()
-            .bytes(("<html>\n" +
-                    "  <body>attachment</body>\n" + // needed indentation, 
else restassured is adding some
-                    "</html>").getBytes(StandardCharsets.UTF_8))
+            .bytes(rawBytes)
             .type("text/html; charset=UTF-8")
             .build();
-        uploadTextAttachment(attachment);
+        String uploadedBlobId = uploadTextAttachment(attachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -4202,7 +4215,7 @@ public abstract class SetMessagesMethodTest {
             "        \"textBody\": \"Test body, plain text version\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + ", " +
             "               \"isInline\" : false }" +
@@ -4233,7 +4246,7 @@ public abstract class SetMessagesMethodTest {
 
         String firstMessage = ARGUMENTS + ".list[0]";
         String firstAttachment = firstMessage + ".attachments[0]";
-        given()
+        String blobId = given()
             .header("Authorization", accessToken.serialize())
             .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + 
"\"]}, \"#0\"]]")
         .when()
@@ -4246,18 +4259,35 @@ public abstract class SetMessagesMethodTest {
             .body(firstMessage + ".textBody", equalTo("Test body, plain text 
version"))
             .body(firstMessage + ".htmlBody", isEmptyOrNullString())
             .body(firstMessage + ".attachments", hasSize(1))
-            .body(firstAttachment + ".blobId", 
equalTo(attachment.getAttachmentId().getId()))
             .body(firstAttachment + ".type", equalTo("text/html"))
-            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()));
+            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
+            .extract()
+            .jsonPath()
+            .getString(firstAttachment + ".blobId");
+
+        checkBlobContent(blobId, rawBytes);
     }
-    
+
+    public void checkBlobContent(String blobId, byte[] rawBytes) {
+        byte[] attachmentBytes = with()
+            .header("Authorization", accessToken.serialize())
+            .get("/download/" + blobId)
+        .then()
+            .extract()
+            .body()
+            .asByteArray();
+
+        assertThat(attachmentBytes).containsExactly(rawBytes);
+    }
+
     @Test
     public void 
attachmentAndEmptyBodyShouldBeRetrievedWhenChainingSetMessagesAndGetMessagesWithTextAttachmentWithoutMailBody()
 throws Exception {
+        byte[] rawBytes = ("some text").getBytes(StandardCharsets.UTF_8);
         Attachment attachment = Attachment.builder()
-            .bytes(("some text").getBytes(StandardCharsets.UTF_8))
+            .bytes(rawBytes)
             .type("text/plain; charset=UTF-8")
             .build();
-        uploadTextAttachment(attachment);
+        String uploadedBlobId = uploadTextAttachment(attachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -4272,7 +4302,7 @@ public abstract class SetMessagesMethodTest {
             "        \"subject\": \"Message with an attachment\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + ", " +
             "               \"isInline\" : false }" +
@@ -4303,7 +4333,7 @@ public abstract class SetMessagesMethodTest {
 
         String firstMessage = ARGUMENTS + ".list[0]";
         String firstAttachment = firstMessage + ".attachments[0]";
-        given()
+        String blobId = given()
             .header("Authorization", accessToken.serialize())
             .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + 
"\"]}, \"#0\"]]")
         .when()
@@ -4316,9 +4346,13 @@ public abstract class SetMessagesMethodTest {
             .body(firstMessage + ".textBody", isEmptyOrNullString())
             .body(firstMessage + ".htmlBody", isEmptyOrNullString())
             .body(firstMessage + ".attachments", hasSize(1))
-            .body(firstAttachment + ".blobId", 
equalTo(attachment.getAttachmentId().getId()))
             .body(firstAttachment + ".type", equalTo("text/plain"))
-            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()));
+            .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
+            .extract()
+            .jsonPath()
+            .getString(firstAttachment + ".blobId");
+
+        checkBlobContent(blobId, rawBytes);
     }
     
     @Test
@@ -5013,7 +5047,7 @@ public abstract class SetMessagesMethodTest {
                 
.bytes(ClassLoaderUtils.getSystemResourceAsByteArray("attachment/nonIndexableAttachment.html"))
                 .type("text/html")
                 .build();
-        uploadTextAttachment(nonIndexableAttachment);
+        String uploadedBlobId = uploadTextAttachment(nonIndexableAttachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -5029,7 +5063,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"textBody\": \"Test body\"," +
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
-                "               {\"blobId\" : \"" + 
nonIndexableAttachment.getAttachmentId().getId() + "\", " +
+                "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
                 "               \"type\" : \"" + 
nonIndexableAttachment.getType() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + 
nonIndexableAttachment.getSize() + "}" +
@@ -5054,7 +5088,7 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
             .body(ARGUMENTS + ".created", aMapWithSize(1))
             .body(createdPath + ".attachments", hasSize(1))
-            .body(singleAttachment + ".blobId", 
equalTo(nonIndexableAttachment.getAttachmentId().getId()))
+            .body(singleAttachment + ".blobId", equalTo(uploadedBlobId))
             .body(singleAttachment + ".type", equalTo("text/html; 
charset=UTF-8"))
             .body(singleAttachment + ".size", equalTo((int) 
nonIndexableAttachment.getSize()));
     }
@@ -5065,7 +5099,7 @@ public abstract class SetMessagesMethodTest {
                 
.bytes(ClassLoaderUtils.getSystemResourceAsByteArray("attachment/nonIndexableAttachment.html"))
                 .type("text/html")
                 .build();
-        uploadTextAttachment(nonIndexableAttachment);
+        String uploadedBlobId = uploadTextAttachment(nonIndexableAttachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -5081,7 +5115,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"textBody\": \"Test body\"," +
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
-                "               {\"blobId\" : \"" + 
nonIndexableAttachment.getAttachmentId().getId() + "\", " +
+                "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
                 "               \"type\" : \"" + 
nonIndexableAttachment.getType() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + 
nonIndexableAttachment.getSize() + "}" +
@@ -5125,7 +5159,7 @@ public abstract class SetMessagesMethodTest {
                 
.bytes(ClassLoaderUtils.getSystemResourceAsByteArray("attachment/nonIndexableAttachment.html"))
                 .type("text/html")
                 .build();
-        uploadTextAttachment(nonIndexableAttachment);
+        String uploadedBlobId = uploadTextAttachment(nonIndexableAttachment);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -5142,7 +5176,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"textBody\": \"Test body\"," +
                 "        \"mailboxIds\": [\"" + outboxId + "\"], " +
                 "        \"attachments\": [" +
-                "               {\"blobId\" : \"" + 
nonIndexableAttachment.getAttachmentId().getId() + "\", " +
+                "               {\"blobId\" : \"" + uploadedBlobId + "\", " +
                 "               \"type\" : \"" + 
nonIndexableAttachment.getType() + "\", " +
                 "               \"name\" : \"nonIndexableAttachment.html\", " +
                 "               \"size\" : " + 
nonIndexableAttachment.getSize() + "}" +
@@ -5181,12 +5215,12 @@ public abstract class SetMessagesMethodTest {
             .bytes("attachment".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment);
+        String uploadedAttachment1 = uploadAttachment(attachment);
         Attachment attachment2 = Attachment.builder()
             .bytes("attachment2".getBytes(StandardCharsets.UTF_8))
             .type("application/octet-stream")
             .build();
-        uploadAttachment(attachment2);
+        String uploadedAttachment2 = uploadAttachment(attachment2);
 
         String messageCreationId = "creationId";
         String fromAddress = USERNAME;
@@ -5202,10 +5236,10 @@ public abstract class SetMessagesMethodTest {
             "        \"textBody\": \"Test body\"," +
             "        \"mailboxIds\": [\"" + outboxId + "\"], " +
             "        \"attachments\": [" +
-            "               {\"blobId\" : \"" + 
attachment.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment1 + "\", " +
             "               \"type\" : \"" + attachment.getType() + "\", " +
             "               \"size\" : " + attachment.getSize() + "}," +
-            "               {\"blobId\" : \"" + 
attachment2.getAttachmentId().getId() + "\", " +
+            "               {\"blobId\" : \"" + uploadedAttachment2 + "\", " +
             "               \"type\" : \"" + attachment2.getType() + "\", " +
             "               \"size\" : " + attachment2.getSize() + ", " +
             "               \"isInline\" : true }" +
@@ -5231,12 +5265,12 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
             .body(ARGUMENTS + ".created", aMapWithSize(1))
             .body(createdPath + ".attachments", hasSize(2))
-            .body(firstAttachment + ".blobId", 
equalTo(attachment.getAttachmentId().getId()))
+            .body(firstAttachment + ".blobId", equalTo(uploadedAttachment1))
             .body(firstAttachment + ".type", 
equalTo("application/octet-stream; charset=UTF-8"))
             .body(firstAttachment + ".size", equalTo((int) 
attachment.getSize()))
             .body(firstAttachment + ".cid", nullValue())
             .body(firstAttachment + ".isInline", equalTo(false))
-            .body(secondAttachment + ".blobId", 
equalTo(attachment2.getAttachmentId().getId()))
+            .body(secondAttachment + ".blobId", equalTo(uploadedAttachment2))
             .body(secondAttachment + ".type", 
equalTo("application/octet-stream; charset=UTF-8"))
             .body(secondAttachment + ".size", equalTo((int) 
attachment2.getSize()))
             .body(secondAttachment + ".cid", nullValue())

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
index 1ff185b..2abf00b 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/DownloadStepdefs.java
@@ -41,6 +41,8 @@ import org.apache.http.client.utils.URIBuilder;
 import org.apache.james.jmap.api.access.AccessToken;
 import org.apache.james.jmap.model.AttachmentAccessToken;
 import org.apache.james.mailbox.MessageManager.AppendCommand;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.AttachmentId;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
@@ -107,31 +109,37 @@ public class DownloadStepdefs {
         ComposedMessageId composedMessageId = 
mainStepdefs.mailboxProbe.appendMessage(user, mailboxPath,
             
AppendCommand.from(ClassLoader.getSystemResourceAsStream("eml/oneAttachment.eml")));
 
-        inputToMessageId.put(messageId, composedMessageId.getMessageId());
-        attachmentsByMessageId.put(messageId, attachmentId);
-        blobIdByAttachmentId.put(attachmentId, 
ONE_ATTACHMENT_EML_ATTACHMENT_BLOB_ID);
+        retrieveAndSaveAttachmentDetails(user, messageId, attachmentId, 
composedMessageId);
     }
 
     @Given("^\"([^\"]*)\" mailbox \"([^\"]*)\" contains a message \"([^\"]*)\" 
with an inlined attachment \"([^\"]*)\"$")
     public void appendMessageWithInlinedAttachmentToMailbox(String user, 
String mailbox, String messageId, String attachmentId) throws Throwable {
         MailboxPath mailboxPath = MailboxPath.forUser(user, mailbox);
 
-        mainStepdefs.mailboxProbe.appendMessage(user, mailboxPath,
-                
AppendCommand.from(ClassLoader.getSystemResourceAsStream("eml/oneInlinedImage.eml")));
-        
+        ComposedMessageId composedMessageId = 
mainStepdefs.mailboxProbe.appendMessage(user, mailboxPath,
+            
AppendCommand.from(ClassLoader.getSystemResourceAsStream("eml/oneInlinedImage.eml")));
+
+        retrieveAndSaveAttachmentDetails(user, messageId, attachmentId, 
composedMessageId);
+    }
+
+    public void retrieveAndSaveAttachmentDetails(String user, String 
messageId, String attachmentId, ComposedMessageId composedMessageId) throws 
MailboxException {
+        AttachmentId mailboxAttachmentId = mainStepdefs.messageIdProbe
+            .retrieveAttachmentIds(composedMessageId.getMessageId(), user)
+            .get(0);
+
+        inputToMessageId.put(messageId, composedMessageId.getMessageId());
         attachmentsByMessageId.put(messageId, attachmentId);
+        blobIdByAttachmentId.put(attachmentId, mailboxAttachmentId.getId());
     }
 
     @Given("^\"([^\"]*)\" mailbox \"([^\"]*)\" contains a message \"([^\"]*)\" 
with multiple same inlined attachments \"([^\"]*)\"$")
     public void appendMessageWithSameInlinedAttachmentsToMailbox(String user, 
String mailbox, String messageName, String attachmentId) throws Throwable {
         MailboxPath mailboxPath = MailboxPath.forUser(user, mailbox);
 
-        mainStepdefs.mailboxProbe.appendMessage(user, mailboxPath,
+        ComposedMessageId composedMessageId = 
mainStepdefs.mailboxProbe.appendMessage(user, mailboxPath,
             
AppendCommand.from(ClassLoader.getSystemResourceAsStream("eml/sameInlinedImages.eml")));
 
-        attachmentsByMessageId.put(messageName, attachmentId);
-
-        blobIdByAttachmentId.put(attachmentId, 
ONE_ATTACHMENT_EML_ATTACHMENT_BLOB_ID);
+        retrieveAndSaveAttachmentDetails(user, messageName, attachmentId, 
composedMessageId);
     }
 
     @When("^\"([^\"]*)\" checks for the availability of the attachment 
endpoint$")

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UploadStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UploadStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UploadStepdefs.java
index d4362cc..4e3171c 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UploadStepdefs.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/UploadStepdefs.java
@@ -54,7 +54,6 @@ import cucumber.runtime.java.guice.ScenarioScoped;
 
 @ScenarioScoped
 public class UploadStepdefs {
-    private static final String _1M_ZEROED_FILE_BLOB_ID = 
"35d87cc65060b896a0541713e7868f5cb5f8be3f563ccf82b72e61c2fee67404";
     private static final int _1M = 1024 * 1024;
     private static final int _10M = 10 * _1M;
 
@@ -193,7 +192,7 @@ public class UploadStepdefs {
             .containsExactly(
                 
normalizeContentType(org.apache.http.entity.ContentType.APPLICATION_JSON.toString()));
         DocumentContext jsonPath = 
JsonPath.parse(response.getEntity().getContent());
-        
assertThat(jsonPath.<String>read("blobId")).isEqualTo(_1M_ZEROED_FILE_BLOB_ID);
+        jsonPath.<String>read("blobId");
         
assertThat(jsonPath.<String>read("type")).isEqualTo("application/octet-stream");
         assertThat(jsonPath.<Integer>read("size")).isEqualTo(_1M);
     }
@@ -205,7 +204,8 @@ public class UploadStepdefs {
     @Then("^\"([^\"]*)\" should be able to retrieve the content$")
     public void contentShouldBeRetrievable(String username) throws Exception {
         AccessToken accessToken = userStepdefs.authenticate(username);
-        Request request = 
Request.Get(baseUri(mainStepdefs.jmapServer).setPath("/download/" + 
_1M_ZEROED_FILE_BLOB_ID).build());
+        DocumentContext jsonPath = 
JsonPath.parse(response.getEntity().getContent());
+        Request request = 
Request.Get(baseUri(mainStepdefs.jmapServer).setPath("/download/" + 
jsonPath.<String>read("blobId")).build());
         if (accessToken != null) {
             request.addHeader("Authorization", accessToken.serialize());
         }

http://git-wip-us.apache.org/repos/asf/james-project/blob/bfa5e426/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/GetMessages.feature
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/GetMessages.feature
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/GetMessages.feature
index cb4c951..7b5f854 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/GetMessages.feature
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/GetMessages.feature
@@ -164,14 +164,12 @@ Feature: GetMessages method
     And the list of attachments of the message contains 2 attachments
     And the first attachment is:
       |key      | value                                                        
     |
-      |blobId   
|"81dad497ef270bd4537f5b43906aa88ad2e7168744c572be9a7414707727bf58" |
       |type     |"image/jpeg"                                                  
     |
       |size     |846                                                           
     |
       |cid      |null                                                          
     |
       |isInline |false                                                         
     |
     And the second attachment is:
       |key      | value                                                        
     |
-      |blobId   
|"632b5341bbe044d26e0916b82a689282cc0891b806884b4d5a2339ea90b28e85" |
       |type     |"image/jpeg"                                                  
     |
       |size     |597                                                           
     |
       |cid      |"part1.37a15c92.a7c34...@linagora.com"                        
     |


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

Reply via email to