You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ma...@apache.org on 2018/05/28 15:43:11 UTC
[2/7] james-project git commit: MAILBOX-339 Introduce
CassandraMailboxPathDAO interface
MAILBOX-339 Introduce CassandraMailboxPathDAO interface
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/2fae24a8
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/2fae24a8
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/2fae24a8
Branch: refs/heads/master
Commit: 2fae24a82c9d453bdb2556257cd9afbb96e56afd
Parents: f8b587a
Author: Antoine Duprat <ad...@linagora.com>
Authored: Mon May 14 15:18:07 2018 +0200
Committer: Matthieu Baechler <ma...@apache.org>
Committed: Mon May 28 17:38:00 2018 +0200
----------------------------------------------------------------------
.../CassandraMailboxSessionMapperFactory.java | 6 +-
.../cassandra/mail/CassandraIdAndPath.java | 58 ++++++
.../cassandra/mail/CassandraMailboxMapper.java | 8 +-
.../cassandra/mail/CassandraMailboxPathDAO.java | 187 +------------------
.../mail/CassandraMailboxPathDAOImpl.java | 185 ++++++++++++++++++
.../CassandraSubscriptionManagerTest.java | 4 +-
...estCassandraMailboxSessionMapperFactory.java | 4 +-
.../CassandraMailboxMapperConcurrencyTest.java | 2 +-
.../mail/CassandraMailboxMapperTest.java | 4 +-
.../mail/CassandraMailboxPathDAOImplTest.java | 28 +++
.../mail/CassandraMailboxPathDAOTest.java | 10 +-
.../modules/mailbox/CassandraMailboxModule.java | 4 +-
12 files changed, 298 insertions(+), 202 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/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 f25c738..60ee504 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
@@ -39,7 +39,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraIndexTableHandler;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
-import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
@@ -79,7 +79,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
private final CassandraIndexTableHandler indexTableHandler;
private final CassandraMailboxDAO mailboxDAO;
- private final CassandraMailboxPathDAO mailboxPathDAO;
+ private final CassandraMailboxPathDAOImpl mailboxPathDAO;
private final CassandraFirstUnseenDAO firstUnseenDAO;
private final CassandraApplicableFlagDAO applicableFlagDAO;
private final CassandraAttachmentDAO attachmentDAO;
@@ -98,7 +98,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
CassandraMessageDAO messageDAO,
CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, CassandraMailboxDAO mailboxDAO,
- CassandraMailboxPathDAO mailboxPathDAO, CassandraFirstUnseenDAO firstUnseenDAO, CassandraApplicableFlagDAO applicableFlagDAO,
+ CassandraMailboxPathDAOImpl mailboxPathDAO, CassandraFirstUnseenDAO firstUnseenDAO, CassandraApplicableFlagDAO applicableFlagDAO,
CassandraAttachmentDAO attachmentDAO, CassandraAttachmentDAOV2 attachmentDAOV2, CassandraDeletedMessageDAO deletedMessageDAO,
ObjectStore objectStore, CassandraAttachmentMessageIdDAO attachmentMessageIdDAO,
CassandraAttachmentOwnerDAO ownerDAO, CassandraACLMapper aclMapper,
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIdAndPath.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIdAndPath.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIdAndPath.java
new file mode 100644
index 0000000..e476b02
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIdAndPath.java
@@ -0,0 +1,58 @@
+/****************************************************************
+ * 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.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.model.MailboxPath;
+
+import com.google.common.base.Objects;
+
+public class CassandraIdAndPath {
+ private final CassandraId cassandraId;
+ private final MailboxPath mailboxPath;
+
+ public CassandraIdAndPath(CassandraId cassandraId, MailboxPath mailboxPath) {
+ this.cassandraId = cassandraId;
+ this.mailboxPath = mailboxPath;
+ }
+
+ public CassandraId getCassandraId() {
+ return cassandraId;
+ }
+
+ public MailboxPath getMailboxPath() {
+ return mailboxPath;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof CassandraIdAndPath) {
+ CassandraIdAndPath that = (CassandraIdAndPath) o;
+
+ return Objects.equal(this.cassandraId, that.cassandraId)
+ && Objects.equal(this.mailboxPath, that.mailboxPath);
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hashCode(cassandraId, mailboxPath);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
index 865938b..8fe6f26 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
@@ -65,13 +65,13 @@ public class CassandraMailboxMapper implements MailboxMapper {
public static final String CLUSTERING_COLUMNS_IS_TOO_LONG = "The sum of all clustering columns is too long";
public static final Logger LOGGER = LoggerFactory.getLogger(CassandraMailboxMapper.class);
- private final CassandraMailboxPathDAO mailboxPathDAO;
+ private final CassandraMailboxPathDAOImpl mailboxPathDAO;
private final CassandraMailboxDAO mailboxDAO;
private final CassandraACLMapper cassandraACLMapper;
private final CassandraUserMailboxRightsDAO userMailboxRightsDAO;
@Inject
- public CassandraMailboxMapper(CassandraMailboxDAO mailboxDAO, CassandraMailboxPathDAO mailboxPathDAO, CassandraUserMailboxRightsDAO userMailboxRightsDAO, CassandraACLMapper aclMapper, CassandraConfiguration cassandraConfiguration) {
+ public CassandraMailboxMapper(CassandraMailboxDAO mailboxDAO, CassandraMailboxPathDAOImpl mailboxPathDAO, CassandraUserMailboxRightsDAO userMailboxRightsDAO, CassandraACLMapper aclMapper, CassandraConfiguration cassandraConfiguration) {
this.mailboxDAO = mailboxDAO;
this.mailboxPathDAO = mailboxPathDAO;
this.userMailboxRightsDAO = userMailboxRightsDAO;
@@ -92,7 +92,7 @@ public class CassandraMailboxMapper implements MailboxMapper {
return mailboxPathDAO.retrieveId(path)
.thenCompose(cassandraIdOptional ->
cassandraIdOptional
- .map(CassandraMailboxPathDAO.CassandraIdAndPath::getCassandraId)
+ .map(CassandraIdAndPath::getCassandraId)
.map(this::retrieveMailbox)
.orElse(CompletableFuture.completedFuture(Optional.empty())))
.join()
@@ -145,7 +145,7 @@ public class CassandraMailboxMapper implements MailboxMapper {
.collect(Guavate.toImmutableList());
}
- private CompletableFuture<Optional<SimpleMailbox>> retrieveMailbox(CassandraMailboxPathDAO.CassandraIdAndPath idAndPath) {
+ private CompletableFuture<Optional<SimpleMailbox>> retrieveMailbox(CassandraIdAndPath idAndPath) {
return retrieveMailbox(idAndPath.getCassandraId())
.thenApply(optional -> OptionalUtils.executeIfEmpty(optional,
() -> LOGGER.warn("Could not retrieve mailbox {} with path {} in mailbox table.", idAndPath.getCassandraId(), idAndPath.getMailboxPath())));
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAO.java
index 8a06811..33fac3c 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAO.java
@@ -19,198 +19,23 @@
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.GhostMailbox.TYPE;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.FIELDS;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.MAILBOX_ID;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.MAILBOX_NAME;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.NAMESPACE_AND_USER;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.TABLE_NAME;
-
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
-import javax.inject.Inject;
-
-import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
-import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
-import org.apache.james.backends.cassandra.utils.CassandraUtils;
-import org.apache.james.mailbox.cassandra.GhostMailbox;
import org.apache.james.mailbox.cassandra.ids.CassandraId;
-import org.apache.james.mailbox.cassandra.mail.utils.MailboxBaseTupleUtil;
-import org.apache.james.mailbox.cassandra.table.CassandraMailboxTable;
import org.apache.james.mailbox.model.MailboxPath;
-import com.datastax.driver.core.PreparedStatement;
-import com.datastax.driver.core.Row;
-import com.datastax.driver.core.Session;
-import com.datastax.driver.core.querybuilder.QueryBuilder;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-
-public class CassandraMailboxPathDAO {
-
- public static class CassandraIdAndPath {
- private final CassandraId cassandraId;
- private final MailboxPath mailboxPath;
-
- public CassandraIdAndPath(CassandraId cassandraId, MailboxPath mailboxPath) {
- this.cassandraId = cassandraId;
- this.mailboxPath = mailboxPath;
- }
-
- public CassandraId getCassandraId() {
- return cassandraId;
- }
-
- public MailboxPath getMailboxPath() {
- return mailboxPath;
- }
-
- @Override
- public final boolean equals(Object o) {
- if (o instanceof CassandraIdAndPath) {
- CassandraIdAndPath that = (CassandraIdAndPath) o;
-
- return Objects.equal(this.cassandraId, that.cassandraId)
- && Objects.equal(this.mailboxPath, that.mailboxPath);
- }
- return false;
- }
-
- @Override
- public final int hashCode() {
- return Objects.hashCode(cassandraId, mailboxPath);
- }
- }
-
- private final CassandraAsyncExecutor cassandraAsyncExecutor;
- private final MailboxBaseTupleUtil mailboxBaseTupleUtil;
- private final CassandraUtils cassandraUtils;
- private final PreparedStatement delete;
- private final PreparedStatement insert;
- private final PreparedStatement select;
- private final PreparedStatement selectAll;
-
- @Inject
- public CassandraMailboxPathDAO(Session session, CassandraTypesProvider typesProvider, CassandraUtils cassandraUtils) {
- this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
- this.mailboxBaseTupleUtil = new MailboxBaseTupleUtil(typesProvider);
- this.cassandraUtils = cassandraUtils;
- this.insert = prepareInsert(session);
- this.delete = prepareDelete(session);
- this.select = prepareSelect(session);
- this.selectAll = prepareSelectAll(session);
- }
-
- @VisibleForTesting
- public CassandraMailboxPathDAO(Session session, CassandraTypesProvider typesProvider) {
- this(session, typesProvider, CassandraUtils.WITH_DEFAULT_CONFIGURATION);
- }
-
- private PreparedStatement prepareDelete(Session session) {
- return session.prepare(QueryBuilder.delete()
- .from(TABLE_NAME)
- .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER)))
- .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME))));
- }
-
- private PreparedStatement prepareInsert(Session session) {
- return session.prepare(insertInto(TABLE_NAME)
- .value(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER))
- .value(MAILBOX_NAME, bindMarker(MAILBOX_NAME))
- .value(MAILBOX_ID, bindMarker(MAILBOX_ID))
- .ifNotExists());
- }
-
- private PreparedStatement prepareSelect(Session session) {
- return session.prepare(select(FIELDS)
- .from(TABLE_NAME)
- .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER)))
- .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME))));
- }
-
- private PreparedStatement prepareSelectAll(Session session) {
- return session.prepare(select(FIELDS)
- .from(TABLE_NAME)
- .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER))));
- }
-
- public CompletableFuture<Optional<CassandraIdAndPath>> retrieveId(MailboxPath mailboxPath) {
- return cassandraAsyncExecutor.executeSingleRow(
- select.bind()
- .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
- .setString(MAILBOX_NAME, mailboxPath.getName()))
- .thenApply(rowOptional ->
- rowOptional.map(this::fromRowToCassandraIdAndPath))
- .thenApply(value -> logGhostMailbox(mailboxPath, value));
- }
-
- public CompletableFuture<Stream<CassandraIdAndPath>> listUserMailboxes(String namespace, String user) {
- return cassandraAsyncExecutor.execute(
- selectAll.bind()
- .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(namespace, user)))
- .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
- .map(this::fromRowToCassandraIdAndPath)
- .peek(this::logReadSuccess));
- }
+public interface CassandraMailboxPathDAO {
- /**
- * See https://issues.apache.org/jira/browse/MAILBOX-322 to read about the Ghost mailbox bug.
- *
- * A missed read on an existing mailbox is the cause of the ghost mailbox bug. Here we log missing reads. Successful
- * reads and write operations are also added in order to allow audit in order to know if the mailbox existed.
- */
- public Optional<CassandraIdAndPath> logGhostMailbox(MailboxPath mailboxPath, Optional<CassandraIdAndPath> value) {
- if (value.isPresent()) {
- CassandraIdAndPath cassandraIdAndPath = value.get();
- logReadSuccess(cassandraIdAndPath);
- } else {
- GhostMailbox.logger()
- .addField(GhostMailbox.MAILBOX_NAME, mailboxPath)
- .addField(TYPE, "readMiss")
- .log(logger -> logger.info("Read mailbox missed"));
- }
- return value;
- }
+ CompletableFuture<Optional<CassandraIdAndPath>> retrieveId(MailboxPath mailboxPath);
- /**
- * See https://issues.apache.org/jira/browse/MAILBOX-322 to read about the Ghost mailbox bug.
- *
- * Read success allows to know if a mailbox existed before (mailbox write history might be older than this log introduction
- * or log history might have been dropped)
- */
- private void logReadSuccess(CassandraIdAndPath cassandraIdAndPath) {
- GhostMailbox.logger()
- .addField(GhostMailbox.MAILBOX_NAME, cassandraIdAndPath.getMailboxPath())
- .addField(TYPE, "readSuccess")
- .addField(GhostMailbox.MAILBOX_ID, cassandraIdAndPath.getCassandraId())
- .log(logger -> logger.info("Read mailbox succeeded"));
- }
+ CompletableFuture<Stream<CassandraIdAndPath>> listUserMailboxes(String namespace, String user);
- private CassandraIdAndPath fromRowToCassandraIdAndPath(Row row) {
- return new CassandraIdAndPath(
- CassandraId.of(row.getUUID(MAILBOX_ID)),
- new MailboxPath(row.getUDTValue(NAMESPACE_AND_USER).getString(CassandraMailboxTable.MailboxBase.NAMESPACE),
- row.getUDTValue(NAMESPACE_AND_USER).getString(CassandraMailboxTable.MailboxBase.USER),
- row.getString(MAILBOX_NAME)));
- }
+ Optional<CassandraIdAndPath> logGhostMailbox(MailboxPath mailboxPath, Optional<CassandraIdAndPath> value);
- public CompletableFuture<Boolean> save(MailboxPath mailboxPath, CassandraId mailboxId) {
- return cassandraAsyncExecutor.executeReturnApplied(insert.bind()
- .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
- .setString(MAILBOX_NAME, mailboxPath.getName())
- .setUUID(MAILBOX_ID, mailboxId.asUuid()));
- }
+ CompletableFuture<Boolean> save(MailboxPath mailboxPath, CassandraId mailboxId);
- public CompletableFuture<Void> delete(MailboxPath mailboxPath) {
- return cassandraAsyncExecutor.executeVoid(delete.bind()
- .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
- .setString(MAILBOX_NAME, mailboxPath.getName()));
- }
+ CompletableFuture<Void> delete(MailboxPath mailboxPath);
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
new file mode 100644
index 0000000..11080e2
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImpl.java
@@ -0,0 +1,185 @@
+/****************************************************************
+ * 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.GhostMailbox.TYPE;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.FIELDS;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.MAILBOX_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.MAILBOX_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.NAMESPACE_AND_USER;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable.TABLE_NAME;
+
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.cassandra.GhostMailbox;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.mail.utils.MailboxBaseTupleUtil;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxTable;
+import org.apache.james.mailbox.model.MailboxPath;
+
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.google.common.annotations.VisibleForTesting;
+
+public class CassandraMailboxPathDAOImpl implements CassandraMailboxPathDAO {
+
+ private final CassandraAsyncExecutor cassandraAsyncExecutor;
+ private final MailboxBaseTupleUtil mailboxBaseTupleUtil;
+ private final CassandraUtils cassandraUtils;
+ private final PreparedStatement delete;
+ private final PreparedStatement insert;
+ private final PreparedStatement select;
+ private final PreparedStatement selectAll;
+
+ @Inject
+ public CassandraMailboxPathDAOImpl(Session session, CassandraTypesProvider typesProvider, CassandraUtils cassandraUtils) {
+ this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+ this.mailboxBaseTupleUtil = new MailboxBaseTupleUtil(typesProvider);
+ this.cassandraUtils = cassandraUtils;
+ this.insert = prepareInsert(session);
+ this.delete = prepareDelete(session);
+ this.select = prepareSelect(session);
+ this.selectAll = prepareSelectAll(session);
+ }
+
+ @VisibleForTesting
+ public CassandraMailboxPathDAOImpl(Session session, CassandraTypesProvider typesProvider) {
+ this(session, typesProvider, CassandraUtils.WITH_DEFAULT_CONFIGURATION);
+ }
+
+ private PreparedStatement prepareDelete(Session session) {
+ return session.prepare(QueryBuilder.delete()
+ .from(TABLE_NAME)
+ .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER)))
+ .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME))));
+ }
+
+ private PreparedStatement prepareInsert(Session session) {
+ return session.prepare(insertInto(TABLE_NAME)
+ .value(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER))
+ .value(MAILBOX_NAME, bindMarker(MAILBOX_NAME))
+ .value(MAILBOX_ID, bindMarker(MAILBOX_ID))
+ .ifNotExists());
+ }
+
+ private PreparedStatement prepareSelect(Session session) {
+ return session.prepare(select(FIELDS)
+ .from(TABLE_NAME)
+ .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER)))
+ .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME))));
+ }
+
+ private PreparedStatement prepareSelectAll(Session session) {
+ return session.prepare(select(FIELDS)
+ .from(TABLE_NAME)
+ .where(eq(NAMESPACE_AND_USER, bindMarker(NAMESPACE_AND_USER))));
+ }
+
+ public CompletableFuture<Optional<CassandraIdAndPath>> retrieveId(MailboxPath mailboxPath) {
+ return cassandraAsyncExecutor.executeSingleRow(
+ select.bind()
+ .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
+ .setString(MAILBOX_NAME, mailboxPath.getName()))
+ .thenApply(rowOptional ->
+ rowOptional.map(this::fromRowToCassandraIdAndPath))
+ .thenApply(value -> logGhostMailbox(mailboxPath, value));
+ }
+
+ @Override
+ public CompletableFuture<Stream<CassandraIdAndPath>> listUserMailboxes(String namespace, String user) {
+ return cassandraAsyncExecutor.execute(
+ selectAll.bind()
+ .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(namespace, user)))
+ .thenApply(resultSet -> cassandraUtils.convertToStream(resultSet)
+ .map(this::fromRowToCassandraIdAndPath)
+ .peek(this::logReadSuccess));
+ }
+
+ /**
+ * See https://issues.apache.org/jira/browse/MAILBOX-322 to read about the Ghost mailbox bug.
+ *
+ * A missed read on an existing mailbox is the cause of the ghost mailbox bug. Here we log missing reads. Successful
+ * reads and write operations are also added in order to allow audit in order to know if the mailbox existed.
+ */
+ @Override
+ public Optional<CassandraIdAndPath> logGhostMailbox(MailboxPath mailboxPath, Optional<CassandraIdAndPath> value) {
+ if (value.isPresent()) {
+ CassandraIdAndPath cassandraIdAndPath = value.get();
+ logReadSuccess(cassandraIdAndPath);
+ } else {
+ GhostMailbox.logger()
+ .addField(GhostMailbox.MAILBOX_NAME, mailboxPath)
+ .addField(TYPE, "readMiss")
+ .log(logger -> logger.info("Read mailbox missed"));
+ }
+ return value;
+ }
+
+ /**
+ * See https://issues.apache.org/jira/browse/MAILBOX-322 to read about the Ghost mailbox bug.
+ *
+ * Read success allows to know if a mailbox existed before (mailbox write history might be older than this log introduction
+ * or log history might have been dropped)
+ */
+ private void logReadSuccess(CassandraIdAndPath cassandraIdAndPath) {
+ GhostMailbox.logger()
+ .addField(GhostMailbox.MAILBOX_NAME, cassandraIdAndPath.getMailboxPath())
+ .addField(TYPE, "readSuccess")
+ .addField(GhostMailbox.MAILBOX_ID, cassandraIdAndPath.getCassandraId())
+ .log(logger -> logger.info("Read mailbox succeeded"));
+ }
+
+ private CassandraIdAndPath fromRowToCassandraIdAndPath(Row row) {
+ return new CassandraIdAndPath(
+ CassandraId.of(row.getUUID(MAILBOX_ID)),
+ new MailboxPath(row.getUDTValue(NAMESPACE_AND_USER).getString(CassandraMailboxTable.MailboxBase.NAMESPACE),
+ row.getUDTValue(NAMESPACE_AND_USER).getString(CassandraMailboxTable.MailboxBase.USER),
+ row.getString(MAILBOX_NAME)));
+ }
+
+ @Override
+ public CompletableFuture<Boolean> save(MailboxPath mailboxPath, CassandraId mailboxId) {
+ return cassandraAsyncExecutor.executeReturnApplied(insert.bind()
+ .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
+ .setString(MAILBOX_NAME, mailboxPath.getName())
+ .setUUID(MAILBOX_ID, mailboxId.asUuid()));
+ }
+
+ @Override
+ public CompletableFuture<Void> delete(MailboxPath mailboxPath) {
+ return cassandraAsyncExecutor.executeVoid(delete.bind()
+ .setUDTValue(NAMESPACE_AND_USER, mailboxBaseTupleUtil.createMailboxBaseUDT(mailboxPath.getNamespace(), mailboxPath.getUser()))
+ .setString(MAILBOX_NAME, mailboxPath.getName()));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/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 1222958..29ec40f 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
@@ -37,7 +37,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
-import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
@@ -88,7 +88,7 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
CassandraMailboxCounterDAO mailboxCounterDAO = null;
CassandraMailboxRecentsDAO mailboxRecentsDAO = null;
CassandraMailboxDAO mailboxDAO = null;
- CassandraMailboxPathDAO mailboxPathDAO = null;
+ CassandraMailboxPathDAOImpl mailboxPathDAO = null;
CassandraFirstUnseenDAO firstUnseenDAO = null;
CassandraApplicableFlagDAO applicableFlagDAO = null;
CassandraAttachmentDAO attachmentDAO = null;
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
index ec7ac05..d26350a 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/TestCassandraMailboxSessionMapperFactory.java
@@ -35,7 +35,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraDeletedMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
-import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
@@ -70,7 +70,7 @@ public class TestCassandraMailboxSessionMapperFactory {
new CassandraMailboxCounterDAO(session),
new CassandraMailboxRecentsDAO(session),
new CassandraMailboxDAO(session, typesProvider),
- new CassandraMailboxPathDAO(session, typesProvider),
+ new CassandraMailboxPathDAOImpl(session, typesProvider),
new CassandraFirstUnseenDAO(session),
new CassandraApplicableFlagDAO(session),
new CassandraAttachmentDAO(session, utils, cassandraConfiguration),
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperConcurrencyTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperConcurrencyTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperConcurrencyTest.java
index 6d7822b..3d2bd73 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperConcurrencyTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperConcurrencyTest.java
@@ -57,7 +57,7 @@ public class CassandraMailboxMapperConcurrencyTest {
CassandraModuleComposite modules = new CassandraModuleComposite(new CassandraMailboxModule(), new CassandraAclModule());
cassandra = CassandraCluster.create(modules, cassandraServer.getIp(), cassandraServer.getBindingPort());
CassandraMailboxDAO mailboxDAO = new CassandraMailboxDAO(cassandra.getConf(), cassandra.getTypesProvider());
- CassandraMailboxPathDAO mailboxPathDAO = new CassandraMailboxPathDAO(cassandra.getConf(), cassandra.getTypesProvider());
+ CassandraMailboxPathDAOImpl mailboxPathDAO = new CassandraMailboxPathDAOImpl(cassandra.getConf(), cassandra.getTypesProvider());
CassandraUserMailboxRightsDAO userMailboxRightsDAO = new CassandraUserMailboxRightsDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
testee = new CassandraMailboxMapper(
mailboxDAO,
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
index e761b7e..350c523 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
@@ -47,7 +47,7 @@ public class CassandraMailboxMapperTest {
@ClassRule public static DockerCassandraRule cassandraServer = new DockerCassandraRule();
private CassandraCluster cassandra;
- private CassandraMailboxPathDAO mailboxPathDAO;
+ private CassandraMailboxPathDAOImpl mailboxPathDAO;
private CassandraMailboxMapper testee;
@Before
@@ -55,7 +55,7 @@ public class CassandraMailboxMapperTest {
CassandraModuleComposite modules = new CassandraModuleComposite(new CassandraMailboxModule(), new CassandraAclModule());
cassandra = CassandraCluster.create(modules, cassandraServer.getIp(), cassandraServer.getBindingPort());
CassandraMailboxDAO mailboxDAO = new CassandraMailboxDAO(cassandra.getConf(), cassandra.getTypesProvider());
- mailboxPathDAO = new CassandraMailboxPathDAO(cassandra.getConf(), cassandra.getTypesProvider());
+ mailboxPathDAO = new CassandraMailboxPathDAOImpl(cassandra.getConf(), cassandra.getTypesProvider());
CassandraUserMailboxRightsDAO userMailboxRightsDAO = new CassandraUserMailboxRightsDAO(cassandra.getConf(), CassandraUtils.WITH_DEFAULT_CONFIGURATION);
testee = new CassandraMailboxMapper(
mailboxDAO,
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImplTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImplTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImplTest.java
new file mode 100644
index 0000000..c532bd7
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOImplTest.java
@@ -0,0 +1,28 @@
+/****************************************************************
+ * 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;
+
+public class CassandraMailboxPathDAOImplTest extends CassandraMailboxPathDAOTest {
+
+ @Override
+ CassandraMailboxPathDAO testee() {
+ return new CassandraMailboxPathDAOImpl(cassandra.getConf(), cassandra.getTypesProvider());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOTest.java
index ce79b44..a88f1d6 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathDAOTest.java
@@ -26,7 +26,6 @@ import java.util.List;
import org.apache.james.backends.cassandra.CassandraCluster;
import org.apache.james.backends.cassandra.DockerCassandraRule;
import org.apache.james.mailbox.cassandra.ids.CassandraId;
-import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAO.CassandraIdAndPath;
import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
import org.apache.james.mailbox.model.MailboxPath;
import org.junit.After;
@@ -38,7 +37,7 @@ import com.github.steveash.guavate.Guavate;
import nl.jqno.equalsverifier.EqualsVerifier;
-public class CassandraMailboxPathDAOTest {
+public abstract class CassandraMailboxPathDAOTest {
private static final String USER = "user";
private static final String OTHER_USER = "other";
private static final CassandraId INBOX_ID = CassandraId.timeBased();
@@ -52,14 +51,15 @@ public class CassandraMailboxPathDAOTest {
@ClassRule public static DockerCassandraRule cassandraServer = new DockerCassandraRule();
- private CassandraCluster cassandra;
+ protected CassandraCluster cassandra;
private CassandraMailboxPathDAO testee;
+ abstract CassandraMailboxPathDAO testee();
@Before
public void setUp() throws Exception {
cassandra = CassandraCluster.create(new CassandraMailboxModule(), cassandraServer.getIp(), cassandraServer.getBindingPort());
- testee = new CassandraMailboxPathDAO(cassandra.getConf(), cassandra.getTypesProvider());
+ testee = testee();
}
@@ -70,7 +70,7 @@ public class CassandraMailboxPathDAOTest {
@Test
public void cassandraIdAndPathShouldRespectBeanContract() {
- EqualsVerifier.forClass(CassandraMailboxPathDAO.CassandraIdAndPath.class).verify();
+ EqualsVerifier.forClass(CassandraIdAndPath.class).verify();
}
@Test
http://git-wip-us.apache.org/repos/asf/james-project/blob/2fae24a8/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 69a44fc..f63ba6d 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -46,7 +46,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraFirstUnseenDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
-import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxPathDAOImpl;
import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
@@ -103,7 +103,7 @@ public class CassandraMailboxModule extends AbstractModule {
bind(CassandraFirstUnseenDAO.class).in(Scopes.SINGLETON);
bind(CassandraMailboxCounterDAO.class).in(Scopes.SINGLETON);
bind(CassandraMailboxDAO.class).in(Scopes.SINGLETON);
- bind(CassandraMailboxPathDAO.class).in(Scopes.SINGLETON);
+ bind(CassandraMailboxPathDAOImpl.class).in(Scopes.SINGLETON);
bind(CassandraMailboxRecentsDAO.class).in(Scopes.SINGLETON);
bind(CassandraMessageDAO.class).in(Scopes.SINGLETON);
bind(CassandraMessageIdDAO.class).in(Scopes.SINGLETON);
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org