james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From adup...@apache.org
Subject [2/7] james-project git commit: JAMES-1948 Introduce CassandraDeletedMessageDAO and its test
Date Thu, 02 Mar 2017 13:44:44 GMT
JAMES-1948 Introduce CassandraDeletedMessageDAO and its test


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

Branch: refs/heads/master
Commit: 33a58466cd1be6fce747452936722ac0454b2e7c
Parents: 9deeded
Author: Luc DUZAN <lduzan@linagora.com>
Authored: Thu Feb 23 16:35:57 2017 +0100
Committer: Luc DUZAN <lduzan@linagora.com>
Committed: Thu Mar 2 12:31:24 2017 +0100

----------------------------------------------------------------------
 .../mail/CassandraDeletedMessageDAO.java        | 179 ++++++++++++++
 .../mail/CassandraDeletedMessageDAOTest.java    | 231 +++++++++++++++++++
 2 files changed, 410 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/33a58466/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAO.java
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAO.java
new file mode 100644
index 0000000..10dabc6
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAO.java
@@ -0,0 +1,179 @@
+/****************************************************************
+ * 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.delete;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.gte;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.lte;
+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.CassandraDeletedMessageTable.MAILBOX_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraDeletedMessageTable.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraDeletedMessageTable.UID;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import com.datastax.driver.core.ResultSet;
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import org.apache.james.mailbox.model.MessageRange;
+
+public class CassandraDeletedMessageDAO {
+    private static final String UID_TO = "uid_to";
+    private static final String UID_FROM = "uid_from";
+
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement addStatement;
+    private final PreparedStatement deleteStatement;
+
+    private final PreparedStatement selectAllUidStatement;
+    private final PreparedStatement selectOneUidStatement;
+    private final PreparedStatement selectBetweenUidStatement;
+    private final PreparedStatement selectFromUidStatement;
+
+    @Inject
+    public CassandraDeletedMessageDAO(Session session) {
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        this.addStatement = prepareAddStatement(session);
+        this.deleteStatement = prepareDeleteStatement(session);
+        this.selectAllUidStatement = prepareAllUidStatement(session);
+        this.selectOneUidStatement = prepareOneUidStatement(session);
+        this.selectBetweenUidStatement = prepareBetweenUidStatement(session);
+        this.selectFromUidStatement = prepareFromUidStatement(session);
+    }
+
+    private PreparedStatement prepareAllUidStatement(Session session) {
+        return session.prepare(select(UID)
+            .from(TABLE_NAME)
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID))));
+    }
+
+    private PreparedStatement prepareOneUidStatement(Session session) {
+        return session.prepare(select(UID)
+            .from(TABLE_NAME)
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID)))
+            .and(eq(UID, bindMarker(UID))));
+    }
+
+    private PreparedStatement prepareBetweenUidStatement(Session session) {
+        return session.prepare(select(UID)
+            .from(TABLE_NAME)
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID)))
+            .and(gte(UID, bindMarker(UID_FROM)))
+            .and(lte(UID, bindMarker(UID_TO))));
+    }
+
+    private PreparedStatement prepareFromUidStatement(Session session) {
+        return session.prepare(select(UID)
+            .from(TABLE_NAME)
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID)))
+            .and(gte(UID, bindMarker(UID_FROM))));
+    }
+
+    private PreparedStatement prepareDeleteStatement(Session session) {
+        return session.prepare(delete()
+            .from(TABLE_NAME)
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID)))
+            .and(eq(UID, bindMarker(UID))));
+    }
+
+    private PreparedStatement prepareAddStatement(Session session) {
+        return session.prepare(insertInto(TABLE_NAME)
+            .value(MAILBOX_ID, bindMarker(MAILBOX_ID))
+            .value(UID, bindMarker(UID)));
+    }
+
+    public CompletableFuture<Void> addDeleted(CassandraId cassandraId, MessageUid uid)
{
+        return cassandraAsyncExecutor.executeVoid(
+            addStatement.bind()
+                .setUUID(MAILBOX_ID, cassandraId.asUuid())
+                .setLong(UID, uid.asLong()));
+    }
+
+    public CompletableFuture<Void> removeDeleted(CassandraId cassandraId, MessageUid
uid) {
+        return cassandraAsyncExecutor.executeVoid(deleteStatement.bind()
+            .setUUID(MAILBOX_ID, cassandraId.asUuid())
+            .setLong(UID, uid.asLong()));
+    }
+
+    public CompletableFuture<Stream<MessageUid>> retrieveDeletedMessage(CassandraId
cassandraId, MessageRange range) {
+        return retrieveResultSetOfDeletedMessage(cassandraId, range)
+            .thenApply(CassandraDeletedMessageDAO::resultSetToStream);
+    }
+
+    private CompletableFuture<ResultSet> retrieveResultSetOfDeletedMessage(CassandraId
cassandraId, MessageRange range) {
+        switch (range.getType()) {
+            case ALL:
+                return retrieveAllDeleted(cassandraId);
+            case FROM:
+                return retrieveDeletedAfter(cassandraId, range.getUidFrom());
+            case RANGE:
+                return retrieveDeletedBetween(cassandraId, range.getUidFrom(), range.getUidTo());
+            case ONE:
+                return retrieveOneDeleted(cassandraId, range.getUidFrom());
+        }
+
+        throw new UnsupportedOperationException();
+    }
+
+    private static Stream<MessageUid> resultSetToStream(ResultSet resultSet) {
+        return CassandraUtils.convertToStream(resultSet)
+            .map(row ->
+                MessageUid.of(row.getLong(UID)));
+    }
+
+    private CompletableFuture<ResultSet> retrieveAllDeleted(CassandraId cassandraId)
{
+        return cassandraAsyncExecutor.execute(
+            selectAllUidStatement.bind()
+                .setUUID(MAILBOX_ID, cassandraId.asUuid()));
+    }
+
+    private CompletableFuture<ResultSet> retrieveOneDeleted(CassandraId cassandraId,
MessageUid uid) {
+        return cassandraAsyncExecutor.execute(
+            selectOneUidStatement.bind()
+                .setUUID(MAILBOX_ID, cassandraId.asUuid())
+                .setLong(UID, uid.asLong()));
+    }
+
+    private CompletableFuture<ResultSet> retrieveDeletedBetween(CassandraId cassandraId,
MessageUid from, MessageUid to) {
+        return cassandraAsyncExecutor.execute(
+            selectBetweenUidStatement.bind()
+                .setUUID(MAILBOX_ID, cassandraId.asUuid())
+                .setLong(UID_FROM, from.asLong())
+                .setLong(UID_TO, to.asLong()));
+    }
+
+    private CompletableFuture<ResultSet> retrieveDeletedAfter(CassandraId cassandraId,
MessageUid from) {
+        return cassandraAsyncExecutor.execute(
+            selectFromUidStatement.bind()
+                .setUUID(MAILBOX_ID, cassandraId.asUuid())
+                .setLong(UID_FROM, from.asLong()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/33a58466/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAOTest.java
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAOTest.java
new file mode 100644
index 0000000..06e751f
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraDeletedMessageDAOTest.java
@@ -0,0 +1,231 @@
+/****************************************************************
+ * 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 com.github.steveash.guavate.Guavate;
+import java.util.UUID;
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.modules.CassandraDeletedMessageModule;
+import org.apache.james.mailbox.model.MessageRange;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CassandraDeletedMessageDAOTest {
+    public static final CassandraId MAILBOX_ID = CassandraId.of(UUID.fromString("110e8400-e29b-11d4-a716-446655440000"));
+    public static final MessageUid UID_1 = MessageUid.of(1);
+    public static final MessageUid UID_2 = MessageUid.of(2);
+    public static final MessageUid UID_3 = MessageUid.of(3);
+    public static final MessageUid UID_4 = MessageUid.of(4);
+    public static final MessageUid UID_7 = MessageUid.of(7);
+    public static final MessageUid UID_8 = MessageUid.of(8);
+
+    private CassandraCluster cassandra;
+    private CassandraDeletedMessageDAO testee;
+
+    @Before
+    public void setUp() {
+        cassandra = CassandraCluster.create(
+            new CassandraDeletedMessageModule());
+        cassandra.ensureAllTables();
+
+        testee = new CassandraDeletedMessageDAO(cassandra.getConf());
+    }
+
+    @After
+    public void tearDown() {
+        cassandra.clearAllTables();
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnEmptyByDefault() {
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void addDeletedMessageShouldThenBeReportedAsDeletedMessage() {
+        testee.addDeleted(MAILBOX_ID, UID_1).join();
+        testee.addDeleted(MAILBOX_ID, UID_2).join();
+
+        List<MessageUid> result = testee.retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_1, UID_2);
+    }
+
+    @Test
+    public void addDeletedMessageShouldBeIdempotent() {
+        testee.addDeleted(MAILBOX_ID, UID_1).join();
+        testee.addDeleted(MAILBOX_ID, UID_1).join();
+
+        List<MessageUid> result = testee.retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_1);
+    }
+
+
+    @Test
+    public void removeUnreadShouldReturnEmptyWhenNoData() {
+        testee.removeDeleted(MAILBOX_ID, UID_1).join();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void removeDeletedMessageShouldNotAffectOtherMessage() {
+        testee.addDeleted(MAILBOX_ID, UID_2).join();
+        testee.addDeleted(MAILBOX_ID, UID_1).join();
+
+        testee.removeDeleted(MAILBOX_ID, UID_1).join();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_2);
+    }
+
+    @Test
+    public void removeDeletedShouldRemoveSpecifiedUID() {
+        testee.addDeleted(MAILBOX_ID, UID_2).join();
+
+        testee.removeDeleted(MAILBOX_ID, UID_2).join();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    private void addMessageForRetrieveTest() {
+        testee.addDeleted(MAILBOX_ID, UID_1).join();
+        testee.addDeleted(MAILBOX_ID, UID_2).join();
+        testee.addDeleted(MAILBOX_ID, UID_3).join();
+        testee.addDeleted(MAILBOX_ID, UID_4).join();
+        testee.addDeleted(MAILBOX_ID, UID_7).join();
+        testee.addDeleted(MAILBOX_ID, UID_8).join();
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnAllMessageForMessageRangeAll() {
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.all())
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_1, UID_2, UID_3, UID_4, UID_7, UID_8);
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnOneMessageForMessageRangeOneIfThisMessageIsPresent()
{
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.one(UID_1))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_1);
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnNoMessageForMessageRangeOneIfThisMessageIsNotPresent()
{
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.one(MessageUid.of(42)))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnMessageInRangeForMessageRangeRange() {
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.range(MessageUid.of(3), MessageUid.of(7)))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_3, UID_4, UID_7);
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnNoMessageForMessageRangeRangeIfNoDeletedMessageInThatRange()
{
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.range(MessageUid.of(5), MessageUid.of(6)))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnNoMessageForMessageRangeFromIfNoDeletedMessageWithIdBiggerOrSameThanFrom()
{
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.from(MessageUid.of(9)))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).isEmpty();
+    }
+
+    @Test
+    public void retrieveDeletedMessageShouldReturnDeletedMessageWithIdBiggerOrSameThanFrom()
{
+        addMessageForRetrieveTest();
+
+        List<MessageUid> result = testee
+            .retrieveDeletedMessage(MAILBOX_ID, MessageRange.from(MessageUid.of(4)))
+            .join()
+            .collect(Guavate.toImmutableList());
+
+        assertThat(result).containsExactly(UID_4, UID_7, UID_8);
+    }
+}


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


Mime
View raw message