You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2017/09/13 10:57:44 UTC

[18/24] james-project git commit: MAILBOX-305 Introduce the new table attachmentId -> messageIds

MAILBOX-305 Introduce the new table attachmentId -> messageIds


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

Branch: refs/heads/master
Commit: 1b4d92391c8e566c81297ae98ba4c75ae3e32408
Parents: 035f0f7
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Mon Sep 11 13:01:07 2017 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Wed Sep 13 10:21:57 2017 +0200

----------------------------------------------------------------------
 .../apache/james/mailbox/AttachmentManager.java |   5 +-
 .../CassandraMailboxSessionMapperFactory.java   |   7 +-
 .../cassandra/CassandraMessageManager.java      |   5 +-
 .../mail/CassandraAttachmentMapper.java         |  29 +++-
 .../mail/CassandraAttachmentMessageIdDAO.java   | 101 ++++++++++++++
 .../modules/CassandraAttachmentModule.java      |  14 +-
 .../CassandraAttachmentMessageIdTable.java      |  30 +++++
 .../CassandraSubscriptionManagerTest.java       |   3 +
 ...estCassandraMailboxSessionMapperFactory.java |   5 +-
 .../mail/CassandraAttachmentFallbackTest.java   |   5 +-
 .../mail/CassandraAttachmentMapperTest.java     |   7 +
 .../inmemory/InMemoryMessageManager.java        |   5 +-
 .../inmemory/mail/InMemoryAttachmentMapper.java |  14 +-
 .../mail/MemoryAttachmentMapperTest.java        |   7 +
 .../mailbox/store/StoreAttachmentManager.java   |  10 +-
 .../mailbox/store/mail/AttachmentMapper.java    |   5 +-
 .../store/mail/model/AttachmentMapperTest.java  | 134 ++++++++++++++++++-
 17 files changed, 363 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
index e9f2e0f..a03bb53 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/AttachmentManager.java
@@ -26,6 +26,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 
 public interface AttachmentManager {
 
@@ -35,5 +36,7 @@ public interface AttachmentManager {
 
     void storeAttachment(Attachment attachment, MailboxSession mailboxSession) throws MailboxException;
 
-    void storeAttachments(Collection<Attachment> attachments, MailboxSession mailboxSession) throws MailboxException;
+    void storeAttachmentsForMessage(Collection<Attachment> attachments, MessageId ownerMessageId, MailboxSession mailboxSession) throws MailboxException;
+
+    Collection<MessageId> getOwnerMessageIds(AttachmentId attachmentId, MailboxSession mailboxSession) throws MailboxException;
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index 85e3349..9d4e059 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -29,6 +29,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraApplicableFlagDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAOV2;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraBlobsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
@@ -82,6 +83,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     private final CassandraAttachmentDAOV2 attachmentDAOV2;
     private final CassandraDeletedMessageDAO deletedMessageDAO;
     private final CassandraBlobsDAO blobsDAO;
+    private final CassandraAttachmentMessageIdDAO attachementMessageIdDAO;
     private CassandraUtils cassandraUtils;
     private CassandraConfiguration cassandraConfiguration;
 
@@ -92,7 +94,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
                                                 CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, CassandraMailboxDAO mailboxDAO,
                                                 CassandraMailboxPathDAO mailboxPathDAO, CassandraFirstUnseenDAO firstUnseenDAO, CassandraApplicableFlagDAO applicableFlagDAO,
                                                 CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2, CassandraDeletedMessageDAO deletedMessageDAO,
-                                                CassandraBlobsDAO blobsDAO, CassandraUtils cassandraUtils, CassandraConfiguration cassandraConfiguration) {
+                                                CassandraBlobsDAO blobsDAO, CassandraAttachmentMessageIdDAO attachementMessageIdDAO, CassandraUtils cassandraUtils, CassandraConfiguration cassandraConfiguration) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.session = session;
@@ -109,6 +111,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
         this.deletedMessageDAO = deletedMessageDAO;
         this.applicableFlagDAO = applicableFlagDAO;
         this.blobsDAO = blobsDAO;
+        this.attachementMessageIdDAO = attachementMessageIdDAO;
         this.cassandraUtils = cassandraUtils;
         this.cassandraConfiguration = cassandraConfiguration;
         this.indexTableHandler = new CassandraIndexTableHandler(
@@ -153,7 +156,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
 
     @Override
     public CassandraAttachmentMapper createAttachmentMapper(MailboxSession mailboxSession) {
-        return new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2, blobsDAO);
+        return new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2, blobsDAO, attachementMessageIdDAO);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java
index 0a8890d..523cad8 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java
@@ -74,9 +74,10 @@ public class CassandraMessageManager extends StoreMessageManager {
     @Override
     protected void storeAttachment(final MailboxMessage message, final List<MessageAttachment> messageAttachments, final MailboxSession session) throws MailboxException {
         mapperFactory.getAttachmentMapper(session)
-            .storeAttachments(
+            .storeAttachmentsForMessage(
                 messageAttachments.stream()
                     .map(MessageAttachment::getAttachment)
-                    .collect(Guavate.toImmutableList()));
+                    .collect(Guavate.toImmutableList()),
+                message.getMessageId());
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
index 8388c20..37edb52 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.AttachmentMapper;
 import org.apache.james.util.FluentFutureStream;
 import org.jetbrains.annotations.Nullable;
@@ -49,12 +50,14 @@ public class CassandraAttachmentMapper implements AttachmentMapper {
     private final CassandraAttachmentDAO attachmentDAO;
     private final CassandraAttachmentDAOV2 attachmentDAOV2;
     private final CassandraBlobsDAO blobsDAO;
+    private final CassandraAttachmentMessageIdDAO attachmentMessageIdDAO;
 
     @Inject
-    public CassandraAttachmentMapper(CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2, CassandraBlobsDAO blobsDAO) {
+    public CassandraAttachmentMapper(CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2, CassandraBlobsDAO blobsDAO, CassandraAttachmentMessageIdDAO attachmentMessageIdDAO) {
         this.attachmentDAO = attachmentDAO;
         this.attachmentDAOV2 = attachmentDAOV2;
         this.blobsDAO = blobsDAO;
+        this.attachmentMessageIdDAO = attachmentMessageIdDAO;
     }
 
     @Override
@@ -118,21 +121,35 @@ public class CassandraAttachmentMapper implements AttachmentMapper {
 
     @Override
     public void storeAttachment(Attachment attachment) throws MailboxException {
-        storeAttachmentAsync(attachment).join();
+        blobsDAO.save(attachment.getBytes())
+            .thenApply(blobId -> CassandraAttachmentDAOV2.from(attachment, blobId))
+            .thenCompose(attachmentDAOV2::storeAttachment)
+            .join();
     }
 
     @Override
-    public void storeAttachments(Collection<Attachment> attachments) throws MailboxException {
+    public void storeAttachmentsForMessage(Collection<Attachment> attachments, MessageId ownerMessageId) throws MailboxException {
         FluentFutureStream.of(
             attachments.stream()
-                .map(this::storeAttachmentAsync))
+                .map(attachment -> storeAttachmentAsync(attachment, ownerMessageId)))
+            .join();
+    }
+
+    @Override
+    public Collection<MessageId> getOwnerMessageIds(AttachmentId attachmentId) throws MailboxException {
+        return attachmentMessageIdDAO.getOwnerMessageIds(attachmentId)
             .join();
     }
 
-    public CompletableFuture<Void> storeAttachmentAsync(Attachment attachment) {
+    public CompletableFuture<Void> storeAttachmentAsync(Attachment attachment, MessageId ownerMessageId) {
         return blobsDAO.save(attachment.getBytes())
             .thenApply(blobId -> CassandraAttachmentDAOV2.from(attachment, blobId))
-            .thenCompose(attachmentDAOV2::storeAttachment);
+            .thenCompose(daoAttachment -> storeAttachmentWithIndex(daoAttachment, ownerMessageId));
+    }
+
+    private CompletableFuture<Void> storeAttachmentWithIndex(DAOAttachment daoAttachment, MessageId ownerMessageId) {
+        return attachmentDAOV2.storeAttachment(daoAttachment)
+                .thenCompose(any -> attachmentMessageIdDAO.storeAttachmentForMessageId(daoAttachment.getAttachmentId(), ownerMessageId));
     }
 
     private Optional<Attachment> logNotFound(AttachmentId attachmentId, Optional<Attachment> optionalAttachment) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMessageIdDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMessageIdDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMessageIdDAO.java
new file mode 100644
index 0000000..88b3d4d
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMessageIdDAO.java
@@ -0,0 +1,101 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable.ATTACHMENT_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable.ATTACHMENT_ID_AS_UUID;
+import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable.FIELDS;
+import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable.MESSAGE_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable.TABLE_NAME;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+
+public class CassandraAttachmentMessageIdDAO {
+
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement insertStatement;
+    private final PreparedStatement selectStatement;
+    private final MessageId.Factory messageIdFactory;
+    private final CassandraUtils cassandraUtils;
+
+    @Inject
+    public CassandraAttachmentMessageIdDAO(Session session, MessageId.Factory messageIdFactory, CassandraUtils cassandraUtils) {
+        this.messageIdFactory = messageIdFactory;
+        this.cassandraUtils = cassandraUtils;
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+
+        this.selectStatement = prepareSelect(session);
+        this.insertStatement = prepareInsert(session);
+    }
+
+    private PreparedStatement prepareInsert(Session session) {
+        return session.prepare(
+            insertInto(TABLE_NAME)
+                .value(ATTACHMENT_ID_AS_UUID, bindMarker(ATTACHMENT_ID_AS_UUID))
+                .value(ATTACHMENT_ID, bindMarker(ATTACHMENT_ID))
+                .value(MESSAGE_ID, bindMarker(MESSAGE_ID)));
+    }
+
+    private PreparedStatement prepareSelect(Session session) {
+        return session.prepare(select(FIELDS)
+            .from(TABLE_NAME)
+            .where(eq(ATTACHMENT_ID_AS_UUID, bindMarker(ATTACHMENT_ID_AS_UUID))));
+    }
+
+    public CompletableFuture<Collection<MessageId>> getOwnerMessageIds(AttachmentId attachmentId) {
+        Preconditions.checkArgument(attachmentId != null);
+        return cassandraAsyncExecutor.execute(
+            selectStatement.bind()
+                .setUUID(ATTACHMENT_ID_AS_UUID, attachmentId.asUUID()))
+            .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
+                .map(this::rowToMessageId)
+                .collect(Guavate.toImmutableSet()));
+    }
+
+    private MessageId rowToMessageId(Row row) {
+        return messageIdFactory.fromString(row.getString(MESSAGE_ID));
+    }
+
+    public CompletableFuture<Void> storeAttachmentForMessageId(AttachmentId attachmentId, MessageId ownerMessageId) {
+        return cassandraAsyncExecutor.executeVoid(
+            insertStatement.bind()
+                .setUUID(ATTACHMENT_ID_AS_UUID, attachmentId.asUUID())
+                .setString(ATTACHMENT_ID, attachmentId.getId())
+                .setString(MESSAGE_ID, ownerMessageId.serialize()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java
index 50bee8b..dd2bc46 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java
@@ -30,6 +30,7 @@ import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.components.CassandraTable;
 import org.apache.james.backends.cassandra.components.CassandraType;
 import org.apache.james.backends.cassandra.utils.CassandraConstants;
+import org.apache.james.mailbox.cassandra.table.CassandraAttachmentMessageIdTable;
 import org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable;
 import org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table;
 
@@ -65,7 +66,18 @@ public class CassandraAttachmentModule implements CassandraModule {
                     .caching(SchemaBuilder.KeyCaching.ALL,
                         SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION))
                     .comment("Holds attachment for fast attachment retrieval. Content of messages is stored" +
-                        "in `blobs` and `blobparts` tables.")));
+                        "in `blobs` and `blobparts` tables.")),
+            new CassandraTable(CassandraAttachmentMessageIdTable.TABLE_NAME,
+                SchemaBuilder.createTable(CassandraAttachmentMessageIdTable.TABLE_NAME)
+                    .ifNotExists()
+                    .addPartitionKey(CassandraAttachmentMessageIdTable.ATTACHMENT_ID_AS_UUID, uuid())
+                    .addColumn(CassandraAttachmentMessageIdTable.ATTACHMENT_ID, text())
+                    .addClusteringColumn(CassandraAttachmentMessageIdTable.MESSAGE_ID, text())
+                    .withOptions()
+                    .compactionOptions(SchemaBuilder.leveledStrategy())
+                    .caching(SchemaBuilder.KeyCaching.ALL,
+                        SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION))
+                    .comment("Holds ids of messages owning the attachment")));
         types = ImmutableList.of();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentMessageIdTable.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentMessageIdTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentMessageIdTable.java
new file mode 100644
index 0000000..42655fb
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentMessageIdTable.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.table;
+
+public interface CassandraAttachmentMessageIdTable {
+
+    String TABLE_NAME = "attachmentMessageId";
+    String ATTACHMENT_ID_AS_UUID = "attachmentIdAsUUID";
+    String ATTACHMENT_ID = "attachmentId";
+    String MESSAGE_ID = "messageId";
+    String[] FIELDS = { ATTACHMENT_ID_AS_UUID, ATTACHMENT_ID, MESSAGE_ID };
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
index 7e94b3c..53345db 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
@@ -29,6 +29,7 @@ import org.apache.james.mailbox.SubscriptionManager;
 import org.apache.james.mailbox.cassandra.mail.CassandraApplicableFlagDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAOV2;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraBlobsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
@@ -91,6 +92,7 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
         CassandraDeletedMessageDAO deletedMessageDAO = null;
         CassandraAttachmentDAOV2 attachmentDAOV2 = null;
         CassandraBlobsDAO cassandraBlobsDAO = null;
+        CassandraAttachmentMessageIdDAO attachmentMessageIdDAO = null;
         return new CassandraSubscriptionManager(
             new CassandraMailboxSessionMapperFactory(
                 new CassandraUidProvider(cassandra.getConf()),
@@ -109,6 +111,7 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
                 attachmentDAOV2,
                 deletedMessageDAO,
                 cassandraBlobsDAO,
+                attachmentMessageIdDAO,
                 CassandraUtils.WITH_DEFAULT_CONFIGURATION,
                 CassandraConfiguration.DEFAULT_CONFIGURATION));
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
index 312accd..fcd3467 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
@@ -26,6 +26,7 @@ import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.mail.CassandraApplicableFlagDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentDAOV2;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraBlobsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
@@ -61,7 +62,9 @@ public class TestCassandraMailboxSessionMapperFactory {
             new CassandraAttachmentDAO(session, CassandraUtils.WITH_DEFAULT_CONFIGURATION, CassandraConfiguration.DEFAULT_CONFIGURATION),
             new CassandraAttachmentDAOV2(session),
             new CassandraDeletedMessageDAO(session),
-            cassandraBlobsDAO, CassandraUtils.WITH_DEFAULT_CONFIGURATION,
+            cassandraBlobsDAO,
+            new CassandraAttachmentMessageIdDAO(session, factory, CassandraUtils.WITH_DEFAULT_CONFIGURATION),
+            CassandraUtils.WITH_DEFAULT_CONFIGURATION,
             CassandraConfiguration.DEFAULT_CONFIGURATION);
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTest.java
index 7408840..d9ac703 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTest.java
@@ -30,6 +30,7 @@ import org.apache.james.backends.cassandra.init.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.mailbox.cassandra.ids.BlobId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraBlobModule;
 import org.apache.james.mailbox.exception.AttachmentNotFoundException;
@@ -54,6 +55,7 @@ public class CassandraAttachmentFallbackTest {
     private CassandraAttachmentDAO attachmentDAO;
     private CassandraAttachmentMapper attachmentMapper;
     private CassandraBlobsDAO blobsDAO;
+    private CassandraAttachmentMessageIdDAO attachmentMessageIdDAO;
 
     @Before
     public void setUp() throws Exception {
@@ -71,7 +73,8 @@ public class CassandraAttachmentFallbackTest {
             CassandraUtils.WITH_DEFAULT_CONFIGURATION,
             CassandraConfiguration.DEFAULT_CONFIGURATION);
         blobsDAO = new CassandraBlobsDAO(cassandra.getConf());
-        attachmentMapper = new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2, blobsDAO);
+        attachmentMessageIdDAO = new CassandraAttachmentMessageIdDAO(cassandra.getConf(), new CassandraMessageId.Factory(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+        attachmentMapper = new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2, blobsDAO, attachmentMessageIdDAO);
     }
 
     @After

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java
index ed3948d..85acca5 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java
@@ -22,6 +22,7 @@ package org.apache.james.mailbox.cassandra.mail;
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.DockerCassandraRule;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.modules.CassandraAclModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraApplicableFlagsModule;
@@ -36,6 +37,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.model.AttachmentMapperTest;
 import org.apache.james.mailbox.store.mail.model.MapperProvider;
 import org.junit.After;
@@ -77,4 +79,9 @@ public class CassandraAttachmentMapperTest extends AttachmentMapperTest {
     protected MapperProvider createMapperProvider() {
         return new CassandraMapperProvider(cassandra);
     }
+
+    @Override
+    protected MessageId generateMessageId() {
+        return new CassandraMessageId.Factory().generate();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMessageManager.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMessageManager.java
index 7620ddc..6732816 100644
--- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMessageManager.java
+++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMessageManager.java
@@ -46,9 +46,10 @@ public class InMemoryMessageManager extends StoreMessageManager {
     @Override
     protected void storeAttachment(final MailboxMessage message, final List<MessageAttachment> messageAttachments, final MailboxSession session) throws MailboxException {
         mapperFactory.getAttachmentMapper(session)
-            .storeAttachments(
+            .storeAttachmentsForMessage(
                 messageAttachments.stream()
                     .map(MessageAttachment::getAttachment)
-                    .collect(Guavate.toImmutableList()));
+                    .collect(Guavate.toImmutableList()),
+                message.getMessageId());
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
index 3b4f92d..314226b 100644
--- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
+++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAttachmentMapper.java
@@ -27,19 +27,25 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.AttachmentMapper;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
 
 public class InMemoryAttachmentMapper implements AttachmentMapper {
     
     private static final int INITIAL_SIZE = 128;
     private final Map<AttachmentId, Attachment> attachmentsById;
+    private final Multimap<AttachmentId, MessageId> messageIdsByAttachmentId;
 
     public InMemoryAttachmentMapper() {
         attachmentsById = new ConcurrentHashMap<>(INITIAL_SIZE);
+        messageIdsByAttachmentId = Multimaps.synchronizedSetMultimap(HashMultimap.create());
     }
 
     @Override
@@ -79,9 +85,15 @@ public class InMemoryAttachmentMapper implements AttachmentMapper {
     }
 
     @Override
-    public void storeAttachments(Collection<Attachment> attachments) throws MailboxException {
+    public void storeAttachmentsForMessage(Collection<Attachment> attachments, MessageId ownerMessageId) throws MailboxException {
         for (Attachment attachment: attachments) {
             storeAttachment(attachment);
+            messageIdsByAttachmentId.put(attachment.getAttachmentId(), ownerMessageId);
         }
     }
+
+    @Override
+    public Collection<MessageId> getOwnerMessageIds(AttachmentId attachmentId) throws MailboxException {
+        return messageIdsByAttachmentId.get(attachmentId);
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryAttachmentMapperTest.java
----------------------------------------------------------------------
diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryAttachmentMapperTest.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryAttachmentMapperTest.java
index 7b60d83..00e6ae1 100644
--- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryAttachmentMapperTest.java
+++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/MemoryAttachmentMapperTest.java
@@ -20,6 +20,8 @@
 package org.apache.james.mailbox.inmemory.mail;
 
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.inmemory.InMemoryMessageId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.model.AttachmentMapperTest;
 import org.apache.james.mailbox.store.mail.model.MapperProvider;
 import org.junit.Before;
@@ -35,4 +37,9 @@ public class MemoryAttachmentMapperTest extends AttachmentMapperTest {
     protected MapperProvider createMapperProvider() {
         return new InMemoryMapperProvider();
     }
+
+    @Override
+    protected MessageId generateMessageId() {
+        return new InMemoryMessageId.Factory().generate();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
index 88799d9..0d2b8a6 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreAttachmentManager.java
@@ -30,6 +30,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.AttachmentMapper;
 import org.apache.james.mailbox.store.mail.AttachmentMapperFactory;
 
@@ -66,7 +67,12 @@ public class StoreAttachmentManager implements AttachmentManager {
     }
 
     @Override
-    public void storeAttachments(Collection<Attachment> attachments, MailboxSession mailboxSession) throws MailboxException {
-        getAttachmentMapper(mailboxSession).storeAttachments(attachments);
+    public void storeAttachmentsForMessage(Collection<Attachment> attachments, MessageId ownerMessageId, MailboxSession mailboxSession) throws MailboxException {
+        getAttachmentMapper(mailboxSession).storeAttachmentsForMessage(attachments, ownerMessageId);
+    }
+
+    @Override
+    public Collection<MessageId> getOwnerMessageIds(AttachmentId attachmentId, MailboxSession mailboxSession) throws MailboxException {
+        return getAttachmentMapper(mailboxSession).getOwnerMessageIds(attachmentId);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
index 06e84a7..c9c12bb 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AttachmentMapper.java
@@ -25,6 +25,7 @@ import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.transaction.Mapper;
 
 public interface AttachmentMapper extends Mapper {
@@ -35,5 +36,7 @@ public interface AttachmentMapper extends Mapper {
 
     void storeAttachment(Attachment attachment) throws MailboxException;
 
-    void storeAttachments(Collection<Attachment> attachments) throws MailboxException;
+    void storeAttachmentsForMessage(Collection<Attachment> attachments, MessageId ownerMessageId) throws MailboxException;
+
+    Collection<MessageId> getOwnerMessageIds(AttachmentId attachmentId) throws MailboxException;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/1b4d9239/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 a915d36..e56ba13 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
@@ -21,12 +21,14 @@ package org.apache.james.mailbox.store.mail.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.util.Collection;
 import java.util.List;
 
 import org.apache.james.mailbox.exception.AttachmentNotFoundException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.AttachmentMapper;
 import org.junit.Assume;
 import org.junit.Rule;
@@ -37,6 +39,7 @@ import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
 
 public abstract class AttachmentMapperTest {
+    private static final AttachmentId UNKNOWN_ATTACHMENT_ID = AttachmentId.forPayload("unknown".getBytes(Charsets.UTF_8));
 
     private AttachmentMapper attachmentMapper;
     private MapperProvider mapperProvider;
@@ -46,6 +49,8 @@ public abstract class AttachmentMapperTest {
 
     protected abstract MapperProvider createMapperProvider();
 
+    protected abstract MessageId generateMessageId();
+
     public void setUp() throws MailboxException {
         mapperProvider = createMapperProvider();
         Assume.assumeTrue(mapperProvider.getSupportedCapabilities().contains(MapperProvider.Capabilities.ATTACHMENT));
@@ -61,7 +66,7 @@ public abstract class AttachmentMapperTest {
     @Test
     public void getAttachmentShouldThrowWhenNonReferencedAttachmentId() throws Exception {
         expected.expect(AttachmentNotFoundException.class);
-        attachmentMapper.getAttachment(AttachmentId.forPayload("unknown".getBytes(Charsets.UTF_8)));
+        attachmentMapper.getAttachment(UNKNOWN_ATTACHMENT_ID);
     }
 
     @Test
@@ -93,7 +98,7 @@ public abstract class AttachmentMapperTest {
         AttachmentId attachmentId1 = expected1.getAttachmentId();
         AttachmentId attachmentId2 = expected2.getAttachmentId();
         //When
-        attachmentMapper.storeAttachments(ImmutableList.of(expected1, expected2));
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(expected1, expected2), generateMessageId());
         //Then
         Attachment attachment1 = attachmentMapper.getAttachment(attachmentId1);
         Attachment attachment2 = attachmentMapper.getAttachment(attachmentId2);
@@ -109,7 +114,7 @@ public abstract class AttachmentMapperTest {
 
     @Test
     public void getAttachmentsShouldReturnEmptyListWhenNonReferencedAttachmentId() throws Exception {
-        List<Attachment> attachments = attachmentMapper.getAttachments(ImmutableList.of(AttachmentId.forPayload("unknown".getBytes(Charsets.UTF_8))));
+        List<Attachment> attachments = attachmentMapper.getAttachments(ImmutableList.of(UNKNOWN_ATTACHMENT_ID));
 
         assertThat(attachments).isEmpty();
     }
@@ -136,4 +141,127 @@ public abstract class AttachmentMapperTest {
         //Then
         assertThat(attachments).contains(expected, expected2);
     }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnEmptyWhenNone() throws Exception {
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(UNKNOWN_ATTACHMENT_ID);
+
+        assertThat(messageIds).isEmpty();
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnEmptyWhenStoredWithoutMessageId() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        attachmentMapper.storeAttachment(attachment);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        //Then
+        assertThat(messageIds).isEmpty();
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnMessageIdWhenStoredWithMessageId() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        MessageId messageId = generateMessageId();
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        //Then
+        assertThat(messageIds).containsOnly(messageId);
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnTwoMessageIdsWhenStoredTwice() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        MessageId messageId1 = generateMessageId();
+        MessageId messageId2 = generateMessageId();
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId1);
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId2);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        //Then
+        assertThat(messageIds).containsOnly(messageId1, messageId2);
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnOnlyMatchingMessageId() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        Attachment otherAttachment = Attachment.builder()
+                .bytes("something different".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        MessageId messageId1 = generateMessageId();
+        MessageId messageId2 = generateMessageId();
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId1);
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(otherAttachment), messageId2);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        //Then
+        assertThat(messageIds).containsOnly(messageId1);
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnOnlyOneMessageIdWhenStoredTwice() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        MessageId messageId = generateMessageId();
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId);
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment), messageId);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        //Then
+        assertThat(messageIds).containsOnly(messageId);
+    }
+
+    @Test
+    public void getOwnerMessageIdsShouldReturnMessageIdForTwoAttachmentsWhenBothStoredAtTheSameTime() throws Exception {
+        //Given
+        Attachment attachment = Attachment.builder()
+                .bytes("payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        Attachment attachment2 = Attachment.builder()
+                .bytes("other payload".getBytes(Charsets.UTF_8))
+                .type("content")
+                .build();
+        AttachmentId attachmentId = attachment.getAttachmentId();
+        AttachmentId attachmentId2 = attachment2.getAttachmentId();
+        MessageId messageId = generateMessageId();
+        attachmentMapper.storeAttachmentsForMessage(ImmutableList.of(attachment, attachment2), messageId);
+        
+        //When
+        Collection<MessageId> messageIds = attachmentMapper.getOwnerMessageIds(attachmentId);
+        Collection<MessageId> messageIds2 = attachmentMapper.getOwnerMessageIds(attachmentId2);
+        //Then
+        assertThat(messageIds).isEqualTo(messageIds2);
+    }
 }


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