MAILBOX-304 Provides a new version of AttachmentDao relying on Blobs storage
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/7c8dc3d3 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/7c8dc3d3 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/7c8dc3d3 Branch: refs/heads/master Commit: 7c8dc3d3a27a2926608e71ecf600e7e87a7f94dd Parents: b8303be Author: benwa <btell...@linagora.com> Authored: Wed Sep 6 17:35:34 2017 +0700 Committer: benwa <btell...@linagora.com> Committed: Wed Sep 6 17:55:52 2017 +0700 ---------------------------------------------------------------------- .../CassandraMailboxSessionMapperFactory.java | 52 +++--- .../mail/CassandraAttachmentDAOV2.java | 132 ++++++++++++++ .../mail/CassandraAttachmentMapper.java | 44 +++-- .../modules/CassandraAttachmentModule.java | 13 +- .../table/CassandraAttachmentV2Table.java | 31 ++++ .../CassandraMailboxManagerProvider.java | 2 +- .../CassandraSubscriptionManagerTest.java | 3 + .../cassandra/CassandraTestSystemFixture.java | 2 +- .../mail/CassandraAttachmentDAOV2Test.java | 88 ++++++++++ .../CassandraAttachmentFallbackTestTest.java | 174 +++++++++++++++++++ .../CassandraMailboxManagerAttachmentTest.java | 2 +- .../cassandra/mail/CassandraMapperProvider.java | 2 +- .../cassandra/host/CassandraHostSystem.java | 2 +- 13 files changed, 499 insertions(+), 48 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/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 2db0e01..164a179 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.ids.CassandraMessageId; import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper; 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.CassandraBlobsDAO; import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO; @@ -61,9 +62,32 @@ import com.datastax.driver.core.Session; /** * Cassandra implementation of {@link MailboxSessionMapperFactory} - * */ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFactory { + + public static CassandraMailboxSessionMapperFactory forTests(Session session, CassandraTypesProvider typesProvider, + CassandraMessageId.Factory factory) { + CassandraBlobsDAO cassandraBlobsDAO = new CassandraBlobsDAO(session); + return new CassandraMailboxSessionMapperFactory( + new CassandraUidProvider(session), + new CassandraModSeqProvider(session), + session, + new CassandraMessageDAO(session, typesProvider, cassandraBlobsDAO), + new CassandraMessageIdDAO(session, factory), + new CassandraMessageIdToImapUidDAO(session, factory), + new CassandraMailboxCounterDAO(session), + new CassandraMailboxRecentsDAO(session), + new CassandraMailboxDAO(session, typesProvider), + new CassandraMailboxPathDAO(session, typesProvider), + new CassandraFirstUnseenDAO(session), + new CassandraApplicableFlagDAO(session), + new CassandraAttachmentDAO(session), + new CassandraAttachmentDAOV2(session, cassandraBlobsDAO), + new CassandraDeletedMessageDAO(session), + CassandraUtils.WITH_DEFAULT_CONFIGURATION, + CassandraConfiguration.DEFAULT_CONFIGURATION); + } + private final Session session; private final CassandraUidProvider uidProvider; private final CassandraModSeqProvider modSeqProvider; @@ -78,6 +102,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa private final CassandraFirstUnseenDAO firstUnseenDAO; private final CassandraApplicableFlagDAO applicableFlagDAO; private final CassandraAttachmentDAO attachmentDAO; + private final CassandraAttachmentDAOV2 attachmentDAOV2; private CassandraUtils cassandraUtils; private CassandraConfiguration cassandraConfiguration; private final CassandraDeletedMessageDAO deletedMessageDAO; @@ -88,7 +113,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, CassandraMailboxDAO mailboxDAO, CassandraMailboxPathDAO mailboxPathDAO, CassandraFirstUnseenDAO firstUnseenDAO, CassandraApplicableFlagDAO applicableFlagDAO, - CassandraAttachmentDAO attachmentDAO, CassandraDeletedMessageDAO deletedMessageDAO, CassandraUtils cassandraUtils, + CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2, CassandraDeletedMessageDAO deletedMessageDAO, CassandraUtils cassandraUtils, CassandraConfiguration cassandraConfiguration) { this.uidProvider = uidProvider; this.modSeqProvider = modSeqProvider; @@ -102,6 +127,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa this.mailboxPathDAO = mailboxPathDAO; this.firstUnseenDAO = firstUnseenDAO; this.attachmentDAO = attachmentDAO; + this.attachmentDAOV2 = attachmentDAOV2; this.deletedMessageDAO = deletedMessageDAO; this.applicableFlagDAO = applicableFlagDAO; this.cassandraUtils = cassandraUtils; @@ -114,26 +140,6 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa deletedMessageDAO); } - public CassandraMailboxSessionMapperFactory(Session session, CassandraTypesProvider typesProvider, - CassandraMessageId.Factory factory) { - this(new CassandraUidProvider(session), - new CassandraModSeqProvider(session), - session, - new CassandraMessageDAO(session, typesProvider, new CassandraBlobsDAO(session)), - new CassandraMessageIdDAO(session, factory), - new CassandraMessageIdToImapUidDAO(session, factory), - new CassandraMailboxCounterDAO(session), - new CassandraMailboxRecentsDAO(session), - new CassandraMailboxDAO(session, typesProvider), - new CassandraMailboxPathDAO(session, typesProvider), - new CassandraFirstUnseenDAO(session), - new CassandraApplicableFlagDAO(session), - new CassandraAttachmentDAO(session), - new CassandraDeletedMessageDAO(session), - CassandraUtils.WITH_DEFAULT_CONFIGURATION, - CassandraConfiguration.DEFAULT_CONFIGURATION); - } - @Override public CassandraMessageMapper createMessageMapper(MailboxSession mailboxSession) { return new CassandraMessageMapper( @@ -168,7 +174,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa @Override public AttachmentMapper createAttachmentMapper(MailboxSession mailboxSession) { - return new CassandraAttachmentMapper(attachmentDAO); + return new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2); } @Override http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java new file mode 100644 index 0000000..be8bebf --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2.java @@ -0,0 +1,132 @@ +/**************************************************************** + * 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.CassandraAttachmentV2Table.BLOB_ID; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table.FIELDS; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table.ID; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table.SIZE; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table.TABLE_NAME; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table.TYPE; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import javax.inject.Inject; + +import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor; +import org.apache.james.mailbox.cassandra.ids.BlobId; +import org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable; +import org.apache.james.mailbox.model.Attachment; +import org.apache.james.mailbox.model.AttachmentId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.datastax.driver.core.PreparedStatement; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.google.common.base.Preconditions; + +public class CassandraAttachmentDAOV2 { + private static final Logger LOGGER = LoggerFactory.getLogger(CassandraAttachmentMapper.class); + private static final boolean NO_LOG_IF_EMPTY = false; + + private final CassandraAsyncExecutor cassandraAsyncExecutor; + private final PreparedStatement insertStatement; + private final CassandraBlobsDAO blobsDAO; + private final PreparedStatement selectStatement; + + @Inject + public CassandraAttachmentDAOV2(Session session, CassandraBlobsDAO blobsDAO) { + this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session); + this.blobsDAO = blobsDAO; + + this.selectStatement = prepareSelect(session); + this.insertStatement = prepareInsert(session); + } + + private PreparedStatement prepareInsert(Session session) { + return session.prepare( + insertInto(TABLE_NAME) + .value(ID, bindMarker(ID)) + .value(BLOB_ID, bindMarker(BLOB_ID)) + .value(TYPE, bindMarker(TYPE)) + .value(SIZE, bindMarker(SIZE))); + } + + private PreparedStatement prepareSelect(Session session) { + return session.prepare(select(FIELDS) + .from(TABLE_NAME) + .where(eq(ID, bindMarker(ID)))); + } + + public CompletableFuture<Optional<Attachment>> getAttachment(AttachmentId attachmentId) { + return getAttachment(attachmentId, NO_LOG_IF_EMPTY); + } + + public CompletableFuture<Optional<Attachment>> getAttachment(AttachmentId attachmentId, boolean logIfEmpty) { + Preconditions.checkArgument(attachmentId != null); + return cassandraAsyncExecutor.executeSingleRow( + selectStatement.bind() + .setString(CassandraAttachmentTable.ID, attachmentId.getId())) + .thenCompose(this::attachment) + .thenApply(optional -> logNotFound(attachmentId, logIfEmpty, optional)); + } + + private Optional<Attachment> logNotFound(AttachmentId attachmentId, boolean logIfEmpty, Optional<Attachment> optional) { + if (!optional.isPresent() && logIfEmpty) { + LOGGER.warn("Failed retrieving attachment {}", attachmentId); + } + return optional; + } + + public CompletableFuture<Void> storeAttachment(Attachment attachment) { + return blobsDAO.save(attachment.getBytes()) + .thenApply(Optional::get) // attachment payload is never null + .thenApply(blobId -> + insertStatement.bind() + .setString(ID, attachment.getAttachmentId().getId()) + .setLong(SIZE, attachment.getSize()) + .setString(TYPE, attachment.getType()) + .setString(BLOB_ID, blobId.getId())) + .thenCompose(cassandraAsyncExecutor::executeVoid); + } + + private CompletableFuture<Optional<Attachment>> attachment(Optional<Row> rowOptional) { + if (rowOptional.isPresent()) { + return attachment(rowOptional.get()) + .thenApply(Optional::of); + } + return CompletableFuture.completedFuture(Optional.empty()); + } + + private CompletableFuture<Attachment> attachment(Row row) { + return blobsDAO.read(BlobId.from(row.getString(BLOB_ID))) + .thenApply(bytes -> Attachment.builder() + .attachmentId(AttachmentId.from(row.getString(ID))) + .bytes(bytes) + .type(row.getString(TYPE)) + .build()); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/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 0a346e8..a0cdc71 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 @@ -19,11 +19,11 @@ package org.apache.james.mailbox.cassandra.mail; -import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import java.util.stream.Stream; import javax.inject.Inject; @@ -36,8 +36,6 @@ import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.util.FluentFutureStream; import org.apache.james.util.OptionalUtils; -import com.github.fge.lambdas.Throwing; -import com.github.fge.lambdas.ThrownByLambdaException; import com.github.steveash.guavate.Guavate; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; @@ -45,12 +43,15 @@ import com.google.common.collect.ImmutableList; public class CassandraAttachmentMapper implements AttachmentMapper { private static final boolean LOG_IF_EMPTY = true; + private static final boolean NO_LOG_IF_EMPTY = !LOG_IF_EMPTY; private final CassandraAttachmentDAO attachmentDAO; + private final CassandraAttachmentDAOV2 attachmentDAOV2; @Inject - public CassandraAttachmentMapper(CassandraAttachmentDAO attachmentDAO) { + public CassandraAttachmentMapper(CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2) { this.attachmentDAO = attachmentDAO; + this.attachmentDAOV2 = attachmentDAOV2; } @Override @@ -65,7 +66,8 @@ public class CassandraAttachmentMapper implements AttachmentMapper { @Override public Attachment getAttachment(AttachmentId attachmentId) throws AttachmentNotFoundException { Preconditions.checkArgument(attachmentId != null); - return attachmentDAO.getAttachment(attachmentId) + return attachmentDAOV2.getAttachment(attachmentId) + .thenCompose(v2Value -> fallbackToV1(attachmentId, v2Value)) .join() .orElseThrow(() -> new AttachmentNotFoundException(attachmentId.getId())); } @@ -81,7 +83,8 @@ public class CassandraAttachmentMapper implements AttachmentMapper { Stream<CompletableFuture<Optional<Attachment>>> attachments = attachmentIds .stream() .distinct() - .map(id -> attachmentDAO.getAttachment(id, LOG_IF_EMPTY)); + .map(id -> attachmentDAOV2.getAttachment(id, LOG_IF_EMPTY) + .thenCompose(v2Value -> fallbackToV1(id, v2Value, LOG_IF_EMPTY))); return FluentFutureStream .of(attachments) @@ -89,24 +92,27 @@ public class CassandraAttachmentMapper implements AttachmentMapper { .collect(Guavate.toImmutableList()); } + private CompletionStage<Optional<Attachment>> fallbackToV1(AttachmentId attachmentId, Optional<Attachment> v2Value) { + return fallbackToV1(attachmentId, v2Value, NO_LOG_IF_EMPTY); + } + + private CompletionStage<Optional<Attachment>> fallbackToV1(AttachmentId attachmentId, Optional<Attachment> v2Value, boolean logIfEmpty) { + if (v2Value.isPresent()) { + return CompletableFuture.completedFuture(v2Value); + } + return attachmentDAO.getAttachment(attachmentId, logIfEmpty); + } + @Override public void storeAttachment(Attachment attachment) throws MailboxException { - try { - attachmentDAO.storeAttachment(attachment).join(); - } catch (IOException e) { - throw new MailboxException(e.getMessage(), e); - } + attachmentDAOV2.storeAttachment(attachment).join(); } @Override public void storeAttachments(Collection<Attachment> attachments) throws MailboxException { - try { - FluentFutureStream.of( - attachments.stream() - .map(Throwing.function(attachmentDAO::storeAttachment))) - .join(); - } catch (ThrownByLambdaException e) { - throw new MailboxException(e.getCause().getMessage(), e.getCause()); - } + FluentFutureStream.of( + attachments.stream() + .map(attachmentDAOV2::storeAttachment)) + .join(); } } http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/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 d576f91..ecd9d64 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 @@ -29,6 +29,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.mailbox.cassandra.table.CassandraAttachmentTable; +import org.apache.james.mailbox.cassandra.table.CassandraAttachmentV2Table; import com.datastax.driver.core.schemabuilder.SchemaBuilder; import com.google.common.collect.ImmutableList; @@ -48,7 +49,17 @@ public class CassandraAttachmentModule implements CassandraModule { .addColumn(CassandraAttachmentTable.TYPE, text()) .addColumn(CassandraAttachmentTable.SIZE, bigint()) .withOptions() - .comment("Holds attachment for fast attachment retrieval"))); + .comment("Holds attachment for fast attachment retrieval")), + new CassandraTable(CassandraAttachmentV2Table.TABLE_NAME, + SchemaBuilder.createTable(CassandraAttachmentV2Table.TABLE_NAME) + .ifNotExists() + .addPartitionKey(CassandraAttachmentV2Table.ID, text()) + .addColumn(CassandraAttachmentV2Table.BLOB_ID, text()) + .addColumn(CassandraAttachmentV2Table.TYPE, text()) + .addColumn(CassandraAttachmentV2Table.SIZE, bigint()) + .withOptions() + .comment("Holds attachment for fast attachment retrieval. Content of messages is stored" + + "in `blobs` and `blobparts` tables."))); types = ImmutableList.of(); } http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentV2Table.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentV2Table.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentV2Table.java new file mode 100644 index 0000000..9b3daec --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentV2Table.java @@ -0,0 +1,31 @@ +/**************************************************************** + * 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 CassandraAttachmentV2Table { + + String TABLE_NAME = "attachmentV2"; + String ID = "id"; + String BLOB_ID = "blobId"; + String TYPE = "type"; + String SIZE = "size"; + String[] FIELDS = { ID, BLOB_ID, TYPE, SIZE }; + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java index 9baf216..06c04a9 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerProvider.java @@ -41,7 +41,7 @@ public class CassandraMailboxManagerProvider { public static CassandraMailboxManager provideMailboxManager(Session session, CassandraTypesProvider cassandraTypesProvider) { CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory(); - CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory( + CassandraMailboxSessionMapperFactory mapperFactory = CassandraMailboxSessionMapperFactory.forTests( session, cassandraTypesProvider, messageIdFactory); http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/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 8f8139c..c9c02ef 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 @@ -28,6 +28,7 @@ import org.apache.james.mailbox.AbstractSubscriptionManagerTest; 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.CassandraDeletedMessageDAO; import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO; @@ -87,6 +88,7 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage CassandraApplicableFlagDAO applicableFlagDAO = null; CassandraAttachmentDAO attachmentDAO = null; CassandraDeletedMessageDAO deletedMessageDAO = null; + CassandraAttachmentDAOV2 attachmentDAOV2 = null; return new CassandraSubscriptionManager( new CassandraMailboxSessionMapperFactory( new CassandraUidProvider(cassandra.getConf()), @@ -102,6 +104,7 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage firstUnseenDAO, applicableFlagDAO, attachmentDAO, + attachmentDAOV2, deletedMessageDAO, CassandraUtils.WITH_DEFAULT_CONFIGURATION, CassandraConfiguration.DEFAULT_CONFIGURATION)); http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java index 3606d81..c8d7580 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java @@ -44,7 +44,7 @@ public class CassandraTestSystemFixture { public static CassandraMailboxSessionMapperFactory createMapperFactory(CassandraCluster cassandra) { CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory(); - return new CassandraMailboxSessionMapperFactory( + return CassandraMailboxSessionMapperFactory.forTests( cassandra.getConf(), cassandra.getTypesProvider(), messageIdFactory); http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2Test.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2Test.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2Test.java new file mode 100644 index 0000000..130ef19 --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentDAOV2Test.java @@ -0,0 +1,88 @@ +/**************************************************************** + * 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 org.assertj.core.api.Assertions.assertThat; + +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +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.modules.CassandraAttachmentModule; +import org.apache.james.mailbox.cassandra.modules.CassandraBlobModule; +import org.apache.james.mailbox.model.Attachment; +import org.apache.james.mailbox.model.AttachmentId; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +public class CassandraAttachmentDAOV2Test { + public static final AttachmentId ATTACHMENT_ID = AttachmentId.from("id1"); + + @ClassRule + public static DockerCassandraRule cassandraServer = new DockerCassandraRule(); + + private CassandraCluster cassandra; + + private CassandraAttachmentDAOV2 testee; + + @Before + public void setUp() throws Exception { + CassandraModuleComposite compositeModule = new CassandraModuleComposite( + new CassandraAttachmentModule(), + new CassandraBlobModule()); + + cassandra = CassandraCluster.create( + compositeModule, + cassandraServer.getIp(), + cassandraServer.getBindingPort()); + + testee = new CassandraAttachmentDAOV2(cassandra.getConf(), new CassandraBlobsDAO(cassandra.getConf())); + } + + @After + public void tearDown() throws Exception { + cassandra.close(); + } + + @Test + public void getAttachmentShouldReturnEmptyWhenAbsent() { + Optional<Attachment> attachment = testee.getAttachment(ATTACHMENT_ID).join(); + + assertThat(attachment).isEmpty(); + } + + @Test + public void getAttachmentShouldReturnAttachmentWhenStored() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + testee.storeAttachment(attachment).join(); + + Optional<Attachment> actual = testee.getAttachment(ATTACHMENT_ID).join(); + + assertThat(actual).contains(attachment); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTestTest.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTestTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTestTest.java new file mode 100644 index 0000000..45f03e2 --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentFallbackTestTest.java @@ -0,0 +1,174 @@ +/**************************************************************** + * 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 org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.nio.charset.StandardCharsets; + +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.modules.CassandraAttachmentModule; +import org.apache.james.mailbox.cassandra.modules.CassandraBlobModule; +import org.apache.james.mailbox.exception.AttachmentNotFoundException; +import org.apache.james.mailbox.model.Attachment; +import org.apache.james.mailbox.model.AttachmentId; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; + +public class CassandraAttachmentFallbackTestTest { + public static final AttachmentId ATTACHMENT_ID_1 = AttachmentId.from("id1"); + public static final AttachmentId ATTACHMENT_ID_2 = AttachmentId.from("id2"); + + @ClassRule + public static DockerCassandraRule cassandraServer = new DockerCassandraRule(); + + private CassandraCluster cassandra; + + private CassandraAttachmentDAOV2 attachmentDAOV2; + private CassandraAttachmentDAO attachmentDAO; + private CassandraAttachmentMapper attachmentMapper; + + @Before + public void setUp() throws Exception { + CassandraModuleComposite compositeModule = new CassandraModuleComposite( + new CassandraAttachmentModule(), + new CassandraBlobModule()); + + cassandra = CassandraCluster.create( + compositeModule, + cassandraServer.getIp(), + cassandraServer.getBindingPort()); + + attachmentDAOV2 = new CassandraAttachmentDAOV2(cassandra.getConf(), new CassandraBlobsDAO(cassandra.getConf())); + attachmentDAO = new CassandraAttachmentDAO(cassandra.getConf()); + attachmentMapper = new CassandraAttachmentMapper(attachmentDAO, attachmentDAOV2); + } + + @After + public void tearDown() throws Exception { + cassandra.close(); + } + + @Test + public void getAttachmentShouldThrowWhenAbsentFromV1AndV2() throws Exception { + assertThatThrownBy(() -> attachmentMapper.getAttachment(ATTACHMENT_ID_1)) + .isInstanceOf(AttachmentNotFoundException.class); + } + + @Test + public void getAttachmentsShouldReturnEmptyWhenAbsentFromV1AndV2() throws Exception { + assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1))) + .isEmpty(); + } + + @Test + public void getAttachmentShouldReturnV2WhenPresentInV1AndV2() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + Attachment otherAttachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + + attachmentDAOV2.storeAttachment(attachment).join(); + attachmentDAO.storeAttachment(otherAttachment).join(); + + assertThat(attachmentMapper.getAttachment(ATTACHMENT_ID_1)) + .isEqualTo(attachment); + } + + @Test + public void getAttachmentShouldReturnV1WhenV2Absent() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + + attachmentDAO.storeAttachment(attachment).join(); + + assertThat(attachmentMapper.getAttachment(ATTACHMENT_ID_1)) + .isEqualTo(attachment); + } + + @Test + public void getAttachmentsShouldReturnV2WhenV2AndV1() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + Attachment otherAttachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + + attachmentDAOV2.storeAttachment(attachment).join(); + attachmentDAO.storeAttachment(otherAttachment).join(); + + assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1))) + .containsExactly(attachment); + } + + @Test + public void getAttachmentsShouldReturnV1WhenV2Absent() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + + attachmentDAO.storeAttachment(attachment).join(); + + assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1))) + .containsExactly(attachment); + } + + @Test + public void getAttachmentsShouldCombineElementsFromV1AndV2() throws Exception { + Attachment attachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_1) + .type("application/json") + .bytes("{\"property\":`\"value\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + Attachment otherAttachment = Attachment.builder() + .attachmentId(ATTACHMENT_ID_2) + .type("application/json") + .bytes("{\"property\":`\"different\"}".getBytes(StandardCharsets.UTF_8)) + .build(); + + attachmentDAOV2.storeAttachment(attachment).join(); + attachmentDAO.storeAttachment(otherAttachment).join(); + + assertThat(attachmentMapper.getAttachments(ImmutableList.of(ATTACHMENT_ID_1, ATTACHMENT_ID_2))) + .containsExactly(attachment, otherAttachment); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java index c196a0f..2c87057 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java @@ -91,7 +91,7 @@ public class CassandraMailboxManagerAttachmentTest extends AbstractMailboxManage private void initSystemUnderTest() throws Exception { CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory(); - mailboxSessionMapperFactory = new CassandraMailboxSessionMapperFactory( + mailboxSessionMapperFactory = CassandraMailboxSessionMapperFactory.forTests( cassandra.getConf(), cassandra.getTypesProvider(), messageIdFactory); http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java index 245acbf..59c1dd1 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java @@ -78,7 +78,7 @@ public class CassandraMapperProvider implements MapperProvider { } private CassandraMailboxSessionMapperFactory createMapperFactory() { - return new CassandraMailboxSessionMapperFactory(cassandra.getConf(), + return CassandraMailboxSessionMapperFactory.forTests(cassandra.getConf(), cassandra.getTypesProvider(), new CassandraMessageId.Factory()); } http://git-wip-us.apache.org/repos/asf/james-project/blob/7c8dc3d3/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java ---------------------------------------------------------------------- diff --git a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java index e0c2468..c8aa4a5 100644 --- a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java +++ b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java @@ -102,7 +102,7 @@ public class CassandraHostSystem extends JamesImapHostSystem { cassandra = CassandraCluster.create(mailboxModule, cassandraHost, cassandraPort); com.datastax.driver.core.Session session = cassandra.getConf(); CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory(); - CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory( + CassandraMailboxSessionMapperFactory mapperFactory = CassandraMailboxSessionMapperFactory.forTests( cassandra.getConf(), cassandra.getTypesProvider(), messageIdFactory); --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org