You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/08/03 02:01:18 UTC

[james-project] branch master updated: JAMES-3516 Implement Thread Cassandra table

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new 33bbc51  JAMES-3516 Implement Thread Cassandra table
33bbc51 is described below

commit 33bbc51f44733e8c7f14ae88f2cd172028b7dd2b
Author: quanth <hq...@linagora.com>
AuthorDate: Thu Jul 22 17:48:39 2021 +0700

    JAMES-3516 Implement Thread Cassandra table
---
 .../mailbox/cassandra/mail/CassandraThreadDAO.java | 114 +++++++++++++
 .../cassandra/modules/CassandraThreadModule.java   |  45 +++++
 .../cassandra/table/CassandraThreadTable.java      |  35 ++++
 .../cassandra/mail/CassandraThreadDAOTest.java     | 190 +++++++++++++++++++++
 .../modules/mailbox/CassandraMailboxModule.java    |   1 +
 .../mailbox/CassandraThreadIdGuessingModule.java   |  39 +++++
 6 files changed, 424 insertions(+)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAO.java
new file mode 100644
index 0000000..b474553
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAO.java
@@ -0,0 +1,114 @@
+/******************************************************************
+ * 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.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIdTable.THREAD_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIds.MESSAGE_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.BASE_SUBJECT;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.MIME_MESSAGE_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.USERNAME;
+import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY;
+
+import java.util.Optional;
+import java.util.Set;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
+import org.apache.james.mailbox.store.mail.model.MimeMessageId;
+import org.apache.james.mailbox.store.mail.model.Subject;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+
+import reactor.core.publisher.Flux;
+
+public class CassandraThreadDAO {
+    private final CassandraAsyncExecutor executor;
+    private final PreparedStatement insertOne;
+    private final PreparedStatement selectOne;
+    private final PreparedStatement deleteOne;
+
+    @Inject
+    public CassandraThreadDAO(Session session) {
+        executor = new CassandraAsyncExecutor(session);
+
+        insertOne = session.prepare(insertInto(TABLE_NAME)
+            .value(USERNAME, bindMarker(USERNAME))
+            .value(MIME_MESSAGE_ID, bindMarker(MIME_MESSAGE_ID))
+            .value(MESSAGE_ID, bindMarker(MESSAGE_ID))
+            .value(THREAD_ID, bindMarker(THREAD_ID))
+            .value(BASE_SUBJECT, bindMarker(BASE_SUBJECT)));
+
+        selectOne = session.prepare(select(BASE_SUBJECT, THREAD_ID)
+            .from(TABLE_NAME)
+            .where(eq(USERNAME, bindMarker(USERNAME)))
+            .and(eq(MIME_MESSAGE_ID, bindMarker(MIME_MESSAGE_ID))));
+
+        deleteOne = session.prepare(delete().from(TABLE_NAME)
+            .where(eq(USERNAME, bindMarker(USERNAME)))
+            .and(eq(MIME_MESSAGE_ID, bindMarker(MIME_MESSAGE_ID))));
+    }
+
+    public Flux<Void> insertSome(Username username, Set<MimeMessageId> mimeMessageIds, MessageId messageId, ThreadId threadId, Optional<Subject> baseSubject) {
+        return Flux.fromIterable(mimeMessageIds)
+            .flatMap(mimeMessageId -> executor.executeVoid(insertOne.bind()
+                .setString(USERNAME, username.asString())
+                .setString(MIME_MESSAGE_ID, mimeMessageId.getValue())
+                .setUUID(MESSAGE_ID, ((CassandraMessageId) messageId).get())
+                .setUUID(THREAD_ID, ((CassandraMessageId) threadId.getBaseMessageId()).get())
+                .setString(BASE_SUBJECT, baseSubject.map(Subject::getValue).orElse(null))), DEFAULT_CONCURRENCY);
+    }
+
+    public Flux<Pair<Optional<Subject>, ThreadId>> selectSome(Username username, Set<MimeMessageId> mimeMessageIds) {
+        return Flux.fromIterable(mimeMessageIds)
+            .flatMap(mimeMessageId -> executor
+                .executeSingleRow(selectOne.bind()
+                    .setString(USERNAME, username.asString())
+                    .setString(MIME_MESSAGE_ID, mimeMessageId.getValue()))
+                .map(this::readRow), DEFAULT_CONCURRENCY)
+            .distinct();
+    }
+
+    public Flux<Void> deleteSome(Username username, Set<MimeMessageId> mimeMessageIds) {
+        return Flux.fromIterable(mimeMessageIds)
+            .flatMap(mimeMessageId -> executor.executeVoid(deleteOne.bind()
+                .setString(USERNAME, username.asString())
+                .setString(MIME_MESSAGE_ID, mimeMessageId.getValue())));
+    }
+
+    public Pair<Optional<Subject>, ThreadId> readRow(Row row) {
+        return Pair.of(Optional.ofNullable(row.getString(BASE_SUBJECT)).map(Subject::new),
+            ThreadId.fromBaseMessageId(CassandraMessageId.Factory.of(row.getUUID(THREAD_ID))));
+    }
+
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraThreadModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraThreadModule.java
new file mode 100644
index 0000000..f75bec3
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraThreadModule.java
@@ -0,0 +1,45 @@
+/******************************************************************
+ * 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.text;
+import static com.datastax.driver.core.DataType.timeuuid;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIdTable.THREAD_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIds.MESSAGE_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.BASE_SUBJECT;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.MIME_MESSAGE_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraThreadTable.USERNAME;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+
+public interface CassandraThreadModule {
+    CassandraModule MODULE = CassandraModule.builder()
+        .table(TABLE_NAME)
+        .comment("Related data needed for guessing threadId algorithm")
+        .statement(statement -> statement
+            .addPartitionKey(USERNAME, text())
+            .addPartitionKey(MIME_MESSAGE_ID, text())
+            .addClusteringColumn(MESSAGE_ID, timeuuid())
+            .addColumn(THREAD_ID, timeuuid())
+            .addColumn(BASE_SUBJECT, text()))
+        .build();
+
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraThreadTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraThreadTable.java
new file mode 100644
index 0000000..0db322a
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraThreadTable.java
@@ -0,0 +1,35 @@
+/******************************************************************
+ * 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;
+
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIdTable.THREAD_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageIds.MESSAGE_ID;
+
+public interface CassandraThreadTable {
+    String TABLE_NAME = "threadTable";
+
+    String USERNAME = "username";
+
+    String MIME_MESSAGE_ID = "mimeMessageId";
+
+    String BASE_SUBJECT = "baseSubject";
+
+    String[] FIELDS = {MESSAGE_ID, THREAD_ID, USERNAME, MIME_MESSAGE_ID, BASE_SUBJECT};
+}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAOTest.java
new file mode 100644
index 0000000..1eee881
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraThreadDAOTest.java
@@ -0,0 +1,190 @@
+/******************************************************************
+ * 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.AssertionsForClassTypes.assertThat;
+
+import java.util.Optional;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.modules.CassandraThreadModule;
+import org.apache.james.mailbox.model.ThreadId;
+import org.apache.james.mailbox.store.mail.model.MimeMessageId;
+import org.apache.james.mailbox.store.mail.model.Subject;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+public class CassandraThreadDAOTest {
+    private static final Username ALICE = Username.of("alice");
+    private static final Username BOB = Username.of("bob");
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(CassandraThreadModule.MODULE);
+
+    private CassandraThreadDAO testee;
+    private CassandraMessageId messageId1;
+    private CassandraMessageId messageId2;
+    private CassandraMessageId messageId3;
+    private ThreadId threadId1;
+    private ThreadId threadId2;
+    private MimeMessageId mimeMessageId1;
+    private MimeMessageId mimeMessageId2;
+    private MimeMessageId mimeMessageId3;
+    private MimeMessageId mimeMessageId4;
+    private MimeMessageId mimeMessageId5;
+
+    @BeforeEach
+    void setUp(CassandraCluster cassandra) {
+        testee = new CassandraThreadDAO(cassandra.getConf());
+
+        CassandraMessageId.Factory messageIdFactory = new CassandraMessageId.Factory();
+        messageId1 = messageIdFactory.generate();
+        messageId2 = messageIdFactory.generate();
+        messageId3 = messageIdFactory.generate();
+
+        threadId1 = ThreadId.fromBaseMessageId(messageId1);
+        threadId2 = ThreadId.fromBaseMessageId(messageId3);
+
+        mimeMessageId1 = new MimeMessageId("MimeMessageID1");
+        mimeMessageId2 = new MimeMessageId("MimeMessageID2");
+        mimeMessageId3 = new MimeMessageId("MimeMessageID3");
+        mimeMessageId4 = new MimeMessageId("MimeMessageID4");
+        mimeMessageId5 = new MimeMessageId("MimeMessageID5");
+    }
+
+    @Test
+    void insertShouldSuccess() {
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(message1BaseSubject, threadId1)));
+    }
+
+    @Test
+    void insertNullBaseSubjectShouldBeAllowed() {
+        Optional<Subject> message1BaseSubject = Optional.empty();
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(Optional.empty(), threadId1)));
+    }
+
+    @Test
+    void insertEmptyBaseSubjectShouldBeAllowed() {
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject(""));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(message1BaseSubject, threadId1)));
+    }
+
+    @Test
+    void selectShouldReturnEmptyByDefault() {
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1)).collectList().block()
+            .isEmpty());
+    }
+
+    @Test
+    void selectShouldReturnDistinctValues() {
+        Optional<Subject> messageBaseSubject = Optional.of(new Subject("subject"));
+
+        // given message1 and message2 belongs to same thread, related to each other by mimeMessageId2, mimeMessageId3
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2, mimeMessageId3), messageId1, threadId1, messageBaseSubject).collectList().block();
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId2, mimeMessageId3, mimeMessageId4), messageId2, threadId1, messageBaseSubject).collectList().block();
+
+        // select with new message having mimeMessageId2 and mimeMessageId3
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId2, mimeMessageId3)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(messageBaseSubject, threadId1)));
+    }
+
+    @Test
+    void selectShouldReturnOnlyRelatedMessageDataOfAUser() {
+        // insert message1 data of ALICE
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        // insert message2 data of BOB
+        Optional<Subject> message2BaseSubject = Optional.of(new Subject("subject2"));
+        testee.insertSome(BOB, ImmutableSet.of(mimeMessageId2), messageId2, threadId2, message2BaseSubject).collectList().block();
+
+        // select some data of BOB
+        assertThat(testee.selectSome(BOB, ImmutableSet.of(mimeMessageId2)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(message2BaseSubject, threadId2)));
+    }
+
+    @Test
+    void selectShouldReturnOnlyRelatedMessageDataOfAThread() {
+        // insert message1 data of ALICE which in thread1
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        // insert message2 data of ALICE which in thread2
+        Optional<Subject> message2BaseSubject = Optional.of(new Subject("subject2"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId2), messageId2, threadId2, message2BaseSubject).collectList().block();
+
+        // select some data related to thread2
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId2)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(message2BaseSubject, threadId2)));
+    }
+
+    @Test
+    void selectWithUnrelatedMimeMessageIDsShouldReturnEmpty() {
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId3, mimeMessageId4, mimeMessageId5)).collectList().block())
+            .isEqualTo(ImmutableList.of());
+    }
+
+    @Test
+    void deletedEntriesShouldNotBeReturned() {
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        testee.deleteSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2));
+
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2)).collectList().block()
+            .isEmpty());
+    }
+
+    @Test
+    void deleteWithUnrelatedMimeMessageIDsShouldDeleteNothing() {
+        // insert message1 data
+        Optional<Subject> message1BaseSubject = Optional.of(new Subject("subject"));
+        testee.insertSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2), messageId1, threadId1, message1BaseSubject).collectList().block();
+
+        // delete with unrelated mimemessageIds
+        testee.deleteSome(ALICE, ImmutableSet.of(mimeMessageId3, mimeMessageId4, mimeMessageId5));
+
+        // alice's data should remain
+        assertThat(testee.selectSome(ALICE, ImmutableSet.of(mimeMessageId1, mimeMessageId2)).collectList().block())
+            .isEqualTo(ImmutableList.of(Pair.of(message1BaseSubject, threadId1)));
+    }
+
+}
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 0c94c90..e1140b1 100644
--- a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -129,6 +129,7 @@ public class CassandraMailboxModule extends AbstractModule {
         install(new DefaultEventModule());
         install(new CassandraQuotaModule());
         install(new CassandraDeadLetterModule());
+        install(new CassandraThreadIdGuessingModule());
 
         bind(CassandraApplicableFlagDAO.class).in(Scopes.SINGLETON);
         bind(CassandraAttachmentDAOV2.class).in(Scopes.SINGLETON);
diff --git a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraThreadIdGuessingModule.java b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraThreadIdGuessingModule.java
new file mode 100644
index 0000000..6461f06
--- /dev/null
+++ b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/mailbox/CassandraThreadIdGuessingModule.java
@@ -0,0 +1,39 @@
+/******************************************************************
+ * 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.modules.mailbox;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.mailbox.cassandra.mail.CassandraThreadDAO;
+import org.apache.james.mailbox.cassandra.modules.CassandraThreadModule;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import com.google.inject.multibindings.Multibinder;
+
+public class CassandraThreadIdGuessingModule extends AbstractModule {
+    @Override
+    protected void configure() {
+        bind(CassandraThreadDAO.class).in(Scopes.SINGLETON);
+
+        Multibinder.newSetBinder(binder(), CassandraModule.class)
+            .addBinding()
+            .toInstance(CassandraThreadModule.MODULE);
+    }
+}

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