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 bt...@apache.org on 2017/09/19 02:00:19 UTC

[1/6] james-project git commit: MAILBOX-306 Allows to retreive all messageIds with their attachmentIds

Repository: james-project
Updated Branches:
  refs/heads/master 81bf0a3c8 -> b1823d38c


MAILBOX-306 Allows to retreive all messageIds with their attachmentIds


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

Branch: refs/heads/master
Commit: 5e28f4aa0f1d445d9521f798d1e7513e0ce78f8d
Parents: 81bf0a3
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Tue Sep 12 17:57:08 2017 +0200
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:29 2017 +0700

----------------------------------------------------------------------
 .../cassandra/mail/CassandraMessageDAO.java     |  88 ++++++++-
 ...estCassandraMailboxSessionMapperFactory.java |   2 +-
 .../cassandra/mail/CassandraMessageDAOTest.java | 182 +++++++++++++++++--
 3 files changed, 255 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/5e28f4aa/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
index a6165e7..b8a3d84 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
@@ -42,7 +42,9 @@ import static org.apache.james.mailbox.cassandra.table.CassandraMessageV2Table.T
 import java.io.IOException;
 import java.util.Collection;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -56,6 +58,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.backends.cassandra.init.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+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.mail.utils.Limit;
@@ -68,6 +71,7 @@ import org.apache.james.mailbox.model.Cid;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.MessageMapper.FetchType;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
@@ -85,7 +89,10 @@ import com.datastax.driver.core.UDTValue;
 import com.datastax.driver.core.querybuilder.QueryBuilder;
 import com.github.steveash.guavate.Guavate;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.primitives.Bytes;
 
 public class CassandraMessageDAO {
@@ -96,32 +103,41 @@ public class CassandraMessageDAO {
     private final CassandraTypesProvider typesProvider;
     private final CassandraBlobsDAO blobsDAO;
     private final CassandraConfiguration configuration;
+    private final CassandraUtils cassandraUtils;
+    private final CassandraMessageId.Factory messageIdFactory;
     private final PreparedStatement insert;
     private final PreparedStatement delete;
     private final PreparedStatement selectMetadata;
     private final PreparedStatement selectHeaders;
     private final PreparedStatement selectFields;
     private final PreparedStatement selectBody;
+    private final PreparedStatement selectAllMessagesWithAttachment;
     private final Cid.CidParser cidParser;
 
     @Inject
-    public CassandraMessageDAO(Session session, CassandraTypesProvider typesProvider, CassandraBlobsDAO blobsDAO, CassandraConfiguration cassandraConfiguration) {
+    public CassandraMessageDAO(Session session, CassandraTypesProvider typesProvider, CassandraBlobsDAO blobsDAO, CassandraConfiguration cassandraConfiguration,
+            CassandraUtils cassandraUtils, CassandraMessageId.Factory messageIdFactory) {
         this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
         this.typesProvider = typesProvider;
         this.blobsDAO = blobsDAO;
         this.configuration = cassandraConfiguration;
+        this.cassandraUtils = cassandraUtils;
+        this.messageIdFactory = messageIdFactory;
+
         this.insert = prepareInsert(session);
         this.delete = prepareDelete(session);
         this.selectMetadata = prepareSelect(session, METADATA);
         this.selectHeaders = prepareSelect(session, HEADERS);
         this.selectFields = prepareSelect(session, FIELDS);
         this.selectBody = prepareSelect(session, BODY);
+        this.selectAllMessagesWithAttachment = prepareSelectAllMessagesWithAttachment(session);
         this.cidParser = Cid.parser().relaxed();
     }
 
     @VisibleForTesting
-    public CassandraMessageDAO(Session session, CassandraTypesProvider typesProvider, CassandraBlobsDAO blobsDAO) {
-        this(session, typesProvider, blobsDAO, CassandraConfiguration.DEFAULT_CONFIGURATION);
+    public CassandraMessageDAO(Session session, CassandraTypesProvider typesProvider, CassandraBlobsDAO blobsDAO,
+            CassandraUtils cassandraUtils, CassandraMessageId.Factory messageIdFactory) {
+        this(session, typesProvider, blobsDAO, CassandraConfiguration.DEFAULT_CONFIGURATION, cassandraUtils, messageIdFactory);
     }
 
     private PreparedStatement prepareSelect(Session session, String[] fields) {
@@ -130,6 +146,11 @@ public class CassandraMessageDAO {
             .where(eq(MESSAGE_ID, bindMarker(MESSAGE_ID))));
     }
 
+    private PreparedStatement prepareSelectAllMessagesWithAttachment(Session session) {
+        return session.prepare(select(MESSAGE_ID, ATTACHMENTS)
+            .from(TABLE_NAME));
+    }
+
     private PreparedStatement prepareInsert(Session session) {
         return session.prepare(insertInto(TABLE_NAME)
             .value(MESSAGE_ID, bindMarker(MESSAGE_ID))
@@ -381,4 +402,65 @@ public class CassandraMessageDAO {
             return message.get();
         }
     }
+
+    public CompletableFuture<Stream<MessageIdAttachmentIds>> retrieveAllMessageIdAttachmentIds() {
+        return cassandraAsyncExecutor.execute(selectAllMessagesWithAttachment.bind())
+                .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
+                        .map(this::fromRow)
+                        .filter(MessageIdAttachmentIds::hasAttachment));
+    }
+
+    private MessageIdAttachmentIds fromRow(Row row) {
+        MessageId messageId = messageIdFactory.of(row.getUUID(MESSAGE_ID));
+        Set<AttachmentId> attachmentIds = attachmentByIds(row.getList(ATTACHMENTS, UDTValue.class))
+            .map(MessageAttachmentRepresentation::getAttachmentId)
+            .collect(Guavate.toImmutableSet());
+        return new MessageIdAttachmentIds(messageId, attachmentIds);
+    }
+
+    public static class MessageIdAttachmentIds {
+        private final MessageId messageId;
+        private final Set<AttachmentId> attachmentIds;
+        
+        public MessageIdAttachmentIds(MessageId messageId, Set<AttachmentId> attachmentIds) {
+            Preconditions.checkNotNull(messageId);
+            Preconditions.checkNotNull(attachmentIds);
+            this.messageId = messageId;
+            this.attachmentIds = ImmutableSet.copyOf(attachmentIds);
+        }
+        
+        public MessageId getMessageId() {
+            return messageId;
+        }
+        
+        public Set<AttachmentId> getAttachmentId() {
+            return attachmentIds;
+        }
+
+        public boolean hasAttachment() {
+            return ! attachmentIds.isEmpty();
+        }
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof MessageIdAttachmentIds) {
+                MessageIdAttachmentIds other = (MessageIdAttachmentIds) o;
+                return Objects.equals(messageId, other.messageId)
+                    && Objects.equals(attachmentIds, other.attachmentIds);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(messageId, attachmentIds);
+        }
+
+        @Override
+        public String toString() {
+            return MoreObjects.toStringHelper(this)
+                .add("messageId", messageId)
+                .add("attachmentIds", attachmentIds)
+                .toString();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/5e28f4aa/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 fcd3467..6f9d9e2 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
@@ -50,7 +50,7 @@ public class TestCassandraMailboxSessionMapperFactory {
             new CassandraUidProvider(session),
             new CassandraModSeqProvider(session),
             session,
-            new CassandraMessageDAO(session, typesProvider, cassandraBlobsDAO),
+            new CassandraMessageDAO(session, typesProvider, cassandraBlobsDAO, CassandraUtils.WITH_DEFAULT_CONFIGURATION, factory),
             new CassandraMessageIdDAO(session, factory),
             new CassandraMessageIdToImapUidDAO(session, factory),
             new CassandraMailboxCounterDAO(session),

http://git-wip-us.apache.org/repos/asf/james-project/blob/5e28f4aa/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
index ae0f3cf..2ad6928 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
@@ -19,8 +19,11 @@
 package org.apache.james.mailbox.cassandra.mail;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
@@ -34,14 +37,18 @@ import org.apache.commons.lang3.tuple.Pair;
 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.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.cassandra.ids.CassandraId;
 import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO.MessageIdAttachmentIds;
 import org.apache.james.mailbox.cassandra.mail.utils.Limit;
 import org.apache.james.mailbox.cassandra.modules.CassandraBlobModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
+import org.apache.james.mailbox.model.Attachment;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.MessageAttachment;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
@@ -53,25 +60,27 @@ import org.junit.Test;
 
 import com.google.common.base.Charsets;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.primitives.Bytes;
 
+import nl.jqno.equalsverifier.EqualsVerifier;
+
 public class CassandraMessageDAOTest {
     private static final int BODY_START = 16;
     private static final CassandraId MAILBOX_ID = CassandraId.timeBased();
     private static final String CONTENT = "Subject: Test7 \n\nBody7\n.\n";
     private static final MessageUid messageUid = MessageUid.of(1);
+    private static final List<MessageAttachment> NO_ATTACHMENT = ImmutableList.of();
 
     @ClassRule public static DockerCassandraRule cassandraServer = new DockerCassandraRule();
 
     private CassandraCluster cassandra;
 
     private CassandraMessageDAO testee;
-    private CassandraBlobsDAO blobsDAO;
     private CassandraMessageId.Factory messageIdFactory;
 
     private SimpleMailboxMessage message;
     private CassandraMessageId messageId;
-    private ComposedMessageId composedMessageId;
     private List<ComposedMessageIdWithMetaData> messageIds;
 
     @Before
@@ -79,13 +88,11 @@ public class CassandraMessageDAOTest {
         cassandra = CassandraCluster.create(new CassandraModuleComposite(new CassandraMessageModule(), new CassandraBlobModule()), cassandraServer.getIp(), cassandraServer.getBindingPort());
         messageIdFactory = new CassandraMessageId.Factory();
         messageId = messageIdFactory.generate();
-        blobsDAO = new CassandraBlobsDAO(cassandra.getConf());
-        testee = new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), blobsDAO);
-
-        composedMessageId = new ComposedMessageId(MAILBOX_ID, messageId, messageUid);
+        CassandraBlobsDAO blobsDAO = new CassandraBlobsDAO(cassandra.getConf());
+        testee = new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), blobsDAO, CassandraUtils.WITH_DEFAULT_CONFIGURATION, new CassandraMessageId.Factory());
 
         messageIds = ImmutableList.of(ComposedMessageIdWithMetaData.builder()
-                .composedMessageId(composedMessageId)
+                .composedMessageId(new ComposedMessageId(MAILBOX_ID, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(1)
                 .build());
@@ -98,7 +105,7 @@ public class CassandraMessageDAOTest {
 
     @Test
     public void saveShouldSaveNullValueForTextualLineCountAsZero() throws Exception {
-        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder());
+        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
 
         testee.save(message).join();
 
@@ -114,7 +121,7 @@ public class CassandraMessageDAOTest {
         long textualLineCount = 10L;
         PropertyBuilder propertyBuilder = new PropertyBuilder();
         propertyBuilder.setTextualLineCount(textualLineCount);
-        message = createMessage(messageId, CONTENT, BODY_START, propertyBuilder);
+        message = createMessage(messageId, CONTENT, BODY_START, propertyBuilder, NO_ATTACHMENT);
 
         testee.save(message).join();
 
@@ -126,7 +133,7 @@ public class CassandraMessageDAOTest {
 
     @Test
     public void saveShouldStoreMessageWithFullContent() throws Exception {
-        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder());
+        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
 
         testee.save(message).join();
 
@@ -139,7 +146,7 @@ public class CassandraMessageDAOTest {
 
     @Test
     public void saveShouldStoreMessageWithBodyContent() throws Exception {
-        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder());
+        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
 
         testee.save(message).join();
 
@@ -155,7 +162,7 @@ public class CassandraMessageDAOTest {
 
     @Test
     public void saveShouldStoreMessageWithHeaderContent() throws Exception {
-        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder());
+        message = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
 
         testee.save(message).join();
 
@@ -166,7 +173,7 @@ public class CassandraMessageDAOTest {
             .isEqualTo(CONTENT.substring(0, BODY_START));
     }
 
-    private SimpleMailboxMessage createMessage(MessageId messageId, String content, int bodyStart, PropertyBuilder propertyBuilder) {
+    private SimpleMailboxMessage createMessage(MessageId messageId, String content, int bodyStart, PropertyBuilder propertyBuilder, Collection<MessageAttachment> attachments) {
         return SimpleMailboxMessage.builder()
             .messageId(messageId)
             .mailboxId(MAILBOX_ID)
@@ -177,6 +184,7 @@ public class CassandraMessageDAOTest {
             .content(new SharedByteArrayInputStream(content.getBytes(Charsets.UTF_8)))
             .flags(new Flags())
             .propertyBuilder(propertyBuilder)
+            .addAttachments(attachments)
             .build();
     }
 
@@ -188,4 +196,152 @@ public class CassandraMessageDAOTest {
             .orElseThrow(() -> new IllegalStateException("Collection is not supposed to be empty"));
     }
 
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldReturnEmptyWhenNone() {
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        assertThat(actual).isEmpty();
+    }
+
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldReturnOneWhenStored() throws Exception {
+        //Given
+        MessageAttachment attachment = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        SimpleMailboxMessage message1 = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachment));
+        testee.save(message1).join();
+        MessageIdAttachmentIds expected = new MessageIdAttachmentIds(messageId, ImmutableSet.of(attachment.getAttachmentId()));
+        
+        //When
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        //Then
+        assertThat(actual).containsOnly(expected);
+    }
+
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldReturnOneWhenStoredWithTwoAttachments() throws Exception {
+        //Given
+        MessageAttachment attachment1 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        MessageAttachment attachment2 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("other content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        SimpleMailboxMessage message1 = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachment1, attachment2));
+        testee.save(message1).join();
+        MessageIdAttachmentIds expected = new MessageIdAttachmentIds(messageId, ImmutableSet.of(attachment1.getAttachmentId(), attachment2.getAttachmentId()));
+        
+        //When
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        //Then
+        assertThat(actual).containsOnly(expected);
+    }
+    
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldReturnAllWhenStoredWithAttachment() throws Exception {
+        //Given
+        MessageId messageId1 = messageIdFactory.generate();
+        MessageId messageId2 = messageIdFactory.generate();
+        MessageAttachment attachment1 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        MessageAttachment attachment2 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("other content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        SimpleMailboxMessage message1 = createMessage(messageId1, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachment1));
+        SimpleMailboxMessage message2 = createMessage(messageId2, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachment2));
+        testee.save(message1).join();
+        testee.save(message2).join();
+        MessageIdAttachmentIds expected1 = new MessageIdAttachmentIds(messageId1, ImmutableSet.of(attachment1.getAttachmentId()));
+        MessageIdAttachmentIds expected2 = new MessageIdAttachmentIds(messageId2, ImmutableSet.of(attachment2.getAttachmentId()));
+        
+        //When
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        //Then
+        assertThat(actual).containsOnly(expected1, expected2);
+    }
+    
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldReturnEmtpyWhenStoredWithoutAttachment() throws Exception {
+        //Given
+        SimpleMailboxMessage message1 = createMessage(messageId, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
+        testee.save(message1).join();
+        
+        //When
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        //Then
+        assertThat(actual).isEmpty();
+    }
+    
+    @Test
+    public void retrieveAllMessageIdAttachmentIdsShouldFilterMessagesWithoutAttachment() throws Exception {
+        //Given
+        MessageId messageId1 = messageIdFactory.generate();
+        MessageId messageId2 = messageIdFactory.generate();
+        MessageId messageId3 = messageIdFactory.generate();
+        MessageAttachment attachmentFor1 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        MessageAttachment attachmentFor3 = MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("other content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+        SimpleMailboxMessage message1 = createMessage(messageId1, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachmentFor1));
+        SimpleMailboxMessage message2 = createMessage(messageId2, CONTENT, BODY_START, new PropertyBuilder(), NO_ATTACHMENT);
+        SimpleMailboxMessage message3 = createMessage(messageId3, CONTENT, BODY_START, new PropertyBuilder(), ImmutableList.of(attachmentFor3));
+        testee.save(message1).join();
+        testee.save(message2).join();
+        testee.save(message3).join();
+        
+        //When
+        Stream<MessageIdAttachmentIds> actual = testee.retrieveAllMessageIdAttachmentIds().join();
+        
+        //Then
+        assertThat(actual).extracting(MessageIdAttachmentIds::getMessageId)
+            .containsOnly(messageId1, messageId3);
+    }
+
+    @Test
+    public void messageIdAttachmentIdsShouldMatchBeanContract() {
+        EqualsVerifier.forClass(MessageIdAttachmentIds.class)
+            .allFieldsShouldBeUsed()
+            .verify();
+    }
+
+    @Test
+    public void messageIdAttachmentIdsShouldThrowOnNullMessageId() {
+        assertThatThrownBy(() -> new MessageIdAttachmentIds(null, ImmutableSet.of()))
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void messageIdAttachmentIdsShouldThrowOnNullAttachmentIds() {
+        assertThatThrownBy(() -> new MessageIdAttachmentIds(messageIdFactory.generate(), null))
+            .isInstanceOf(NullPointerException.class);
+    }
 }
\ No newline at end of file


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


[3/6] james-project git commit: MAILBOX-306 Binding new migration which move attachment ids from message to messageAttachmentId

Posted by bt...@apache.org.
MAILBOX-306 Binding new migration which move attachment ids from message to messageAttachmentId


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

Branch: refs/heads/master
Commit: 8c82a8560fc86a1bfd947ed4e0dd05e22fc9cefd
Parents: f111e62
Author: quynhn <qn...@linagora.com>
Authored: Wed Sep 13 14:16:08 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:35 2017 +0700

----------------------------------------------------------------------
 .../org/apache/james/modules/server/CassandraRoutesModule.java    | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8c82a856/server/container/guice/protocols/webadmin-cassandra/src/main/java/org/apache/james/modules/server/CassandraRoutesModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/protocols/webadmin-cassandra/src/main/java/org/apache/james/modules/server/CassandraRoutesModule.java b/server/container/guice/protocols/webadmin-cassandra/src/main/java/org/apache/james/modules/server/CassandraRoutesModule.java
index 96f807c..ae2340f 100644
--- a/server/container/guice/protocols/webadmin-cassandra/src/main/java/org/apache/james/modules/server/CassandraRoutesModule.java
+++ b/server/container/guice/protocols/webadmin-cassandra/src/main/java/org/apache/james/modules/server/CassandraRoutesModule.java
@@ -20,6 +20,7 @@
 package org.apache.james.modules.server;
 
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManager;
+import org.apache.james.mailbox.cassandra.mail.migration.AttachmentMessageIdCreation;
 import org.apache.james.mailbox.cassandra.mail.migration.AttachmentV2Migration;
 import org.apache.james.mailbox.cassandra.mail.migration.Migration;
 import org.apache.james.webadmin.Routes;
@@ -35,6 +36,7 @@ import com.google.inject.name.Names;
 public class CassandraRoutesModule extends AbstractModule {
     private static final int FROM_V2_TO_V3 = 2;
     private static final int FROM_V3_TO_V4 = 3;
+    private static final int FROM_V4_TO_V5 = 4;
 
     @Override
     protected void configure() {
@@ -47,6 +49,7 @@ public class CassandraRoutesModule extends AbstractModule {
         MapBinder<Integer, Migration> allMigrationClazzBinder = MapBinder.newMapBinder(binder(), Integer.class, Migration.class);
         allMigrationClazzBinder.addBinding(FROM_V2_TO_V3).toInstance(() -> Migration.MigrationResult.COMPLETED);
         allMigrationClazzBinder.addBinding(FROM_V3_TO_V4).to(AttachmentV2Migration.class);
+        allMigrationClazzBinder.addBinding(FROM_V4_TO_V5).to(AttachmentMessageIdCreation.class);
 
         bindConstant()
             .annotatedWith(Names.named(CassandraMigrationService.LATEST_VERSION))


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


[6/6] james-project git commit: MAILBOX-306 Read timeout setting when retrieving all attachment ids from message

Posted by bt...@apache.org.
MAILBOX-306 Read timeout setting when retrieving all attachment ids from message


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

Branch: refs/heads/master
Commit: f111e6236ec18c92233a7b356d6f9951cf39dc76
Parents: 1e808fa
Author: quynhn <qn...@linagora.com>
Authored: Wed Sep 13 14:14:41 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:35 2017 +0700

----------------------------------------------------------------------
 .../cassandra/init/CassandraConfiguration.java  | 30 +++++++++++++++++---
 .../init/CassandraConfigurationTest.java        | 19 +++++++++++++
 .../cassandra/mail/CassandraMessageDAO.java     | 10 ++++---
 .../modules/mailbox/CassandraSessionModule.java |  3 ++
 .../mailbox/CassandraSessionModuleTest.java     |  1 +
 .../modules/mailbox/cassandra.properties        |  1 +
 src/site/xdoc/server/config-cassandra.xml       | 15 ++++++++++
 7 files changed, 71 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
index 2121cff..f35d3b8 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
@@ -41,6 +41,7 @@ public class CassandraConfiguration {
     public static final int DEFAULT_FETCH_NEXT_PAGE_ADVANCE_IN_ROW = 100;
     public static final int DEFAULT_BLOB_PART_SIZE = 100 * 1024;
     public static final int DEFAULT_ATTACHMENT_V2_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.DAYS.toMillis(1));
+    public static final int DEFAULT_MESSAGE_ATTACHMENT_ID_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.DAYS.toMillis(1));
     public static final CassandraConfiguration DEFAULT_CONFIGURATION = builder().build();
 
     public static class Builder {
@@ -55,6 +56,7 @@ public class CassandraConfiguration {
         private Optional<Integer> fetchNextPageInAdvanceRow = Optional.empty();
         private Optional<Integer> blobPartSize = Optional.empty();
         private Optional<Integer> attachmentV2MigrationReadTimeout = Optional.empty();
+        private Optional<Integer> messageAttachmentIdsReadTimeout = Optional.empty();
 
         public Builder messageReadChunkSize(int value) {
             Preconditions.checkArgument(value > 0, "messageReadChunkSize needs to be strictly positive");
@@ -122,6 +124,12 @@ public class CassandraConfiguration {
             return this;
         }
 
+        public Builder messageAttachmentIdsReadTimeout(int value) {
+            Preconditions.checkArgument(value > 0, "messageAttachmentIdsReadTimeout needs to be strictly positive");
+            this.messageAttachmentIdsReadTimeout = Optional.of(value);
+            return this;
+        }
+
         public Builder messageReadChunkSize(Optional<Integer> value) {
             value.ifPresent(this::messageReadChunkSize);
             return this;
@@ -177,6 +185,11 @@ public class CassandraConfiguration {
             return this;
         }
 
+        public Builder messageAttachmentIdsReadTimeout(Optional<Integer> value) {
+            value.ifPresent(this::messageAttachmentIdsReadTimeout);
+            return this;
+        }
+
         public CassandraConfiguration build() {
             return new CassandraConfiguration(aclMaxRetry.orElse(DEFAULT_ACL_MAX_RETRY),
                 messageReadChunkSize.orElse(DEFAULT_MESSAGE_CHUNK_SIZE_ON_READ),
@@ -188,7 +201,8 @@ public class CassandraConfiguration {
                 uidMaxRetry.orElse(DEFAULT_UID_MAX_RETRY),
                 fetchNextPageInAdvanceRow.orElse(DEFAULT_FETCH_NEXT_PAGE_ADVANCE_IN_ROW),
                 blobPartSize.orElse(DEFAULT_BLOB_PART_SIZE),
-                attachmentV2MigrationReadTimeout.orElse(DEFAULT_ATTACHMENT_V2_MIGRATION_READ_TIMEOUT));
+                attachmentV2MigrationReadTimeout.orElse(DEFAULT_ATTACHMENT_V2_MIGRATION_READ_TIMEOUT),
+                messageAttachmentIdsReadTimeout.orElse(DEFAULT_MESSAGE_ATTACHMENT_ID_MIGRATION_READ_TIMEOUT));
         }
     }
 
@@ -207,12 +221,13 @@ public class CassandraConfiguration {
     private final int fetchNextPageInAdvanceRow;
     private final int blobPartSize;
     private final int attachmentV2MigrationReadTimeout;
+    private final int messageAttachmentIdsReadTimeout;
 
     @VisibleForTesting
     CassandraConfiguration(int aclMaxRetry, int messageReadChunkSize, int expungeChunkSize,
                            int flagsUpdateChunkSize, int flagsUpdateMessageIdMaxRetry, int flagsUpdateMessageMaxRetry,
                            int modSeqMaxRetry, int uidMaxRetry, int fetchNextPageInAdvanceRow,
-                           int blobPartSize, final int attachmentV2MigrationReadTimeout) {
+                           int blobPartSize, final int attachmentV2MigrationReadTimeout, int messageAttachmentIdsReadTimeout) {
         this.aclMaxRetry = aclMaxRetry;
         this.messageReadChunkSize = messageReadChunkSize;
         this.expungeChunkSize = expungeChunkSize;
@@ -224,6 +239,7 @@ public class CassandraConfiguration {
         this.flagsUpdateChunkSize = flagsUpdateChunkSize;
         this.blobPartSize = blobPartSize;
         this.attachmentV2MigrationReadTimeout = attachmentV2MigrationReadTimeout;
+        this.messageAttachmentIdsReadTimeout = messageAttachmentIdsReadTimeout;
     }
 
     public int getBlobPartSize() {
@@ -270,6 +286,10 @@ public class CassandraConfiguration {
         return attachmentV2MigrationReadTimeout;
     }
 
+    public int getMessageAttachmentIdsReadTimeout() {
+        return messageAttachmentIdsReadTimeout;
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof CassandraConfiguration) {
@@ -285,7 +305,8 @@ public class CassandraConfiguration {
                 && Objects.equals(this.flagsUpdateChunkSize, that.flagsUpdateChunkSize)
                 && Objects.equals(this.fetchNextPageInAdvanceRow, that.fetchNextPageInAdvanceRow)
                 && Objects.equals(this.blobPartSize, that.blobPartSize)
-                && Objects.equals(this.attachmentV2MigrationReadTimeout, that.attachmentV2MigrationReadTimeout);
+                && Objects.equals(this.attachmentV2MigrationReadTimeout, that.attachmentV2MigrationReadTimeout)
+                && Objects.equals(this.messageAttachmentIdsReadTimeout, that.messageAttachmentIdsReadTimeout);
         }
         return false;
     }
@@ -294,7 +315,7 @@ public class CassandraConfiguration {
     public final int hashCode() {
         return Objects.hash(aclMaxRetry, messageReadChunkSize, expungeChunkSize, flagsUpdateMessageIdMaxRetry,
             flagsUpdateMessageMaxRetry, modSeqMaxRetry, uidMaxRetry, fetchNextPageInAdvanceRow, flagsUpdateChunkSize,
-            blobPartSize, attachmentV2MigrationReadTimeout);
+            blobPartSize, attachmentV2MigrationReadTimeout, messageAttachmentIdsReadTimeout);
     }
 
     @Override
@@ -311,6 +332,7 @@ public class CassandraConfiguration {
             .add("uidMaxRetry", uidMaxRetry)
             .add("blobPartSize", blobPartSize)
             .add("attachmentV2MigrationReadTimeout", attachmentV2MigrationReadTimeout)
+            .add("messageAttachmentIdsReadTimeout", messageAttachmentIdsReadTimeout)
             .toString();
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
index efde00e..34b6a01 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
@@ -210,6 +210,22 @@ public class CassandraConfigurationTest {
     }
 
     @Test
+    public void messageAttachmentIdsReadTimeoutShouldThrowOnZero() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        CassandraConfiguration.builder()
+            .messageAttachmentIdsReadTimeout(0);
+    }
+
+    @Test
+    public void messageAttachmentIdsReadTimeoutShouldThrowOnNegativeValue() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        CassandraConfiguration.builder()
+            .messageAttachmentIdsReadTimeout(-1);
+    }
+
+    @Test
     public void builderShouldCreateTheRightObject() {
         int aclMaxRetry = 1;
         int modSeqMaxRetry = 2;
@@ -222,6 +238,7 @@ public class CassandraConfigurationTest {
         int expungeChunkSize = 9;
         int blobPartSize = 10;
         int attachmentV2MigrationReadTimeout = 11;
+        int messageAttachmentIdReadTimeout = 12;
 
         CassandraConfiguration configuration = CassandraConfiguration.builder()
             .aclMaxRetry(aclMaxRetry)
@@ -235,6 +252,7 @@ public class CassandraConfigurationTest {
             .expungeChunkSize(expungeChunkSize)
             .blobPartSize(blobPartSize)
             .attachmentV2MigrationReadTimeout(attachmentV2MigrationReadTimeout)
+            .messageAttachmentIdsReadTimeout(messageAttachmentIdReadTimeout)
             .build();
 
         softly.assertThat(configuration.getAclMaxRetry()).isEqualTo(aclMaxRetry);
@@ -248,6 +266,7 @@ public class CassandraConfigurationTest {
         softly.assertThat(configuration.getExpungeChunkSize()).isEqualTo(expungeChunkSize);
         softly.assertThat(configuration.getBlobPartSize()).isEqualTo(blobPartSize);
         softly.assertThat(configuration.getAttachmentV2MigrationReadTimeout()).isEqualTo(attachmentV2MigrationReadTimeout);
+        softly.assertThat(configuration.getMessageAttachmentIdsReadTimeout()).isEqualTo(messageAttachmentIdReadTimeout);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
index b8a3d84..9c18e73 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAO.java
@@ -404,10 +404,12 @@ public class CassandraMessageDAO {
     }
 
     public CompletableFuture<Stream<MessageIdAttachmentIds>> retrieveAllMessageIdAttachmentIds() {
-        return cassandraAsyncExecutor.execute(selectAllMessagesWithAttachment.bind())
-                .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
-                        .map(this::fromRow)
-                        .filter(MessageIdAttachmentIds::hasAttachment));
+        return cassandraAsyncExecutor.execute(
+            selectAllMessagesWithAttachment.bind()
+                .setReadTimeoutMillis(configuration.getMessageAttachmentIdsReadTimeout()))
+            .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
+                .map(this::fromRow)
+                .filter(MessageIdAttachmentIds::hasAttachment));
     }
 
     private MessageIdAttachmentIds fromRow(Row row) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
index fb08814..5453df1 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
@@ -73,6 +73,7 @@ public class CassandraSessionModule extends AbstractModule {
     private static final String CHUNK_SIZE_EXPUNGE = "chunk.size.expunge";
     private static final String BLOB_PART_SIZE = "mailbox.blob.part.size";
     private static final String ATTACHMENT_V2_MIGRATION_READ_TIMEOUT = "attachment.v2.migration.read.timeout";
+    private static final String MESSAGE_ATTACHMENTID_READ_TIMEOUT = "message.attachmentids.read.timeout";
     private static final String CASSANDRA_NODES = "cassandra.nodes";
 
     @Override
@@ -160,6 +161,8 @@ public class CassandraSessionModule extends AbstractModule {
                 propertiesConfiguration.getInteger(BLOB_PART_SIZE, null)))
             .attachmentV2MigrationReadTimeout(Optional.ofNullable(
                 propertiesConfiguration.getInteger(ATTACHMENT_V2_MIGRATION_READ_TIMEOUT, null)))
+            .messageAttachmentIdsReadTimeout(Optional.ofNullable(
+                propertiesConfiguration.getInteger(MESSAGE_ATTACHMENTID_READ_TIMEOUT, null)))
             .build();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/CassandraSessionModuleTest.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/CassandraSessionModuleTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/CassandraSessionModuleTest.java
index da0358e..39bc3ad 100644
--- a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/CassandraSessionModuleTest.java
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/modules/mailbox/CassandraSessionModuleTest.java
@@ -61,6 +61,7 @@ public class CassandraSessionModuleTest {
                 .expungeChunkSize(9)
                 .blobPartSize(10)
                 .attachmentV2MigrationReadTimeout(11)
+                .messageAttachmentIdsReadTimeout(12)
                 .build());
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/server/container/guice/cassandra-guice/src/test/resources/modules/mailbox/cassandra.properties
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/test/resources/modules/mailbox/cassandra.properties b/server/container/guice/cassandra-guice/src/test/resources/modules/mailbox/cassandra.properties
index d41cb75..5a37b4e 100644
--- a/server/container/guice/cassandra-guice/src/test/resources/modules/mailbox/cassandra.properties
+++ b/server/container/guice/cassandra-guice/src/test/resources/modules/mailbox/cassandra.properties
@@ -9,3 +9,4 @@ chunk.size.message.read=8
 chunk.size.expunge=9
 mailbox.blob.part.size=10
 attachment.v2.migration.read.timeout=11
+message.attachmentids.read.timeout=12

http://git-wip-us.apache.org/repos/asf/james-project/blob/f111e623/src/site/xdoc/server/config-cassandra.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/server/config-cassandra.xml b/src/site/xdoc/server/config-cassandra.xml
index c2980c9..3c74beb 100644
--- a/src/site/xdoc/server/config-cassandra.xml
+++ b/src/site/xdoc/server/config-cassandra.xml
@@ -186,6 +186,21 @@
       
     </subsection>
 
+    <subsection name="From V4 to V5">
+
+      <p>Migration tag on git repository: <a href="https://github.com/apache/james-project/releases/tag/cassandra_migration_v4_to_v5">cassandra_migration_v4_to_v5</a></p>
+
+      <p>Goal is to store attachment ids in the separated AttachmentMessageId table.</p>
+
+      <p>Summary of available options for this migration:</p>
+
+      <dl>
+        <dt><strong>message.attachmentids.read.timeout</strong></dt>
+        <dd>Optional. Defaults to one day.<br/> Controls how many milliseconds before the read attachment ids on message time out.</dd>
+      </dl>
+
+    </subsection>
+
   </section>
 
 </body>


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


[5/6] james-project git commit: MAILBOX-306 Lower default timeout for big attachments migrations queries

Posted by bt...@apache.org.
MAILBOX-306 Lower default timeout for big attachments migrations queries


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

Branch: refs/heads/master
Commit: b1823d38cf967b01cf3eee7ec93478e6262ae825
Parents: dacb968
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Mon Sep 18 16:21:53 2017 +0200
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:35 2017 +0700

----------------------------------------------------------------------
 .../james/backends/cassandra/init/CassandraConfiguration.java    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/b1823d38/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
index f35d3b8..10764a5 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/CassandraConfiguration.java
@@ -40,8 +40,8 @@ public class CassandraConfiguration {
     public static final int DEFAULT_ACL_MAX_RETRY = 1000;
     public static final int DEFAULT_FETCH_NEXT_PAGE_ADVANCE_IN_ROW = 100;
     public static final int DEFAULT_BLOB_PART_SIZE = 100 * 1024;
-    public static final int DEFAULT_ATTACHMENT_V2_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.DAYS.toMillis(1));
-    public static final int DEFAULT_MESSAGE_ATTACHMENT_ID_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.DAYS.toMillis(1));
+    public static final int DEFAULT_ATTACHMENT_V2_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.HOURS.toMillis(1));
+    public static final int DEFAULT_MESSAGE_ATTACHMENT_ID_MIGRATION_READ_TIMEOUT = toIntExact(TimeUnit.HOURS.toMillis(1));
     public static final CassandraConfiguration DEFAULT_CONFIGURATION = builder().build();
 
     public static class Builder {


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


[4/6] james-project git commit: MAILBOX-306 Use assertThatThrownBy in CassandraConfigurationTest

Posted by bt...@apache.org.
MAILBOX-306 Use assertThatThrownBy in CassandraConfigurationTest


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

Branch: refs/heads/master
Commit: dacb968e1d20026bb52de0422dad25802dde7f08
Parents: 8c82a85
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Mon Sep 18 15:55:39 2017 +0200
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:35 2017 +0700

----------------------------------------------------------------------
 .../init/CassandraConfigurationTest.java        | 160 ++++++++-----------
 1 file changed, 67 insertions(+), 93 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/dacb968e/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
index 34b6a01..c59fc6c 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/CassandraConfigurationTest.java
@@ -20,19 +20,15 @@
 package org.apache.james.backends.cassandra.init;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.assertj.core.api.JUnitSoftAssertions;
 import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 public class CassandraConfigurationTest {
-
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
     @Rule
     public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
 
@@ -51,178 +47,156 @@ public class CassandraConfigurationTest {
 
     @Test
     public void aclMaxRetryShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .aclMaxRetry(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .aclMaxRetry(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void aclMaxRetryShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .aclMaxRetry(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+            .aclMaxRetry(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void expungeChunkSizeShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .expungeChunkSize(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .expungeChunkSize(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void expungeChunkSizeShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .expungeChunkSize(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .expungeChunkSize(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void messageReadChunkSizeShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .messageReadChunkSize(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .messageReadChunkSize(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void messageReadChunkSizeShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .messageReadChunkSize(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .messageReadChunkSize(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateChunkSizeShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateChunkSize(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateChunkSize(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateChunkSizeShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateChunkSize(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateChunkSize(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateMessageIdMaxRetryShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateMessageIdMaxRetry(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateMessageIdMaxRetry(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateMessageIdMaxRetryShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateMessageIdMaxRetry(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateMessageIdMaxRetry(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateMessageMaxRetryShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateMessageMaxRetry(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateMessageMaxRetry(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void flagsUpdateMessageMaxRetryShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .flagsUpdateMessageMaxRetry(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .flagsUpdateMessageMaxRetry(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void fetchNextPageInAdvanceRowShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .fetchNextPageInAdvanceRow(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .fetchNextPageInAdvanceRow(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void fetchNextPageInAdvanceRowShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .fetchNextPageInAdvanceRow(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .fetchNextPageInAdvanceRow(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void modSeqMaxRetryShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .modSeqMaxRetry(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .modSeqMaxRetry(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void modSeqMaxRetryShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .modSeqMaxRetry(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .modSeqMaxRetry(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void uidMaxRetryShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .uidMaxRetry(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .uidMaxRetry(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void uidMaxRetryShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .uidMaxRetry(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .uidMaxRetry(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void attachmentV2MigrationReadTimeoutShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .attachmentV2MigrationReadTimeout(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .attachmentV2MigrationReadTimeout(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void attachmentV2MigrationReadTimeoutShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .attachmentV2MigrationReadTimeout(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .attachmentV2MigrationReadTimeout(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void messageAttachmentIdsReadTimeoutShouldThrowOnZero() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .messageAttachmentIdsReadTimeout(0);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .messageAttachmentIdsReadTimeout(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
     public void messageAttachmentIdsReadTimeoutShouldThrowOnNegativeValue() {
-        expectedException.expect(IllegalArgumentException.class);
-
-        CassandraConfiguration.builder()
-            .messageAttachmentIdsReadTimeout(-1);
+        assertThatThrownBy(() -> CassandraConfiguration.builder()
+                .messageAttachmentIdsReadTimeout(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test


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


[2/6] james-project git commit: MAILBOX-306 New migration which duplicate attachment id from message to AttachmentMessageId table

Posted by bt...@apache.org.
MAILBOX-306 New migration which duplicate attachment id from message to AttachmentMessageId table


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

Branch: refs/heads/master
Commit: 1e808fa54d5452912a94549da8d2c4ab8c442ca3
Parents: 5e28f4a
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Tue Sep 12 18:02:54 2017 +0200
Committer: benwa <bt...@linagora.com>
Committed: Tue Sep 19 08:59:34 2017 +0700

----------------------------------------------------------------------
 .../versions/CassandraSchemaVersionManager.java |   2 +-
 .../migration/AttachmentMessageIdCreation.java  |  66 +++++++
 .../AttachmentMessageIdCreationTest.java        | 197 +++++++++++++++++++
 3 files changed, 264 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/1e808fa5/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionManager.java
----------------------------------------------------------------------
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionManager.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionManager.java
index 663db63..42502e8 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionManager.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/versions/CassandraSchemaVersionManager.java
@@ -34,7 +34,7 @@ import com.google.common.base.Preconditions;
 
 public class CassandraSchemaVersionManager {
     public static final int MIN_VERSION = 2;
-    public static final int MAX_VERSION = 4;
+    public static final int MAX_VERSION = 5;
     public static final int DEFAULT_VERSION = 2;
 
     private static final Logger LOGGER = LoggerFactory.getLogger(CassandraSchemaVersionManager.class);

http://git-wip-us.apache.org/repos/asf/james-project/blob/1e808fa5/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreation.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreation.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreation.java
new file mode 100644
index 0000000..adcc1ff
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreation.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail.migration;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO.MessageIdAttachmentIds;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AttachmentMessageIdCreation implements Migration {
+    private static final Logger LOGGER = LoggerFactory.getLogger(AttachmentMessageIdCreation.class);
+    private final CassandraMessageDAO cassandraMessageDAO;
+    private final CassandraAttachmentMessageIdDAO attachmentMessageIdDAO;
+
+    @Inject
+    public AttachmentMessageIdCreation(CassandraMessageDAO cassandraMessageDAO,
+                                 CassandraAttachmentMessageIdDAO attachmentMessageIdDAO) {
+        this.cassandraMessageDAO = cassandraMessageDAO;
+        this.attachmentMessageIdDAO = attachmentMessageIdDAO;
+    }
+
+    @Override
+    public MigrationResult run() {
+        try {
+            return cassandraMessageDAO.retrieveAllMessageIdAttachmentIds()
+                .join()
+                .map(this::createIndex)
+                .reduce(MigrationResult.COMPLETED, Migration::combine);
+        } catch (Exception e) {
+            LOGGER.error("Error while creation attachmentId -> messageIds index", e);
+            return MigrationResult.PARTIAL;
+        }
+    }
+
+    private MigrationResult createIndex(MessageIdAttachmentIds message) {
+        try {
+            message.getAttachmentId()
+                .stream()
+                .forEach(attachmentId -> attachmentMessageIdDAO.storeAttachmentForMessageId(attachmentId, message.getMessageId()).join());
+            return MigrationResult.COMPLETED;
+        } catch (Exception e) {
+            LOGGER.error("Error while creation attachmentId -> messageIds index", e);
+            return MigrationResult.PARTIAL;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/1e808fa5/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreationTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreationTest.java
new file mode 100644
index 0000000..277741c
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/AttachmentMessageIdCreationTest.java
@@ -0,0 +1,197 @@
+/****************************************************************
+ * 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.migration;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+
+import javax.mail.Flags;
+import javax.mail.util.SharedByteArrayInputStream;
+
+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.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraBlobsDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
+import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraBlobModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
+import org.apache.james.mailbox.model.Attachment;
+import org.apache.james.mailbox.model.AttachmentId;
+import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class AttachmentMessageIdCreationTest {
+    @ClassRule
+    public static DockerCassandraRule cassandraServer = new DockerCassandraRule();
+
+    private CassandraBlobsDAO blobsDAO;
+    private CassandraMessageDAO cassandraMessageDAO;
+    private CassandraAttachmentMessageIdDAO attachmentMessageIdDAO;
+
+    private AttachmentMessageIdCreation migration;
+
+    private SimpleMailboxMessage message;
+    private CassandraMessageId messageId;
+
+    @Before
+    public void setUp() {
+        CassandraCluster cassandra = CassandraCluster.create(
+            new CassandraModuleComposite(
+                new CassandraMessageModule(),
+                new CassandraAttachmentModule(),
+                new CassandraBlobModule()),
+            cassandraServer.getIp(),
+            cassandraServer.getBindingPort());
+        CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory();
+
+        blobsDAO = new CassandraBlobsDAO(cassandra.getConf());
+        cassandraMessageDAO = new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(),
+            blobsDAO, CassandraUtils.WITH_DEFAULT_CONFIGURATION, messageIdFactory);
+
+        attachmentMessageIdDAO = new CassandraAttachmentMessageIdDAO(cassandra.getConf(),
+            new CassandraMessageId.Factory(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+
+        migration = new AttachmentMessageIdCreation(cassandraMessageDAO, attachmentMessageIdDAO);
+
+        messageId = messageIdFactory.generate();
+    }
+
+    @Test
+    public void emptyMigrationShouldSucceed() {
+        assertThat(migration.run())
+            .isEqualTo(Migration.MigrationResult.COMPLETED);
+    }
+
+    @Test
+    public void migrationShouldSucceedWhenNoAttachment() throws Exception {
+        List<MessageAttachment> noAttachment = ImmutableList.of();
+        message = createMessage(messageId, noAttachment);
+
+        cassandraMessageDAO.save(message).join();
+
+        assertThat(migration.run())
+            .isEqualTo(Migration.MigrationResult.COMPLETED);
+    }
+
+    @Test
+    public void migrationShouldSucceedWhenAttachment() throws Exception {
+        MessageAttachment attachment = createAttachment();
+        message = createMessage(messageId, ImmutableList.of(attachment));
+
+        cassandraMessageDAO.save(message).join();
+
+        assertThat(migration.run())
+            .isEqualTo(Migration.MigrationResult.COMPLETED);
+    }
+
+    @Test
+    public void migrationShouldCreateAttachmentIdOnAttachmentMessageIdTableFromMessage() throws Exception {
+        MessageAttachment attachment = createAttachment();
+        message = createMessage(messageId, ImmutableList.of(attachment));
+
+        cassandraMessageDAO.save(message).join();
+
+        migration.run();
+
+        assertThat(attachmentMessageIdDAO.getOwnerMessageIds(attachment.getAttachmentId()).join())
+            .containsExactly(messageId);
+    }
+
+    @Test
+    public void migrationShouldReturnPartialWhenRetrieveAllAttachmentIdFromMessageFail() throws Exception {
+        CassandraMessageDAO cassandraMessageDAO = mock(CassandraMessageDAO.class);
+        CassandraAttachmentMessageIdDAO attachmentMessageIdDAO = mock(CassandraAttachmentMessageIdDAO.class);
+        migration = new AttachmentMessageIdCreation(cassandraMessageDAO, attachmentMessageIdDAO);
+
+        when(cassandraMessageDAO.retrieveAllMessageIdAttachmentIds()).thenThrow(new RuntimeException());
+
+        assertThat(migration.run()).isEqualTo(Migration.MigrationResult.PARTIAL);
+    }
+
+    @Test
+    public void migrationShouldReturnPartialWhenSavingAttachmentIdForMessageIdFail() throws Exception {
+        CassandraMessageDAO cassandraMessageDAO = mock(CassandraMessageDAO.class);
+        CassandraAttachmentMessageIdDAO attachmentMessageIdDAO = mock(CassandraAttachmentMessageIdDAO.class);
+        CassandraMessageDAO.MessageIdAttachmentIds messageIdAttachmentIds = mock(CassandraMessageDAO.MessageIdAttachmentIds.class);
+
+        migration = new AttachmentMessageIdCreation(cassandraMessageDAO, attachmentMessageIdDAO);
+
+        when(messageIdAttachmentIds.getAttachmentId()).thenReturn(ImmutableSet.of(AttachmentId.from("any")));
+        when(cassandraMessageDAO.retrieveAllMessageIdAttachmentIds())
+            .thenReturn(CompletableFuture.completedFuture(Stream.of(messageIdAttachmentIds)));
+        when(attachmentMessageIdDAO.storeAttachmentForMessageId(any(AttachmentId.class), any(MessageId.class)))
+            .thenThrow(RuntimeException.class);
+
+        assertThat(migration.run()).isEqualTo(Migration.MigrationResult.PARTIAL);
+    }
+
+    private SimpleMailboxMessage createMessage(MessageId messageId, Collection<MessageAttachment> attachments) {
+        MessageUid messageUid = MessageUid.of(1);
+        CassandraId mailboxId = CassandraId.timeBased();
+        String content = "Subject: Any subject \n\nThis is the body\n.\n";
+        int BODY_START = 22;
+
+        return SimpleMailboxMessage.builder()
+            .messageId(messageId)
+            .mailboxId(mailboxId)
+            .uid(messageUid)
+            .internalDate(new Date())
+            .bodyStartOctet(BODY_START)
+            .size(content.length())
+            .content(new SharedByteArrayInputStream(content.getBytes(Charsets.UTF_8)))
+            .flags(new Flags())
+            .propertyBuilder(new PropertyBuilder())
+            .addAttachments(attachments)
+            .build();
+    }
+
+    private MessageAttachment createAttachment() {
+        return MessageAttachment.builder()
+            .attachment(Attachment.builder()
+                .bytes("content".getBytes(StandardCharsets.UTF_8))
+                .type("type")
+                .build())
+            .build();
+    }
+}
\ No newline at end of file


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