MAILBOX-268 CassandraAttachmentMapper implementation
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/7697abfe Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/7697abfe Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/7697abfe Branch: refs/heads/master Commit: 7697abfe18a19d3fa8dd34f4409ee12edaf47bd0 Parents: dd4f7ca Author: Antoine Duprat <adup...@linagora.com> Authored: Wed May 25 10:16:28 2016 +0200 Committer: Antoine Duprat <antdup...@gmail.com> Committed: Fri May 27 10:02:02 2016 +0200 ---------------------------------------------------------------------- .../CassandraMailboxSessionMapperFactory.java | 4 +- .../mail/CassandraAttachmentMapper.java | 109 +++++++++++++++++++ .../modules/CassandraAttachmentModule.java | 70 ++++++++++++ .../table/CassandraAttachmentTable.java | 31 ++++++ .../mail/CassandraAttachmentMapperTest.java | 30 +++++ .../cassandra/mail/CassandraMapperProvider.java | 15 ++- .../inmemory/mail/InMemoryMapperProvider.java | 6 + .../model/AbstractAttachmentMapperTest.java | 72 ++++++++++++ .../store/mail/model/MapperProvider.java | 3 + 9 files changed, 337 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/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 02e8373..7e18c26 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 @@ -23,6 +23,7 @@ import javax.inject.Inject; import org.apache.james.backends.cassandra.init.CassandraTypesProvider; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper; import org.apache.james.mailbox.cassandra.mail.CassandraMessageMapper; import org.apache.james.mailbox.cassandra.user.CassandraSubscriptionMapper; @@ -30,7 +31,6 @@ import org.apache.james.mailbox.store.MailboxSessionMapperFactory; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.ModSeqProvider; -import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; import org.apache.james.mailbox.store.mail.UidProvider; import org.apache.james.mailbox.store.user.SubscriptionMapper; @@ -74,7 +74,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa @Override public AttachmentMapper createAttachmentMapper(MailboxSession mailboxSession) { - return new NoopAttachmentMapper(); + return new CassandraAttachmentMapper(session); } @Override http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/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 new file mode 100644 index 0000000..68cacfa --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapper.java @@ -0,0 +1,109 @@ +/**************************************************************** + * 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.CassandraAttachmentTable.FIELDS; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable.ID; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable.PAYLOAD; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable.SIZE; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable.TABLE_NAME; +import static org.apache.james.mailbox.cassandra.table.CassandraAttachmentTable.TYPE; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.apache.commons.io.IOUtils; +import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor; +import org.apache.james.mailbox.exception.AttachmentNotFoundException; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.store.mail.AttachmentMapper; +import org.apache.james.mailbox.store.mail.model.Attachment; +import org.apache.james.mailbox.store.mail.model.AttachmentId; + +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 CassandraAttachmentMapper implements AttachmentMapper { + + private final CassandraAsyncExecutor cassandraAsyncExecutor; + private final PreparedStatement select; + private final PreparedStatement insert; + + public CassandraAttachmentMapper(Session session) { + this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session); + this.select = session.prepare(select(FIELDS) + .from(TABLE_NAME) + .where(eq(ID, bindMarker(ID)))); + this.insert = session.prepare(insertInto(TABLE_NAME) + .value(ID, bindMarker(ID)) + .value(PAYLOAD, bindMarker(PAYLOAD)) + .value(TYPE, bindMarker(TYPE)) + .value(SIZE, bindMarker(SIZE))); + } + + @Override + public void endRequest() { + } + + @Override + public <T> T execute(Transaction<T> transaction) throws MailboxException { + return transaction.run(); + } + + @Override + public Attachment getAttachment(AttachmentId attachmentId) throws AttachmentNotFoundException { + Preconditions.checkArgument(attachmentId != null); + return cassandraAsyncExecutor.executeSingleRow(select.bind() + .setString(ID, attachmentId.getId())) + .thenApply(optional -> optional.map(this::attachment)) + .join() + .orElseThrow(() -> new AttachmentNotFoundException(attachmentId.getId())); + } + + private Attachment attachment(Row row) { + return Attachment.builder() + .attachmentId(AttachmentId.from(row.getString(ID))) + .bytes(row.getBytes(PAYLOAD).array()) + .type(row.getString(TYPE)) + .size(row.getLong(SIZE)) + .build(); + } + + @Override + public void storeAttachment(Attachment attachment) throws MailboxException { + try { + cassandraAsyncExecutor.execute( + insert.bind() + .setString(ID, attachment.getAttachmentId().getId()) + .setBytes(PAYLOAD, ByteBuffer.wrap(IOUtils.toByteArray(attachment.getStream()))) + .setString(TYPE, attachment.getType()) + .setLong(SIZE, attachment.getSize()) + ).join(); + } catch (IOException e) { + throw new MailboxException(e.getMessage(), e); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/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 new file mode 100644 index 0000000..0108161 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAttachmentModule.java @@ -0,0 +1,70 @@ +/**************************************************************** + * 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.modules; + +import static com.datastax.driver.core.DataType.bigint; +import static com.datastax.driver.core.DataType.blob; +import static com.datastax.driver.core.DataType.text; + +import java.util.Collections; +import java.util.List; + +import org.apache.james.backends.cassandra.components.CassandraIndex; +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 com.datastax.driver.core.schemabuilder.SchemaBuilder; + +public class CassandraAttachmentModule implements CassandraModule { + + private final List<CassandraTable> tables; + private final List<CassandraIndex> index; + private final List<CassandraType> types; + + public CassandraAttachmentModule() { + tables = Collections.singletonList( + new CassandraTable(CassandraAttachmentTable.TABLE_NAME, + SchemaBuilder.createTable(CassandraAttachmentTable.TABLE_NAME) + .ifNotExists() + .addPartitionKey(CassandraAttachmentTable.ID, text()) + .addColumn(CassandraAttachmentTable.PAYLOAD, blob()) + .addColumn(CassandraAttachmentTable.TYPE, text()) + .addColumn(CassandraAttachmentTable.SIZE, bigint()))); + index = Collections.emptyList(); + types = Collections.emptyList(); + } + + @Override + public List<CassandraTable> moduleTables() { + return tables; + } + + @Override + public List<CassandraIndex> moduleIndex() { + return index; + } + + @Override + public List<CassandraType> moduleTypes() { + return types; + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentTable.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentTable.java new file mode 100644 index 0000000..a7115eb --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAttachmentTable.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 CassandraAttachmentTable { + + String TABLE_NAME = "attachment"; + String ID = "id"; + String PAYLOAD = "payload"; + String TYPE = "type"; + String SIZE = "size"; + String[] FIELDS = { ID, PAYLOAD, TYPE, SIZE }; + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java new file mode 100644 index 0000000..d4162bb --- /dev/null +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraAttachmentMapperTest.java @@ -0,0 +1,30 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox.cassandra.mail; + +import org.apache.james.mailbox.store.mail.model.AbstractAttachmentMapperTest; + +public class CassandraAttachmentMapperTest extends AbstractAttachmentMapperTest { + + public CassandraAttachmentMapperTest() { + super(new CassandraMapperProvider()); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/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 6201a9e..246b6a2 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 @@ -5,6 +5,7 @@ import org.apache.james.backends.cassandra.init.CassandraModuleComposite; import org.apache.james.mailbox.cassandra.CassandraId; import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory; import org.apache.james.mailbox.cassandra.modules.CassandraAclModule; +import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule; import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule; @@ -12,6 +13,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule; import org.apache.james.mailbox.cassandra.modules.CassandraUidModule; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.mock.MockMailboxSession; +import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.MapperProvider; @@ -24,7 +26,8 @@ public class CassandraMapperProvider implements MapperProvider { new CassandraMessageModule(), new CassandraMailboxCounterModule(), new CassandraModSeqModule(), - new CassandraUidModule())); + new CassandraUidModule(), + new CassandraAttachmentModule())); @Override public MailboxMapper createMailboxMapper() throws MailboxException { @@ -47,6 +50,16 @@ public class CassandraMapperProvider implements MapperProvider { } @Override + public AttachmentMapper createAttachmentMapper() throws MailboxException { + return new CassandraMailboxSessionMapperFactory( + new CassandraUidProvider(cassandra.getConf()), + new CassandraModSeqProvider(cassandra.getConf()), + cassandra.getConf(), + cassandra.getTypesProvider() + ).getAttachmentMapper(new MockMailboxSession("benwa")); + } + + @Override public CassandraId generateId() { return CassandraId.timeBased(); } http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java index 50114a4..3fa98da 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java @@ -6,6 +6,7 @@ import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.mock.MockMailboxSession; +import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.model.MapperProvider; @@ -29,6 +30,11 @@ public class InMemoryMapperProvider implements MapperProvider { } @Override + public AttachmentMapper createAttachmentMapper() throws MailboxException { + return new InMemoryMailboxSessionMapperFactory().createAttachmentMapper(new MockMailboxSession("user")); + } + + @Override public InMemoryId generateId() { return InMemoryId.of(random.nextInt()); } http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AbstractAttachmentMapperTest.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AbstractAttachmentMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AbstractAttachmentMapperTest.java new file mode 100644 index 0000000..ab31166 --- /dev/null +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/AbstractAttachmentMapperTest.java @@ -0,0 +1,72 @@ +/**************************************************************** + * 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.store.mail.model; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.apache.james.mailbox.exception.AttachmentNotFoundException; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.store.mail.AttachmentMapper; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public abstract class AbstractAttachmentMapperTest { + + private MapperProvider mapperProvider; + private AttachmentMapper attachmentMapper; + + public AbstractAttachmentMapperTest(MapperProvider mapperProvider) { + this.mapperProvider = mapperProvider; + } + + @Before + public void setUp() throws MailboxException { + mapperProvider.ensureMapperPrepared(); + attachmentMapper = mapperProvider.createAttachmentMapper(); + } + + @After + public void tearDown() throws MailboxException { + mapperProvider.clearMapper(); + } + + @Test (expected = IllegalArgumentException.class) + public void getAttachmentShouldThrowWhenNullAttachmentId() throws Exception { + attachmentMapper.getAttachment(null); + } + + @Test (expected = AttachmentNotFoundException.class) + public void getAttachmentShouldThrowWhenNonReferencedAttachmentId() throws Exception { + attachmentMapper.getAttachment(AttachmentId.forPayload("unknown".getBytes())); + } + + @Test + public void getAttachmentShouldReturnTheAttachmentWhenReferenced() throws Exception { + //Given + Attachment expected = Attachment.from("payload".getBytes(), "content"); + AttachmentId attachmentId = expected.getAttachmentId(); + attachmentMapper.storeAttachment(expected); + //When + Attachment attachment = attachmentMapper.getAttachment(attachmentId); + //Then + assertThat(attachment).isEqualTo(expected); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/7697abfe/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java index 91b42a8..4a8314a 100644 --- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MapperProvider.java @@ -20,6 +20,7 @@ package org.apache.james.mailbox.store.mail.model; import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; @@ -28,6 +29,8 @@ public interface MapperProvider { MessageMapper createMessageMapper() throws MailboxException; + AttachmentMapper createAttachmentMapper() throws MailboxException; + MailboxId generateId(); void clearMapper() throws MailboxException; --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org