You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2020/10/14 02:31:35 UTC

[james-project] 09/22: JAMES-3409 CassandraMailboxPathV3DAO (with UidValidity)

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

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

commit 33da147943ae0a5631841fea7e75ab242251b6c6
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Jul 29 16:48:50 2020 +0700

    JAMES-3409 CassandraMailboxPathV3DAO (with UidValidity)
---
 .../cassandra/mail/CassandraMailboxPathV3DAO.java  | 215 +++++++++++++++++++++
 .../cassandra/modules/CassandraMailboxModule.java  |  13 ++
 .../table/CassandraMailboxPathV3Table.java}        |  34 ++--
 .../mail/CassandraMailboxPathV3DAOTest.java        | 153 +++++++++++++++
 .../mailbox/cassandra/mail/MailboxFixture.java     |   6 +
 5 files changed, 404 insertions(+), 17 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAO.java
new file mode 100644
index 0000000..c07c702
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAO.java
@@ -0,0 +1,215 @@
+/****************************************************************
+ * 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.CassandraMailboxPathV3Table.FIELDS;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.MAILBOX_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.MAILBOX_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.NAMESPACE;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.UIDVALIDITY;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table.USER;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.init.configuration.CassandraConsistenciesConfiguration;
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.cassandra.GhostMailbox;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UidValidity;
+import org.apache.james.util.FunctionalUtils;
+import org.apache.james.util.ReactorUtils;
+
+import com.datastax.driver.core.ConsistencyLevel;
+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 reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class CassandraMailboxPathV3DAO {
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final CassandraUtils cassandraUtils;
+    private final PreparedStatement delete;
+    private final PreparedStatement insert;
+    private final PreparedStatement select;
+    private final PreparedStatement selectUser;
+    private final PreparedStatement selectAll;
+    private final ConsistencyLevel consistencyLevel;
+
+    @Inject
+    public CassandraMailboxPathV3DAO(Session session, CassandraUtils cassandraUtils,
+                                     CassandraConsistenciesConfiguration consistenciesConfiguration) {
+        this.cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        this.consistencyLevel = consistenciesConfiguration.getLightweightTransaction();
+        this.cassandraUtils = cassandraUtils;
+        this.insert = prepareInsert(session);
+        this.delete = prepareDelete(session);
+        this.select = prepareSelect(session);
+        this.selectUser = prepareSelectUser(session);
+        this.selectAll = prepareSelectAll(session);
+    }
+
+    private PreparedStatement prepareDelete(Session session) {
+        return session.prepare(QueryBuilder.delete()
+            .from(TABLE_NAME)
+            .where(eq(NAMESPACE, bindMarker(NAMESPACE)))
+            .and(eq(USER, bindMarker(USER)))
+            .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME)))
+            .ifExists());
+    }
+
+    private PreparedStatement prepareInsert(Session session) {
+        return session.prepare(insertInto(TABLE_NAME)
+            .value(NAMESPACE, bindMarker(NAMESPACE))
+            .value(USER, bindMarker(USER))
+            .value(MAILBOX_NAME, bindMarker(MAILBOX_NAME))
+            .value(MAILBOX_ID, bindMarker(MAILBOX_ID))
+            .value(UIDVALIDITY, bindMarker(UIDVALIDITY))
+            .ifNotExists());
+    }
+
+    private PreparedStatement prepareSelect(Session session) {
+        return session.prepare(select(FIELDS)
+            .from(TABLE_NAME)
+            .where(eq(NAMESPACE, bindMarker(NAMESPACE)))
+            .and(eq(USER, bindMarker(USER)))
+            .and(eq(MAILBOX_NAME, bindMarker(MAILBOX_NAME))));
+    }
+
+    private PreparedStatement prepareSelectUser(Session session) {
+        return session.prepare(select(FIELDS)
+            .from(TABLE_NAME)
+            .where(eq(NAMESPACE, bindMarker(NAMESPACE)))
+            .and(eq(USER, bindMarker(USER))));
+    }
+
+    private PreparedStatement prepareSelectAll(Session session) {
+        return session.prepare(select(FIELDS)
+            .from(TABLE_NAME));
+    }
+
+    public Mono<Mailbox> retrieve(MailboxPath mailboxPath) {
+        return cassandraAsyncExecutor.executeSingleRow(
+            select.bind()
+                .setString(NAMESPACE, mailboxPath.getNamespace())
+                .setString(USER, sanitizeUser(mailboxPath.getUser()))
+                .setString(MAILBOX_NAME, mailboxPath.getName())
+                .setConsistencyLevel(consistencyLevel))
+            .map(this::fromRowToCassandraIdAndPath)
+            .map(FunctionalUtils.toFunction(this::logGhostMailboxSuccess))
+            .switchIfEmpty(ReactorUtils.executeAndEmpty(() -> logGhostMailboxFailure(mailboxPath)));
+    }
+
+    public Flux<Mailbox> listUserMailboxes(String namespace, Username user) {
+        return cassandraAsyncExecutor.execute(
+            selectUser.bind()
+                .setString(NAMESPACE, namespace)
+                .setString(USER, sanitizeUser(user))
+                .setConsistencyLevel(consistencyLevel))
+            .flatMapMany(cassandraUtils::convertToFlux)
+            .map(this::fromRowToCassandraIdAndPath)
+            .map(FunctionalUtils.toFunction(this::logReadSuccess));
+    }
+
+    public Flux<Mailbox> listAll() {
+        return cassandraAsyncExecutor.execute(
+            selectAll.bind())
+            .flatMapMany(cassandraUtils::convertToFlux)
+            .map(this::fromRowToCassandraIdAndPath)
+            .map(FunctionalUtils.toFunction(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.
+     */
+    public void logGhostMailboxSuccess(Mailbox value) {
+        logReadSuccess(value);
+    }
+
+    public void logGhostMailboxFailure(MailboxPath mailboxPath) {
+        GhostMailbox.logger()
+                .addField(GhostMailbox.MAILBOX_NAME, mailboxPath)
+                .addField(TYPE, "readMiss")
+                .log(logger -> logger.debug("Read mailbox missed"));
+    }
+
+    /**
+     * 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(Mailbox mailbox) {
+        GhostMailbox.logger()
+            .addField(GhostMailbox.MAILBOX_NAME, mailbox.generateAssociatedPath())
+            .addField(TYPE, "readSuccess")
+            .addField(GhostMailbox.MAILBOX_ID, mailbox.getMailboxId())
+            .log(logger -> logger.debug("Read mailbox succeeded"));
+    }
+
+    private Mailbox fromRowToCassandraIdAndPath(Row row) {
+        return new Mailbox(
+            new MailboxPath(row.getString(NAMESPACE),
+                Username.of(row.getString(USER)),
+                row.getString(MAILBOX_NAME)),
+            UidValidity.of(row.getLong(UIDVALIDITY)),
+            CassandraId.of(row.getUUID(MAILBOX_ID)));
+    }
+
+    public Mono<Boolean> save(Mailbox mailbox) {
+        CassandraId id = (CassandraId) mailbox.getMailboxId();
+
+        return cassandraAsyncExecutor.executeReturnApplied(insert.bind()
+            .setString(NAMESPACE, mailbox.getNamespace())
+            .setString(USER, sanitizeUser(mailbox.getUser()))
+            .setLong(UIDVALIDITY, mailbox.getUidValidity().asLong())
+            .setString(MAILBOX_NAME, mailbox.getName())
+            .setUUID(MAILBOX_ID, id.asUuid()));
+    }
+
+    public Mono<Void> delete(MailboxPath mailboxPath) {
+        return cassandraAsyncExecutor.executeVoid(delete.bind()
+            .setString(NAMESPACE, mailboxPath.getNamespace())
+            .setString(USER, sanitizeUser(mailboxPath.getUser()))
+            .setString(MAILBOX_NAME, mailboxPath.getName()));
+    }
+
+    private String sanitizeUser(Username user) {
+        if (user == null) {
+            return "";
+        }
+        return user.asString();
+    }
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxModule.java
index d4341aa..62e9fb5 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxModule.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxModule.java
@@ -27,6 +27,7 @@ import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.utils.CassandraConstants;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxPathTable;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV2Table;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxPathV3Table;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxTable;
 
 import com.datastax.driver.core.schemabuilder.SchemaBuilder;
@@ -68,5 +69,17 @@ public interface CassandraMailboxModule {
             .addPartitionKey(CassandraMailboxPathV2Table.USER, text())
             .addClusteringColumn(CassandraMailboxPathV2Table.MAILBOX_NAME, text())
             .addColumn(CassandraMailboxPathV2Table.MAILBOX_ID, timeuuid()))
+        .table(CassandraMailboxPathV3Table.TABLE_NAME)
+        .comment("Denormalisation table. Allow to retrieve mailboxes belonging to a certain user. This is a " +
+            "LIST optimisation.")
+        .options(options -> options
+            .caching(SchemaBuilder.KeyCaching.ALL,
+                SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION)))
+        .statement(statement -> statement
+            .addPartitionKey(CassandraMailboxPathV3Table.NAMESPACE, text())
+            .addPartitionKey(CassandraMailboxPathV3Table.USER, text())
+            .addClusteringColumn(CassandraMailboxPathV3Table.MAILBOX_NAME, text())
+            .addColumn(CassandraMailboxPathV3Table.MAILBOX_ID, timeuuid())
+            .addColumn(CassandraMailboxPathV3Table.UIDVALIDITY, bigint()))
         .build();
 }
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxPathV3Table.java
similarity index 59%
copy from mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java
copy to mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxPathV3Table.java
index b9f29e8..078824f 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxPathV3Table.java
@@ -17,21 +17,21 @@
  * under the License.                                           *
  ****************************************************************/
 
-package org.apache.james.mailbox.cassandra.mail;
-
-import org.apache.james.core.Username;
-import org.apache.james.mailbox.cassandra.ids.CassandraId;
-import org.apache.james.mailbox.model.MailboxPath;
-
-public interface MailboxFixture {
-    Username USER = Username.of("user");
-    Username OTHER_USER = Username.of("other");
-
-    CassandraId INBOX_ID = CassandraId.timeBased();
-    CassandraId OUTBOX_ID = CassandraId.timeBased();
-    CassandraId otherMailboxId = CassandraId.timeBased();
-    MailboxPath USER_INBOX_MAILBOXPATH = MailboxPath.forUser(USER, "INBOX");
-    MailboxPath USER_OUTBOX_MAILBOXPATH = MailboxPath.forUser(USER, "OUTBOX");
-    MailboxPath OTHER_USER_MAILBOXPATH = MailboxPath.forUser(OTHER_USER, "INBOX");
-    CassandraIdAndPath INBOX_ID_AND_PATH = new CassandraIdAndPath(INBOX_ID, USER_INBOX_MAILBOXPATH);
+package org.apache.james.mailbox.cassandra.table;
+
+public interface CassandraMailboxPathV3Table {
+
+    String TABLE_NAME = "mailboxPathV3";
+
+    String NAMESPACE = "namespace";
+
+    String USER = "user";
+
+    String MAILBOX_NAME = "mailboxName";
+
+    String MAILBOX_ID = "mailboxId";
+    String UIDVALIDITY = "uidvalidity";
+
+    String[] FIELDS = { NAMESPACE, USER, MAILBOX_NAME, MAILBOX_ID, UIDVALIDITY};
+
 }
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAOTest.java
new file mode 100644
index 0000000..92d1aa9
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxPathV3DAOTest.java
@@ -0,0 +1,153 @@
+/****************************************************************
+ * 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.apache.james.mailbox.cassandra.mail.MailboxFixture.MAILBOX_1;
+import static org.apache.james.mailbox.cassandra.mail.MailboxFixture.MAILBOX_2;
+import static org.apache.james.mailbox.cassandra.mail.MailboxFixture.MAILBOX_3;
+import static org.apache.james.mailbox.cassandra.mail.MailboxFixture.USER_INBOX_MAILBOXPATH;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.model.Mailbox;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class CassandraMailboxPathV3DAOTest {
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(CassandraModule.aggregateModules(
+        CassandraMailboxModule.MODULE, CassandraSchemaVersionModule.MODULE));
+
+    CassandraMailboxPathV3DAO testee;
+
+    @BeforeEach
+    void setUp(CassandraCluster cassandra) {
+        testee = new CassandraMailboxPathV3DAO(
+            cassandra.getConf(),
+            CassandraUtils.WITH_DEFAULT_CONFIGURATION,
+            cassandraCluster.getCassandraConsistenciesConfiguration());
+    }
+
+    @Test
+    void cassandraIdAndPathShouldRespectBeanContract() {
+        EqualsVerifier.forClass(CassandraIdAndPath.class).verify();
+    }
+
+    @Test
+    void saveShouldInsertNewEntry() {
+        assertThat(testee.save(MAILBOX_1).block()).isTrue();
+
+        assertThat(testee.retrieve(USER_INBOX_MAILBOXPATH).blockOptional())
+            .contains(MAILBOX_1);
+    }
+
+    @Test
+    void saveOnSecondShouldBeFalse() {
+        assertThat(testee.save(MAILBOX_1).block()).isTrue();
+        assertThat(testee.save(MAILBOX_1).block()).isFalse();
+    }
+
+    @Test
+    void retrieveIdShouldReturnEmptyWhenEmptyData() {
+        assertThat(testee.retrieve(USER_INBOX_MAILBOXPATH).blockOptional())
+            .isEmpty();
+    }
+
+    @Test
+    void retrieveIdShouldReturnStoredData() {
+        testee.save(MAILBOX_1).block();
+
+        assertThat(testee.retrieve(USER_INBOX_MAILBOXPATH).blockOptional())
+            .contains(MAILBOX_1);
+    }
+
+    @Test
+    void getUserMailboxesShouldReturnAllMailboxesOfUser() {
+        testee.save(MAILBOX_1).block();
+        testee.save(MAILBOX_2).block();
+        testee.save(MAILBOX_3).block();
+
+        List<Mailbox> cassandraIds = testee
+            .listUserMailboxes(USER_INBOX_MAILBOXPATH.getNamespace(), USER_INBOX_MAILBOXPATH.getUser())
+            .collectList()
+            .block();
+
+        assertThat(cassandraIds)
+            .hasSize(2)
+            .containsOnly(MAILBOX_1, MAILBOX_2);
+    }
+
+    @Test
+    void deleteShouldNotThrowWhenEmpty() {
+        testee.delete(USER_INBOX_MAILBOXPATH).block();
+    }
+
+    @Test
+    void deleteShouldDeleteTheExistingMailboxId() {
+        testee.save(MAILBOX_1).block();
+
+        testee.delete(USER_INBOX_MAILBOXPATH).block();
+
+        assertThat(testee.retrieve(USER_INBOX_MAILBOXPATH).blockOptional())
+            .isEmpty();
+    }
+
+    @Test
+    void listAllShouldBeEmptyByDefault() {
+        assertThat(testee.listAll().collectList().block()).isEmpty();
+    }
+
+    @Test
+    void listAllShouldContainAddedEntry() {
+        testee.save(MAILBOX_1).block();
+
+        assertThat(testee.listAll().collectList().block())
+            .containsExactlyInAnyOrder(MAILBOX_1);
+    }
+
+    @Test
+    void listAllShouldNotContainDeletedEntry() {
+        testee.save(MAILBOX_1).block();
+
+        testee.delete(USER_INBOX_MAILBOXPATH).block();
+
+        assertThat(testee.listAll().collectList().block())
+            .isEmpty();
+    }
+
+    @Test
+    void listAllShouldContainAddedEntries() {
+        testee.save(MAILBOX_1).block();
+        testee.save(MAILBOX_3).block();
+
+        assertThat(testee.listAll().collectList().block())
+            .containsExactlyInAnyOrder(MAILBOX_1, MAILBOX_3);
+    }
+}
\ No newline at end of file
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java
index b9f29e8..6f2a79b 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/MailboxFixture.java
@@ -21,7 +21,9 @@ package org.apache.james.mailbox.cassandra.mail;
 
 import org.apache.james.core.Username;
 import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UidValidity;
 
 public interface MailboxFixture {
     Username USER = Username.of("user");
@@ -34,4 +36,8 @@ public interface MailboxFixture {
     MailboxPath USER_OUTBOX_MAILBOXPATH = MailboxPath.forUser(USER, "OUTBOX");
     MailboxPath OTHER_USER_MAILBOXPATH = MailboxPath.forUser(OTHER_USER, "INBOX");
     CassandraIdAndPath INBOX_ID_AND_PATH = new CassandraIdAndPath(INBOX_ID, USER_INBOX_MAILBOXPATH);
+
+    Mailbox MAILBOX_1 = new Mailbox(USER_INBOX_MAILBOXPATH, UidValidity.of(42), INBOX_ID);
+    Mailbox MAILBOX_2 = new Mailbox(USER_OUTBOX_MAILBOXPATH, UidValidity.of(43), OUTBOX_ID);
+    Mailbox MAILBOX_3 = new Mailbox(OTHER_USER_MAILBOXPATH, UidValidity.of(44), otherMailboxId);
 }


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