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 bt...@apache.org on 2020/03/09 16:07:31 UTC

[james-project] branch master updated (26bac42 -> 151db97)

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

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


    from 26bac42  JAMES-3107 Log request when P99 is exceeded
     new 803dcce  JAMES-3105 Add a MailboxCounters::toString method to ease debugging
     new 26bf6d6  JAMES-3105 Allow resetting counters
     new 23e7dd6  JAMES-3105 Add a service for recomputing mailbox counters
     new e5915c8  JAMES-3105 Task for recomputing mailbox counters
     new da5884a  JAMES-3105 Webadmin endpoint to recompute mailbox counters
     new da4cd47  JAMES-3105 Guice bindings
     new 24250e2  JAMES-3105 Piggy back message denormalization checks on mailbox counters recomputation
     new abbd07a  JAMES-3105 Changelog entry
     new aaba16c  JAMES-3105 Documentation for webadmin endpoint
     new a5e9e72  PROTOCOLS-121 Use enabled cipher suites in createPipelineFactory
     new 8372992  JAMES-3074 Maildir: On the fly UidValidity sanitizing
     new 819f25d  JAMES-3074 Cassandra: On the fly UidValidity sanitizing
     new 9c8a18e  JAMES-3074 JPA: On the fly UidValidity sanitizing
     new ea7bb21  JAMES-3074 UidValidity sanitizing at the IMAP level
     new b077642  JAMES-3074 Enforce UidValidity to be always valid
     new 930fd38  JAMES-3074 Changelog & upgrade instructions for JPA & Cassandra as well
     new 151db97  JAMES-3074 UidValidity sanitizing at the IMAP level

The 17 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   2 +
 .../james/mailbox/model/MailboxCounters.java       |  10 +
 .../apache/james/mailbox/model/UidValidity.java    |   8 +-
 .../mailbox/model/MailboxAssertingToolTest.java    |   4 +-
 .../james/mailbox/model/UidValidityTest.java       |  21 +-
 .../mailbox/backup/MailboxMessageFixture.java      |   8 +-
 .../mailbox/cassandra/ids/CassandraMessageId.java  |   2 +-
 .../cassandra/mail/CassandraMailboxCounterDAO.java |  86 ++++--
 .../cassandra/mail/CassandraMailboxDAO.java        |  52 +++-
 .../mail/task/RecomputeMailboxCountersService.java | 213 +++++++++++++++
 .../mail/task/RecomputeMailboxCountersTask.java    | 102 ++++++++
 ...ilboxCountersTaskAdditionalInformationDTO.java} |  74 +++---
 ...O.java => RecomputeMailboxCountersTaskDTO.java} |  22 +-
 .../mail/CassandraIndexTableHandlerTest.java       |   2 +-
 .../mail/CassandraMailboxCounterDAOTest.java       | 145 +++++++++--
 .../cassandra/mail/CassandraMailboxDAOTest.java    |  72 +++++-
 .../CassandraMailboxMapperConcurrencyTest.java     |   2 +-
 .../cassandra/mail/CassandraMailboxMapperTest.java |   2 +-
 .../mail/CassandraModSeqProviderTest.java          |   2 +-
 .../cassandra/mail/CassandraUidProviderTest.java   |   2 +-
 .../mail/migration/MailboxPathV2MigrationTest.java |   2 +-
 .../task/RecomputeMailboxCountersServiceTest.java  | 287 +++++++++++++++++++++
 ...mputeMailboxCountersTaskSerializationTest.java} |  32 ++-
 .../SolveMailboxInconsistenciesServiceTest.java    |   4 +-
 .../james/mailbox/jpa/mail/model/JPAMailbox.java   |  25 +-
 .../mailbox/jpa/mail/JpaMailboxMapperTest.java     |  38 +++
 .../LuceneMailboxMessageSearchIndexTest.java       |   6 +-
 .../james/mailbox/maildir/MaildirFolder.java       |  10 +-
 .../spamassassin/SpamAssassinListenerTest.java     |   2 +-
 .../james/mailbox/store/StoreRightManagerTest.java |   2 +-
 .../store/mail/model/ListMailboxAssertTest.java    |   2 +-
 .../store/mail/model/ListMessageAssertTest.java    |   2 +-
 .../store/mail/model/MailboxMapperACLTest.java     |   2 +-
 .../store/mail/model/MailboxMapperTest.java        |   6 +-
 .../store/mail/model/MessageIdMapperTest.java      |   2 +-
 .../store/mail/model/MessageMapperTest.java        |   2 +-
 .../mailbox/store/mail/model/MessageMoveTest.java  |   2 +-
 .../model/MessageWithAttachmentMapperTest.java     |   2 +-
 .../quota/DefaultUserQuotaRootResolverTest.java    |   4 +-
 .../mailbox/store/quota/QuotaCheckerTest.java      |   2 +-
 .../parser/AbstractSelectionCommandParser.java     |  13 +-
 .../imap/decode/parser/ExamineCommandParser.java   |   4 +-
 .../imap/decode/parser/SelectCommandParser.java    |   4 +-
 .../request/AbstractMailboxSelectionRequest.java   | 125 ++++++++-
 .../james/imap/message/request/ExamineRequest.java |   3 +-
 .../james/imap/message/request/SelectRequest.java  |   3 +-
 .../imap/processor/AbstractSelectionProcessor.java |   9 +-
 .../james/imap/processor/ExamineProcessor.java     |   5 +-
 .../james/imap/processor/SelectProcessor.java      |   5 +-
 ...arserTest.java => SelectCommandParserTest.java} |  54 ++--
 .../encode/MailboxStatusResponseEncoderTest.java   |   2 +-
 .../AbstractMailboxSelectionRequestTest.java       | 123 +++++++++
 .../james/imap/processor/CopyProcessorTest.java    |   2 +-
 .../james/imap/processor/MoveProcessorTest.java    |   2 +-
 .../processor/base/MailboxEventAnalyserTest.java   |   2 +-
 .../apache/james/protocols/netty/NettyServer.java  |   1 +
 .../webadmin/InconsistencySolvingRoutesModule.java |  14 +-
 .../james/modules/TaskSerializationModule.java     |  13 +
 .../adapter/mailbox/MailboxManagementTest.java     |   2 +-
 ...dminServerTaskSerializationIntegrationTest.java |  20 ++
 ... => RecomputeMailboxCountersRequestToTask.java} |  12 +-
 src/site/markdown/server/manage-webadmin.md        |  35 +++
 upgrade-instructions.md                            |  34 +++
 63 files changed, 1514 insertions(+), 238 deletions(-)
 create mode 100644 mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
 create mode 100644 mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTask.java
 copy mailbox/{tools/indexer/src/main/java/org/apache/mailbox/tools/indexer/SingleMessageReindexingTaskAdditionalInformationDTO.java => cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskAdditionalInformationDTO.java} (51%)
 copy mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/{SolveMailboxInconsistenciesTaskDTO.java => RecomputeMailboxCountersTaskDTO.java} (64%)
 create mode 100644 mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
 copy mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/{MailboxMergingTaskTest.java => RecomputeMailboxCountersTaskSerializationTest.java} (57%)
 copy protocols/imap/src/test/java/org/apache/james/imap/decode/parser/{AppendCommandParserTest.java => SelectCommandParserTest.java} (56%)
 create mode 100644 protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
 copy server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/{SolveMailboxInconsistenciesRequestToTask.java => RecomputeMailboxCountersRequestToTask.java} (75%)


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


[james-project] 14/17: JAMES-3074 UidValidity sanitizing at the IMAP level

Posted by bt...@apache.org.
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

commit ea7bb21c609abb3e121cc717cdfe860fea9e61d7
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 14:56:06 2020 +0700

    JAMES-3074 UidValidity sanitizing at the IMAP level
---
 .../parser/AbstractSelectionCommandParser.java     | 19 +++++--
 .../decode/parser/SelectCommandParserTest.java     | 58 ++++++++++++++++++++++
 2 files changed, 74 insertions(+), 3 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
index 71b41d0..1bd3841 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
@@ -80,8 +80,9 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
                 
                 // Consume enclosing paren
                 request.consumeChar('(');
-                lastKnownUidValidity = UidValidity.of(request.number());
-                
+                long uidValidityAsNumber = request.number();
+                lastKnownUidValidity = sanitizeUidValidity(uidValidityAsNumber);
+
                 // Consume the SP
                 request.consumeChar(' ');
                 knownModSeq = request.number(true);
@@ -129,7 +130,19 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
         request.eol();
         return createRequest(mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
-    
+
+    private UidValidity sanitizeUidValidity(long uidValidityAsNumber) {
+        if (UidValidity.isValid(uidValidityAsNumber)) {
+            return UidValidity.ofValid(uidValidityAsNumber);
+        } else {
+            // The UidValidity cached by the client is invalid
+            // We know that the backend will regenerate it
+            // Hence we force the mismatch
+            // QRSYNC command will be ignored
+            return UidValidity.random();
+        }
+    }
+
     /**
      * Check if the {@link IdRange}'s are formatted like stated in the QRESYNC RFC.
      * 
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
new file mode 100644
index 0000000..683385b
--- /dev/null
+++ b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.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.imap.decode.parser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.james.imap.api.Tag;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.decode.ImapRequestStreamLineReader;
+import org.apache.james.imap.message.request.SelectRequest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class SelectCommandParserTest {
+    private SelectCommandParser testee;
+
+    @BeforeEach
+    void setUp() {
+        testee = new SelectCommandParser(mock(StatusResponseFactory.class));
+    }
+
+    @Test
+    void decodeShouldSanitizeUidValidity() throws Exception {
+        ImapRequestStreamLineReader request = toRequest("mailbox (QRESYNC (0 42))\n");
+
+        SelectRequest selectRequest = (SelectRequest) testee.decode(request, new Tag("0001"), mock(ImapSession.class));
+
+        assertThat(selectRequest.getLastKnownUidValidity().isValid())
+            .isTrue();
+    }
+
+    private ImapRequestStreamLineReader toRequest(String input) {
+        return new ImapRequestStreamLineReader(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)), new ByteArrayOutputStream());
+    }
+}
\ No newline at end of file


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


[james-project] 03/17: JAMES-3105 Add a service for recomputing mailbox counters

Posted by bt...@apache.org.
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

commit 23e7dd605febdbd1262df908bd9a800d2d53ae28
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 6 22:36:42 2020 +0700

    JAMES-3105 Add a service for recomputing mailbox counters
    
    Algorithm:
    
     - List existing mailboxes
     - List their messages
     - Check them against their source of truth
     - Compute mailbox counter values
     - And reset the value of the counter if needed
    
    Limitations:
    
    This won't be friendly in the face of concurrent operations, which
    would be ignored during the recomputation of the counter of the
    given mailbox. However the source of truth is unaffected hence,
    upon rerunning the algorithm, the result will be eventually correct.
    
    We rely on the "listing messages by mailbox" projection (that we
    recheck). Missing entries in there will be ignored until the given
    projection is healed (currently unsupported).
---
 .../mailbox/cassandra/ids/CassandraMessageId.java  |   2 +-
 .../mail/task/RecomputeMailboxCountersService.java | 198 ++++++++++++++
 .../task/RecomputeMailboxCountersServiceTest.java  | 287 +++++++++++++++++++++
 3 files changed, 486 insertions(+), 1 deletion(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/ids/CassandraMessageId.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/ids/CassandraMessageId.java
index be359a4..cf7cb63 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/ids/CassandraMessageId.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/ids/CassandraMessageId.java
@@ -41,7 +41,7 @@ public class CassandraMessageId implements MessageId {
         }
 
         @Override
-        public MessageId fromString(String serialized) {
+        public CassandraMessageId fromString(String serialized) {
             return of(UUID.fromString(serialized));
         }
     }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
new file mode 100644
index 0000000..d45cb30
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
@@ -0,0 +1,198 @@
+/****************************************************************
+ * 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.task;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MailboxCounters;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.task.Task;
+import org.apache.james.task.Task.Result;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class RecomputeMailboxCountersService {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(RecomputeMailboxCountersService.class);
+
+    private static class Counter {
+        private final CassandraId mailboxId;
+        private final AtomicLong total;
+        private final AtomicLong unseen;
+
+        private Counter(CassandraId mailboxId) {
+            this.mailboxId = mailboxId;
+            unseen = new AtomicLong();
+            total = new AtomicLong();
+        }
+
+        void process(ComposedMessageIdWithMetaData metadata) {
+            total.incrementAndGet();
+            if (!metadata.getFlags().contains(Flags.Flag.SEEN)) {
+                unseen.incrementAndGet();
+            }
+        }
+
+        MailboxCounters snapshot() {
+            return MailboxCounters.builder()
+                .mailboxId(mailboxId)
+                .count(total.get())
+                .unseen(unseen.get())
+                .build();
+        }
+    }
+
+    static class Context {
+        static class Snapshot {
+            private final long processedMailboxCount;
+            private final ImmutableList<CassandraId> failedMailboxes;
+
+            private Snapshot(long processedMailboxCount, ImmutableList<CassandraId> failedMailboxes) {
+                this.processedMailboxCount = processedMailboxCount;
+                this.failedMailboxes = failedMailboxes;
+            }
+
+            long getProcessedMailboxCount() {
+                return processedMailboxCount;
+            }
+
+            ImmutableList<CassandraId> getFailedMailboxes() {
+                return failedMailboxes;
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Snapshot) {
+                    Snapshot snapshot = (Snapshot) o;
+
+                    return Objects.equals(this.processedMailboxCount, snapshot.processedMailboxCount)
+                        && Objects.equals(this.failedMailboxes, snapshot.failedMailboxes);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(processedMailboxCount, failedMailboxes);
+            }
+
+            @Override
+            public String toString() {
+                return MoreObjects.toStringHelper(this)
+                    .add("processedMailboxCount", processedMailboxCount)
+                    .add("failedMailboxes", failedMailboxes)
+                    .toString();
+            }
+        }
+
+        private final AtomicLong processedMailboxCount;
+        private final ConcurrentLinkedDeque<CassandraId> failedMailboxes;
+
+        Context() {
+            processedMailboxCount = new AtomicLong();
+            failedMailboxes = new ConcurrentLinkedDeque<>();
+        }
+
+        void incrementProcessed() {
+            processedMailboxCount.incrementAndGet();
+        }
+
+        void addToFailedMailboxes(CassandraId cassandraId) {
+            failedMailboxes.add(cassandraId);
+        }
+
+        Snapshot snapshot() {
+            return new Snapshot(processedMailboxCount.get(),
+                ImmutableList.copyOf(failedMailboxes));
+        }
+    }
+
+    private final CassandraMailboxDAO mailboxDAO;
+    private final CassandraMessageIdDAO imapUidToMessageIdDAO;
+    private final CassandraMessageIdToImapUidDAO messageIdToImapUidDAO;
+    private final CassandraMailboxCounterDAO counterDAO;
+
+    @Inject
+    RecomputeMailboxCountersService(CassandraMailboxDAO mailboxDAO,
+                                    CassandraMessageIdDAO imapUidToMessageIdDAO,
+                                    CassandraMessageIdToImapUidDAO messageIdToImapUidDAO,
+                                    CassandraMailboxCounterDAO counterDAO) {
+        this.mailboxDAO = mailboxDAO;
+        this.imapUidToMessageIdDAO = imapUidToMessageIdDAO;
+        this.messageIdToImapUidDAO = messageIdToImapUidDAO;
+        this.counterDAO = counterDAO;
+    }
+
+    Mono<Result> recomputeMailboxCounters(Context context) {
+        return mailboxDAO.retrieveAllMailboxes()
+            .flatMap(mailbox -> recomputeMailboxCounter(context, mailbox))
+            .reduce(Result.COMPLETED, Task::combine)
+            .onErrorResume(e -> {
+                LOGGER.error("Error listing mailboxes", e);
+                return Mono.just(Result.PARTIAL);
+            });
+    }
+
+    private Mono<Result> recomputeMailboxCounter(Context context, Mailbox mailbox) {
+        CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
+        Counter counter = new Counter(mailboxId);
+
+        return imapUidToMessageIdDAO.retrieveMessages(mailboxId, MessageRange.all())
+            .flatMap(message -> latestMetadata(mailboxId, message))
+            .doOnNext(counter::process)
+            .then(Mono.defer(() -> counterDAO.resetCounters(counter.snapshot())))
+            .then(Mono.just(Result.COMPLETED))
+            .doOnNext(any -> {
+                LOGGER.info("Counters recomputed for {}", mailboxId.serialize());
+                context.incrementProcessed();
+            })
+            .onErrorResume(e -> {
+                context.addToFailedMailboxes(mailboxId);
+                LOGGER.error("Error while recomputing counters for {}", mailboxId.serialize(), e);
+                return Mono.just(Result.PARTIAL);
+            });
+    }
+
+    private Flux<ComposedMessageIdWithMetaData> latestMetadata(CassandraId mailboxId, ComposedMessageIdWithMetaData message) {
+        CassandraMessageId messageId = (CassandraMessageId) message.getComposedMessageId().getMessageId();
+
+        return messageIdToImapUidDAO.retrieve(messageId, Optional.of(mailboxId));
+    }
+}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
new file mode 100644
index 0000000..334f7b4
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
@@ -0,0 +1,287 @@
+/****************************************************************
+ * 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.task;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.UUID;
+
+import javax.mail.Flags;
+
+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.versions.CassandraSchemaVersionModule;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService.Context;
+import org.apache.james.mailbox.cassandra.modules.CassandraAclModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.Mailbox;
+import org.apache.james.mailbox.model.MailboxCounters;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UidValidity;
+import org.apache.james.task.Task.Result;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class RecomputeMailboxCountersServiceTest {
+    private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(145);
+    private static final Username USER = Username.of("user");
+    private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER, "abc");
+    private static final CassandraMessageId.Factory MESSAGE_ID_FACTORY = new CassandraMessageId.Factory();
+    private static final CassandraMessageId MESSAGE_ID_1 = MESSAGE_ID_FACTORY.fromString("40ff9e30-6022-11ea-9a94-d300cbf968c0");
+    private static CassandraId CASSANDRA_ID_1 = CassandraId.of(UUID.fromString("16d681e0-6023-11ea-a7f2-0f94ad804b0d"));
+    private static final ComposedMessageIdWithMetaData METADATA_UNSEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(), ModSeq.of(45));
+    private static final ComposedMessageIdWithMetaData METADATA_SEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(Flags.Flag.SEEN), ModSeq.of(45));
+    private static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UID_VALIDITY_1, CASSANDRA_ID_1);
+
+    @RegisterExtension
+    static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(
+        CassandraModule.aggregateModules(
+            CassandraSchemaVersionModule.MODULE,
+            CassandraMailboxModule.MODULE,
+            CassandraMessageModule.MODULE,
+            CassandraMailboxCounterModule.MODULE,
+            CassandraAclModule.MODULE));
+
+    CassandraMailboxDAO mailboxDAO;
+    CassandraMessageIdDAO imapUidToMessageIdDAO;
+    CassandraMessageIdToImapUidDAO messageIdToImapUidDAO;
+    CassandraMailboxCounterDAO counterDAO;
+    RecomputeMailboxCountersService testee;
+
+    @BeforeEach
+    void setUp(CassandraCluster cassandra) {
+        mailboxDAO = new CassandraMailboxDAO(cassandra.getConf(), cassandra.getTypesProvider());
+        imapUidToMessageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), MESSAGE_ID_FACTORY);
+        messageIdToImapUidDAO = new CassandraMessageIdToImapUidDAO(cassandra.getConf(), MESSAGE_ID_FACTORY);
+        counterDAO = new CassandraMailboxCounterDAO(cassandra.getConf());
+        testee = new RecomputeMailboxCountersService(mailboxDAO, imapUidToMessageIdDAO, messageIdToImapUidDAO, counterDAO);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenNoMailboxes() {
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenMailboxWithNoMessages() {
+        mailboxDAO.save(MAILBOX).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenMailboxWithMessages() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+        counterDAO.incrementUnseen(CASSANDRA_ID_1).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenMessageDenormalizationIssue() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_SEEN).block();
+        counterDAO.incrementUnseen(CASSANDRA_ID_1).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCountersAreIncorrect() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenOrphanMailboxRegistration() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        counterDAO.incrementUnseen(CASSANDRA_ID_1).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldReturnCompletedWhenMailboxListReferenceIsMissing() {
+        mailboxDAO.save(MAILBOX).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+        counterDAO.incrementUnseen(CASSANDRA_ID_1).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(testee.recomputeMailboxCounters(new Context()).block())
+            .isEqualTo(Result.COMPLETED);
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldNoopWhenMailboxWithoutMessage() {
+        mailboxDAO.save(MAILBOX).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .isEmpty();
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldNoopWhenValidCounters() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+        counterDAO.incrementUnseen(CASSANDRA_ID_1).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .contains(MailboxCounters.builder()
+                .mailboxId(CASSANDRA_ID_1)
+                .count(1)
+                .unseen(1)
+                .build());
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldRecreateMissingCounters() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .contains(MailboxCounters.builder()
+                .mailboxId(CASSANDRA_ID_1)
+                .count(1)
+                .unseen(1)
+                .build());
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldResetIncorrectCounters() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .contains(MailboxCounters.builder()
+                .mailboxId(CASSANDRA_ID_1)
+                .count(1)
+                .unseen(1)
+                .build());
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldTakeSeenIntoAccount() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_SEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_SEEN).block();
+        counterDAO.incrementCount(CASSANDRA_ID_1).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .contains(MailboxCounters.builder()
+                .mailboxId(CASSANDRA_ID_1)
+                .count(1)
+                .unseen(0)
+                .build());
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldUseSourceOfTruthForComputation() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_SEEN).block();
+        messageIdToImapUidDAO.insert(METADATA_UNSEEN).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .contains(MailboxCounters.builder()
+                .mailboxId(CASSANDRA_ID_1)
+                .count(1)
+                .unseen(1)
+                .build());
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldIgnoreMissingMailboxListReferences() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_SEEN).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .isEmpty();
+    }
+
+    @Test
+    void recomputeMailboxCountersShouldIgnoreOrphanMailboxListReference() {
+        mailboxDAO.save(MAILBOX).block();
+        imapUidToMessageIdDAO.insert(METADATA_UNSEEN).block();
+
+        testee.recomputeMailboxCounters(new Context()).block();
+
+        assertThat(counterDAO.retrieveMailboxCounters(CASSANDRA_ID_1).blockOptional())
+            .isEmpty();
+    }
+}
\ No newline at end of file


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


[james-project] 05/17: JAMES-3105 Webadmin endpoint to recompute mailbox counters

Posted by bt...@apache.org.
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

commit da5884a0f5940f803349e1ad290ee94b78e66a64
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 6 23:05:23 2020 +0700

    JAMES-3105 Webadmin endpoint to recompute mailbox counters
    
    Proposed endpoint:
    
    ```
    curl -XPST http://ip:port/mailboxes?task=RecomputeMailboxCounters
    ```
---
 .../RecomputeMailboxCountersRequestToTask.java     | 37 ++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/RecomputeMailboxCountersRequestToTask.java b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/RecomputeMailboxCountersRequestToTask.java
new file mode 100644
index 0000000..2ec6679
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-cassandra/src/main/java/org/apache/james/webadmin/routes/RecomputeMailboxCountersRequestToTask.java
@@ -0,0 +1,37 @@
+/****************************************************************
+ * 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.webadmin.routes;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService;
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersTask;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+import org.apache.james.webadmin.tasks.TaskRegistrationKey;
+
+public class RecomputeMailboxCountersRequestToTask extends TaskFromRequestRegistry.TaskRegistration {
+    private static final TaskRegistrationKey REGISTRATION_KEY = TaskRegistrationKey.of("RecomputeMailboxCounters");
+
+    @Inject
+    public RecomputeMailboxCountersRequestToTask(RecomputeMailboxCountersService service) {
+        super(REGISTRATION_KEY,
+            request -> new RecomputeMailboxCountersTask(service));
+    }
+}


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


[james-project] 06/17: JAMES-3105 Guice bindings

Posted by bt...@apache.org.
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

commit da4cd47636cb3833098f59a1ec48454848a24a56
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 6 23:09:57 2020 +0700

    JAMES-3105 Guice bindings
---
 .../webadmin/InconsistencySolvingRoutesModule.java   | 14 +++++++++++---
 .../james/modules/TaskSerializationModule.java       | 13 +++++++++++++
 ...bAdminServerTaskSerializationIntegrationTest.java | 20 ++++++++++++++++++++
 3 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java
index bf995c1..baf4402 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/webadmin/InconsistencySolvingRoutesModule.java
@@ -19,9 +19,12 @@
 
 package org.apache.james.modules.webadmin;
 
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService;
+import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesService;
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.routes.CassandraMappingsRoutes;
 import org.apache.james.webadmin.routes.MailboxesRoutes;
+import org.apache.james.webadmin.routes.RecomputeMailboxCountersRequestToTask;
 import org.apache.james.webadmin.routes.SolveMailboxInconsistenciesRequestToTask;
 import org.apache.james.webadmin.service.CassandraMappingsService;
 import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
@@ -46,9 +49,14 @@ public class InconsistencySolvingRoutesModule extends AbstractModule {
     public static class SolveMailboxInconsistenciesModules extends AbstractModule {
         @Override
         protected void configure() {
-            Multibinder.newSetBinder(binder(), TaskFromRequestRegistry.TaskRegistration.class,
-                Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS))
-                .addBinding().to(SolveMailboxInconsistenciesRequestToTask.class);
+            bind(RecomputeMailboxCountersService.class).in(Scopes.SINGLETON);
+            bind(SolveMailboxInconsistenciesService.class).in(Scopes.SINGLETON);
+
+            Multibinder<TaskFromRequestRegistry.TaskRegistration> multiBinder = Multibinder.newSetBinder(binder(),
+                TaskFromRequestRegistry.TaskRegistration.class, Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS));
+
+            multiBinder.addBinding().to(SolveMailboxInconsistenciesRequestToTask.class);
+            multiBinder.addBinding().to(RecomputeMailboxCountersRequestToTask.class);
         }
     }
 
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
index 30ef794..bf3f70f 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
@@ -35,6 +35,9 @@ import org.apache.james.json.DTOModule;
 import org.apache.james.mailbox.cassandra.mail.task.MailboxMergingTaskAdditionalInformationDTO;
 import org.apache.james.mailbox.cassandra.mail.task.MailboxMergingTaskDTO;
 import org.apache.james.mailbox.cassandra.mail.task.MailboxMergingTaskRunner;
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService;
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersTaskAdditionalInformationDTO;
+import org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersTaskDTO;
 import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesService;
 import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesTaskAdditionalInformationDTO;
 import org.apache.james.mailbox.cassandra.mail.task.SolveMailboxInconsistenciesTaskDTO;
@@ -260,6 +263,11 @@ public class TaskSerializationModule extends AbstractModule {
     }
 
     @ProvidesIntoSet
+    public TaskDTOModule<? extends Task, ? extends TaskDTO> recomputeMailboxCountersTask(RecomputeMailboxCountersService service) {
+        return RecomputeMailboxCountersTaskDTO.module(service);
+    }
+
+    @ProvidesIntoSet
     public TaskDTOModule<? extends Task, ? extends TaskDTO> messageIdReindexingTask(MessageIdReIndexingTask.Factory factory) {
         return MessageIdReindexingTaskDTO.module(factory);
     }
@@ -370,6 +378,11 @@ public class TaskSerializationModule extends AbstractModule {
     }
 
     @ProvidesIntoSet
+    public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends  AdditionalInformationDTO> recomputeMailboxCountersAdditionalInformation() {
+        return RecomputeMailboxCountersTaskAdditionalInformationDTO.MODULE;
+    }
+
+    @ProvidesIntoSet
     public AdditionalInformationDTOModule<? extends TaskExecutionDetails.AdditionalInformation, ? extends  AdditionalInformationDTO> messageIdReindexingAdditionalInformation(MessageId.Factory messageIdFactory) {
         return MessageIdReindexingTaskAdditionalInformationDTO.serializationModule(messageIdFactory);
     }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
index af117df..fe77c70 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
@@ -739,6 +739,26 @@ class RabbitMQWebAdminServerTaskSerializationIntegrationTest {
             .body("additionalInformation.errorMappingsCount", is(0));
     }
 
+    @Test
+    void recomputeMailboxCountersShouldComplete() {
+        String taskId = with()
+                .basePath("/mailboxes")
+                .queryParam("task", "RecomputeMailboxCounters")
+            .post()
+                .jsonPath()
+                .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is("recompute-mailbox-counters"))
+            .body("additionalInformation.processedMailboxes", is(0));
+    }
+
     private MailboxListener.MailboxAdded createMailboxAdded() {
         String uuid = "6e0dd59d-660e-4d9b-b22f-0354479f47b4";
         return EventFactory.mailboxAdded()


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


[james-project] 02/17: JAMES-3105 Allow resetting counters

Posted by bt...@apache.org.
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

commit 26bf6d6449c606b0d7ce2cfecea68d0adc9657c5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 6 22:36:25 2020 +0700

    JAMES-3105 Allow resetting counters
    
    Resetting counters is intrinsecly flowed in the face of concurrent operations.
---
 .../cassandra/mail/CassandraMailboxCounterDAO.java |  86 ++++++++++---
 .../mail/CassandraMailboxCounterDAOTest.java       | 143 ++++++++++++++++++---
 2 files changed, 195 insertions(+), 34 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
index 0c12e28..01f2564 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
@@ -25,13 +25,15 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.incr;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.COUNT;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.MAILBOX_ID;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.UNSEEN;
 
 import javax.inject.Inject;
 
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.mailbox.cassandra.ids.CassandraId;
-import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
-import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxCounters;
 
@@ -48,6 +50,8 @@ public class CassandraMailboxCounterDAO {
     private final PreparedStatement readStatement;
     private final PreparedStatement incrementUnseenCountStatement;
     private final PreparedStatement incrementMessageCountStatement;
+    private final PreparedStatement addToCounters;
+    private final PreparedStatement removeToCounters;
     private final PreparedStatement decrementUnseenCountStatement;
     private final PreparedStatement decrementMessageCountStatement;
 
@@ -55,36 +59,82 @@ public class CassandraMailboxCounterDAO {
     public CassandraMailboxCounterDAO(Session session) {
         cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
         readStatement = createReadStatement(session);
-        incrementMessageCountStatement = updateMailboxStatement(session, incr(CassandraMailboxCountersTable.COUNT));
-        incrementUnseenCountStatement = updateMailboxStatement(session, incr(CassandraMailboxCountersTable.UNSEEN));
-        decrementMessageCountStatement = updateMailboxStatement(session, decr(CassandraMailboxCountersTable.COUNT));
-        decrementUnseenCountStatement = updateMailboxStatement(session, decr(CassandraMailboxCountersTable.UNSEEN));
+        incrementMessageCountStatement = updateMailboxStatement(session, incr(COUNT));
+        incrementUnseenCountStatement = updateMailboxStatement(session, incr(UNSEEN));
+        addToCounters = session.prepare(update(TABLE_NAME)
+            .with(incr(COUNT, bindMarker(COUNT)))
+            .and(incr(UNSEEN, bindMarker(UNSEEN)))
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID))));
+        removeToCounters = session.prepare(update(TABLE_NAME)
+            .with(decr(COUNT, bindMarker(COUNT)))
+            .and(decr(UNSEEN, bindMarker(UNSEEN)))
+            .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID))));
+        decrementMessageCountStatement = updateMailboxStatement(session, decr(COUNT));
+        decrementUnseenCountStatement = updateMailboxStatement(session, decr(UNSEEN));
     }
 
     private PreparedStatement createReadStatement(Session session) {
         return session.prepare(
-            select(CassandraMailboxCountersTable.UNSEEN, CassandraMailboxCountersTable.COUNT)
-                .from(CassandraMailboxCountersTable.TABLE_NAME)
-                .where(eq(CassandraMailboxCountersTable.MAILBOX_ID, bindMarker(CassandraMailboxCountersTable.MAILBOX_ID))));
+            select(UNSEEN, COUNT)
+                .from(TABLE_NAME)
+                .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID))));
     }
 
     private PreparedStatement updateMailboxStatement(Session session, Assignment operation) {
         return session.prepare(
-            update(CassandraMailboxCountersTable.TABLE_NAME)
+            update(TABLE_NAME)
                 .with(operation)
-                .where(eq(CassandraMailboxCountersTable.MAILBOX_ID, bindMarker(CassandraMailboxCountersTable.MAILBOX_ID))));
+                .where(eq(MAILBOX_ID, bindMarker(MAILBOX_ID))));
     }
 
     public Mono<MailboxCounters> retrieveMailboxCounters(CassandraId mailboxId) {
         return cassandraAsyncExecutor.executeSingleRow(bindWithMailbox(mailboxId, readStatement))
             .map(row ->  MailboxCounters.builder()
                 .mailboxId(mailboxId)
-                .count(row.getLong(CassandraMailboxCountersTable.COUNT))
-                .unseen(row.getLong(CassandraMailboxCountersTable.UNSEEN))
+                .count(row.getLong(COUNT))
+                .unseen(row.getLong(UNSEEN))
                 .build());
     }
 
-    public Mono<Long> countMessagesInMailbox(Mailbox mailbox) throws MailboxException {
+    public Mono<Void> resetCounters(MailboxCounters counters) {
+        CassandraId mailboxId = (CassandraId) counters.getMailboxId();
+
+        return retrieveMailboxCounters(mailboxId)
+            .switchIfEmpty(Mono.just(emptyCounters(mailboxId)))
+            .flatMap(storedCounters -> {
+                if (storedCounters.equals(counters)) {
+                    return Mono.empty();
+                }
+                return remove(storedCounters)
+                    .then(add(counters));
+            });
+    }
+
+    private MailboxCounters emptyCounters(CassandraId mailboxId) {
+        return MailboxCounters.builder()
+            .count(0)
+            .unseen(0)
+            .mailboxId(mailboxId)
+            .build();
+    }
+
+    private Mono<Void> add(MailboxCounters counters) {
+        CassandraId mailboxId = (CassandraId) counters.getMailboxId();
+        return cassandraAsyncExecutor.executeVoid(
+            bindWithMailbox(mailboxId, addToCounters)
+                .setLong(COUNT, counters.getCount())
+                .setLong(UNSEEN, counters.getUnseen()));
+    }
+
+    private Mono<Void> remove(MailboxCounters counters) {
+        CassandraId mailboxId = (CassandraId) counters.getMailboxId();
+        return cassandraAsyncExecutor.executeVoid(
+            bindWithMailbox(mailboxId, removeToCounters)
+                .setLong(COUNT, counters.getCount())
+                .setLong(UNSEEN, counters.getUnseen()));
+    }
+
+    public Mono<Long> countMessagesInMailbox(Mailbox mailbox) {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
 
         return countMessagesInMailbox(mailboxId);
@@ -92,14 +142,14 @@ public class CassandraMailboxCounterDAO {
 
     public Mono<Long> countMessagesInMailbox(CassandraId cassandraId) {
         return cassandraAsyncExecutor.executeSingleRow(bindWithMailbox(cassandraId, readStatement))
-            .map(row -> row.getLong(CassandraMailboxCountersTable.COUNT));
+            .map(row -> row.getLong(COUNT));
     }
 
-    public Mono<Long> countUnseenMessagesInMailbox(Mailbox mailbox) throws MailboxException {
+    public Mono<Long> countUnseenMessagesInMailbox(Mailbox mailbox) {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
 
         return cassandraAsyncExecutor.executeSingleRow(bindWithMailbox(mailboxId, readStatement))
-            .map(row -> row.getLong(CassandraMailboxCountersTable.UNSEEN));
+            .map(row -> row.getLong(UNSEEN));
     }
 
     public Mono<Void> decrementCount(CassandraId mailboxId) {
@@ -120,6 +170,6 @@ public class CassandraMailboxCounterDAO {
 
     private BoundStatement bindWithMailbox(CassandraId mailboxId, PreparedStatement statement) {
         return statement.bind()
-            .setUUID(CassandraMailboxCountersTable.MAILBOX_ID, mailboxId.asUuid());
+            .setUUID(MAILBOX_ID, mailboxId.asUuid());
     }
 }
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
index 15f5939..62aeb01 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
@@ -52,36 +52,36 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void countMessagesInMailboxShouldReturnEmptyByDefault() throws Exception {
+    void countMessagesInMailboxShouldReturnEmptyByDefault() {
         assertThat(testee.countMessagesInMailbox(mailbox).hasElement().block()).isFalse();
     }
 
     @Test
-    void countUnseenMessagesInMailboxShouldReturnEmptyByDefault() throws Exception {
+    void countUnseenMessagesInMailboxShouldReturnEmptyByDefault() {
         assertThat(testee.countUnseenMessagesInMailbox(mailbox).hasElement().block()).isFalse();
     }
 
     @Test
-    void retrieveMailboxCounterShouldReturnEmptyByDefault() throws Exception {
+    void retrieveMailboxCounterShouldReturnEmptyByDefault() {
         assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).hasElement().block()).isFalse();
     }
 
     @Test
-    void incrementCountShouldAddOneWhenAbsent() throws Exception {
+    void incrementCountShouldAddOneWhenAbsent() {
         testee.incrementCount(MAILBOX_ID).block();
 
         assertThat(testee.countMessagesInMailbox(mailbox).block()).isEqualTo(1L);
     }
 
     @Test
-    void incrementUnseenShouldAddOneWhenAbsent() throws Exception {
+    void incrementUnseenShouldAddOneWhenAbsent() {
         testee.incrementUnseen(MAILBOX_ID).block();
 
         assertThat(testee.countUnseenMessagesInMailbox(mailbox).block()).isEqualTo(1L);
     }
 
     @Test
-    void incrementUnseenShouldAddOneWhenAbsentOnMailboxCounters() throws Exception {
+    void incrementUnseenShouldAddOneWhenAbsentOnMailboxCounters() {
         testee.incrementUnseen(MAILBOX_ID).block();
 
         assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).block())
@@ -93,7 +93,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void incrementCountShouldAddOneWhenAbsentOnMailboxCounters() throws Exception {
+    void incrementCountShouldAddOneWhenAbsentOnMailboxCounters() {
         testee.incrementCount(MAILBOX_ID).block();
 
         assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).block())
@@ -105,7 +105,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void retrieveMailboxCounterShouldWorkWhenFullRow() throws Exception {
+    void retrieveMailboxCounterShouldWorkWhenFullRow() {
         testee.incrementCount(MAILBOX_ID).block();
         testee.incrementUnseen(MAILBOX_ID).block();
 
@@ -118,7 +118,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementCountShouldRemoveOne() throws Exception {
+    void decrementCountShouldRemoveOne() {
         testee.incrementCount(MAILBOX_ID).block();
 
         testee.decrementCount(MAILBOX_ID).block();
@@ -128,7 +128,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementUnseenShouldRemoveOne() throws Exception {
+    void decrementUnseenShouldRemoveOne() {
         testee.incrementUnseen(MAILBOX_ID).block();
 
         testee.decrementUnseen(MAILBOX_ID).block();
@@ -138,7 +138,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void incrementUnseenShouldHaveNoImpactOnMessageCount() throws Exception {
+    void incrementUnseenShouldHaveNoImpactOnMessageCount() {
         testee.incrementUnseen(MAILBOX_ID).block();
 
         assertThat(testee.countMessagesInMailbox(mailbox).block())
@@ -146,7 +146,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void incrementCountShouldHaveNoEffectOnUnseenCount() throws Exception {
+    void incrementCountShouldHaveNoEffectOnUnseenCount() {
         testee.incrementCount(MAILBOX_ID).block();
 
         assertThat(testee.countUnseenMessagesInMailbox(mailbox).block())
@@ -154,7 +154,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementUnseenShouldHaveNoEffectOnMessageCount() throws Exception {
+    void decrementUnseenShouldHaveNoEffectOnMessageCount() {
         testee.incrementCount(MAILBOX_ID).block();
 
         testee.decrementUnseen(MAILBOX_ID).block();
@@ -164,7 +164,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementCountShouldHaveNoEffectOnUnseenCount() throws Exception {
+    void decrementCountShouldHaveNoEffectOnUnseenCount() {
         testee.incrementUnseen(MAILBOX_ID).block();
 
         testee.decrementCount(MAILBOX_ID).block();
@@ -174,7 +174,7 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementCountCanLeadToNegativeValue() throws Exception {
+    void decrementCountCanLeadToNegativeValue() {
         testee.decrementCount(MAILBOX_ID).block();
 
         assertThat(testee.countMessagesInMailbox(mailbox).block())
@@ -182,10 +182,121 @@ class CassandraMailboxCounterDAOTest {
     }
 
     @Test
-    void decrementUnseenCanLeadToNegativeValue() throws Exception {
+    void decrementUnseenCanLeadToNegativeValue() {
         testee.decrementUnseen(MAILBOX_ID).block();
 
         assertThat(testee.countUnseenMessagesInMailbox(mailbox).block())
             .isEqualTo(-1L);
     }
+
+    @Test
+    void resetCountersShouldNoopWhenZeroAndNoData() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(0)
+            .count(0)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .isEmpty();
+    }
+
+    @Test
+    void resetCountersShouldNoopWhenZeroAndZeroData() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(0)
+            .count(0)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.incrementUnseen(MAILBOX_ID).block();
+        testee.decrementUnseen(MAILBOX_ID).block();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
+
+    @Test
+    void resetCountersShouldReInitCountWhenNothing() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(45)
+            .count(78)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
+
+    @Test
+    void resetCountersShouldReInitCountWhenData() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(45)
+            .count(78)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.incrementCount(MAILBOX_ID).block();
+        testee.incrementUnseen(MAILBOX_ID).block();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
+
+    @Test
+    void resetCountersShouldBeIdempotent() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(45)
+            .count(78)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.resetCounters(counters).block();
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
+
+    @Test
+    void resetCountersShouldReInitCountWhenZeroUnseen() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(0)
+            .count(78)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.incrementCount(MAILBOX_ID).block();
+        testee.incrementUnseen(MAILBOX_ID).block();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
+
+    @Test
+    void resetCountersShouldReInitCountWhenZeroCount() {
+        MailboxCounters counters = MailboxCounters.builder()
+            .unseen(46)
+            .count(0)
+            .mailboxId(MAILBOX_ID)
+            .build();
+
+        testee.incrementCount(MAILBOX_ID).block();
+        testee.incrementUnseen(MAILBOX_ID).block();
+
+        testee.resetCounters(counters).block();
+
+        assertThat(testee.retrieveMailboxCounters(MAILBOX_ID).blockOptional())
+            .contains(counters);
+    }
 }


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


[james-project] 09/17: JAMES-3105 Documentation for webadmin endpoint

Posted by bt...@apache.org.
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

commit aaba16cab99a0fa93207549c3c016d69f5da34b1
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 7 14:12:27 2020 +0700

    JAMES-3105 Documentation for webadmin endpoint
---
 src/site/markdown/server/manage-webadmin.md | 35 +++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index 0b5675f..0b84b2f 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -453,6 +453,41 @@ A dirty read is when data is read between the two writes of the denormalization
 In order to ensure being offline, stop the traffic on SMTP, JMAP and IMAP ports, for example via re-configuration or 
 firewall rules.
 
+#### Recomputing mailbox counters
+
+This task is only available on top of Guice Cassandra products.
+
+```
+curl -XPOST /mailboxes?task=RecomputeMailboxCounters
+```
+
+Will recompute counters (unseen & total count) for the mailbox object stored in Cassandra.
+
+Cassandra maintains a per mailbox projection for message count and unseen message count. As with any projection, it can 
+go out of sync, leading to inconsistent results being returned to the client.
+
+[More details about endpoints returning a task](#Endpoints_returning_a_task).
+
+The scheduled task will have the following type `recompute-mailbox-counters` and the following `additionalInformation`:
+
+```
+{
+  "type":"recompute-mailbox-counters",
+  "processedMailboxes": 3,
+  "failedMailboxes": ["464765a0-e4e7-11e4-aba4-710c1de3782b"]
+}
+```
+
+Note that conflicting inconsistencies entries will not be fixed and will require to explicitly use 
+[ghost mailbox](#correcting-ghost-mailbox) endpoint in order to merge the conflicting mailboxes and prevent any message
+loss.
+
+**WARNING**: this task do not take into account concurrent modifications upon a single mailbox counter recomputation. 
+Rerunning the task will *eventually* provide the consistent result. As such we advise to run this task offline. 
+
+In order to ensure being offline, stop the traffic on SMTP, JMAP and IMAP ports, for example via re-configuration or 
+firewall rules.
+
 #### Recomputing Global JMAP fast message view projection
 
 This action is only available for backends supporting JMAP protocol.


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


[james-project] 16/17: JAMES-3074 Changelog & upgrade instructions for JPA & Cassandra as well

Posted by bt...@apache.org.
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

commit 930fd38717b67f753b96f8b0595e80af7903298a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 15:08:58 2020 +0700

    JAMES-3074 Changelog & upgrade instructions for JPA & Cassandra as well
---
 CHANGELOG.md            |  2 +-
 upgrade-instructions.md | 18 ++++++++++++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index b846a76..a76e0e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,7 +49,7 @@ of tasks being currently executed.
 - JAMES-2972 Incorrect attribute name in the mailet configuration thanks to jtconsol
 - JAMES-2632 JMAP Draft GetMailboxes performance enhancements when retrieving all mailboxes of a user
 - JAMES-2964 Forbid to create User quota/ Domain quota/ Global quota using negative number
-- JAMES-3074 Maildir backend generated invalid UidValidity. Read upgrade instructions.
+- JAMES-3074 Fixing UidValidity generation, sanitizing of invalid values upon reads. Read upgrade instructions.
 
 ### Removed
 - Classes marked as deprecated whose removal was planned after 3.4.0 release (See JAMES-2703). This includes:
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index d37ba88..7b29280 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -28,7 +28,25 @@ Change list:
  - [Hybrid blobStore replaces Union blobStore](#hybrid-blobstore-replaces-union-blobstore)
  - [New forbidden set of characters in Usernames local part](#new-forbidden-set-of-characters-in-usernames-local-part)
  - [UidValidity and maildir](#uid-validity-and-maildir)
+ - [UidValidity and JPA or Cassandra](#uid-validity-and-jpa-or-cassandra)
  
+### UidValidity and JPA or Cassandra
+
+Date 26/02/2020
+
+SHA-1 XXX
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3074
+
+Concerned products: JPA mailbox backend, Cassandra mailbox backend
+
+Non-Maildir backends could generate '0' uid validity with a low probability, which is invalid regarding RFC-3501. 
+We changed the generation mechanism to use valid UidValidity for newly created mailboxes. Regarding persisted mailboxes,
+we regenerate invalid UidValidity upon reads.
+
+While this sanitizing is transparent to the end user and the admin, it might lead to rare IMAP client full mailbox
+ resynchronisation. (one chance out of two billions).
+
 ### UidValidity and maildir
 
 Date 26/02/2020


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


[james-project] 07/17: JAMES-3105 Piggy back message denormalization checks on mailbox counters recomputation

Posted by bt...@apache.org.
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

commit 24250e2c3f50a3d7c006e567787692647152919c
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 7 13:50:11 2020 +0700

    JAMES-3105 Piggy back message denormalization checks on mailbox counters recomputation
    
    We can piggy back a partial check of the message denormalization upon
    counter recomputation (partial because we cannot detect missing
    entries in the "list messages in mailbox" denormalization table)
---
 .../mail/task/RecomputeMailboxCountersService.java      | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
index d45cb30..b057c93 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersService.java
@@ -193,6 +193,21 @@ public class RecomputeMailboxCountersService {
     private Flux<ComposedMessageIdWithMetaData> latestMetadata(CassandraId mailboxId, ComposedMessageIdWithMetaData message) {
         CassandraMessageId messageId = (CassandraMessageId) message.getComposedMessageId().getMessageId();
 
-        return messageIdToImapUidDAO.retrieve(messageId, Optional.of(mailboxId));
+        return messageIdToImapUidDAO.retrieve(messageId, Optional.of(mailboxId))
+            .doOnNext(trustedMessage -> {
+                if (!trustedMessage.equals(message)) {
+                    LOGGER.warn("Possible denormalization issue on {}. " +
+                            "Mismatch between the two denormalization table. " +
+                            "This can also be due to concurrent modifications.",
+                        message.getComposedMessageId());
+                }
+            })
+            .switchIfEmpty(Flux.<ComposedMessageIdWithMetaData>empty()
+                .doOnComplete(() -> {
+                    LOGGER.warn("Possible denormalization issue on {}. " +
+                            "Source of truth do not contain listed entry." +
+                            "This can also be due to concurrent modifications.",
+                        message.getComposedMessageId());
+                }));
     }
 }


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


[james-project] 17/17: JAMES-3074 UidValidity sanitizing at the IMAP level

Posted by bt...@apache.org.
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

commit 151db97de33fbffb6924cfe19273de8433e1fc8b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Mar 2 07:10:25 2020 +0100

    JAMES-3074 UidValidity sanitizing at the IMAP level
---
 .../parser/AbstractSelectionCommandParser.java     |  20 +---
 .../imap/decode/parser/ExamineCommandParser.java   |   4 +-
 .../imap/decode/parser/SelectCommandParser.java    |   4 +-
 .../request/AbstractMailboxSelectionRequest.java   | 125 ++++++++++++++++++++-
 .../james/imap/message/request/ExamineRequest.java |   3 +-
 .../james/imap/message/request/SelectRequest.java  |   3 +-
 .../imap/processor/AbstractSelectionProcessor.java |   9 +-
 .../james/imap/processor/ExamineProcessor.java     |   5 +-
 .../james/imap/processor/SelectProcessor.java      |   5 +-
 .../decode/parser/SelectCommandParserTest.java     |  17 ++-
 .../AbstractMailboxSelectionRequestTest.java       | 123 ++++++++++++++++++++
 11 files changed, 276 insertions(+), 42 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
index d183a29..5014107 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
@@ -32,8 +32,8 @@ import org.apache.james.imap.decode.ImapRequestLineReader;
 import org.apache.james.imap.decode.ImapRequestLineReader.StringMatcherCharacterValidator;
 import org.apache.james.imap.decode.base.AbstractImapCommandParser;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.model.UidValidity;
 
 public abstract class AbstractSelectionCommandParser extends AbstractImapCommandParser {
     private static final String CONDSTORE = ImapConstants.SUPPORTS_CONDSTORE.asString();
@@ -47,7 +47,7 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
     protected ImapMessage decode(ImapRequestLineReader request, Tag tag, ImapSession session) throws DecodingException {
         final String mailboxName = request.mailbox();
         boolean condstore = false;
-        UidValidity lastKnownUidValidity = null;
+        ClientSpecifiedUidValidity lastKnownUidValidity = ClientSpecifiedUidValidity.UNKNOWN;
         Long knownModSeq = null;
         UidRange[] uidSet = null;
         UidRange[] knownUidSet = null;
@@ -81,7 +81,7 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
                 // Consume enclosing paren
                 request.consumeChar('(');
                 long uidValidityAsNumber = request.number();
-                lastKnownUidValidity = sanitizeUidValidity(uidValidityAsNumber);
+                lastKnownUidValidity = ClientSpecifiedUidValidity.of(uidValidityAsNumber);
 
                 // Consume the SP
                 request.consumeChar(' ');
@@ -131,18 +131,6 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
         return createRequest(mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 
-    private UidValidity sanitizeUidValidity(long uidValidityAsNumber) {
-        if (UidValidity.isValid(uidValidityAsNumber)) {
-            return UidValidity.of(uidValidityAsNumber);
-        } else {
-            // The UidValidity cached by the client is invalid
-            // We know that the backend will regenerate it
-            // Hence we force the mismatch
-            // QRSYNC command will be ignored
-            return UidValidity.random();
-        }
-    }
-
     /**
      * Check if the {@link IdRange}'s are formatted like stated in the QRESYNC RFC.
      * 
@@ -220,5 +208,5 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
     /**
      * Create a new {@link AbstractMailboxSelectionRequest} for the given arguments
      */
-    protected abstract AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag);
+    protected abstract AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag);
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
index b2bef7e..e711e9d 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
@@ -24,8 +24,8 @@ import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.ExamineRequest;
-import org.apache.james.mailbox.model.UidValidity;
 
 /**
  * Parse EXAMINE commands
@@ -37,7 +37,7 @@ public class ExamineCommandParser extends AbstractSelectionCommandParser {
     }
 
     @Override
-    protected AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    protected AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         return new ExamineRequest(mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
index dd30d44..c9ad85a 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
@@ -24,8 +24,8 @@ import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.SelectRequest;
-import org.apache.james.mailbox.model.UidValidity;
 
 /**
  * Parse SELECT commands
@@ -36,7 +36,7 @@ public class SelectCommandParser extends AbstractSelectionCommandParser {
     }
 
     @Override
-    protected AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    protected AbstractMailboxSelectionRequest createRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         return new SelectRequest(mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
index 468e2b0..bfdb30d 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.imap.message.request;
 
+import java.util.Objects;
+
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
@@ -26,29 +28,144 @@ import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.request.ImapRequest;
 import org.apache.james.mailbox.model.UidValidity;
 
+import com.google.common.base.Preconditions;
+
 /**
  * {@link ImapRequest} which selects a Mailbox. 
  * 
  * This supports also the <code>CONDSTORE</code> and the <code>QRESYNC</code> extension
  */
 public abstract class AbstractMailboxSelectionRequest extends AbstractImapRequest {
+    public interface ClientSpecifiedUidValidity {
+        ClientSpecifiedUidValidity UNKNOWN = new ClientSpecifiedUidValidity() {
+            @Override
+            public boolean isUnknown() {
+                return true;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return false;
+            }
+
+            @Override
+            public String toString() {
+                return "UidValidity{UNKNOWN}";
+            }
+        };
+
+        static ClientSpecifiedUidValidity of(long value) {
+            if (UidValidity.isValid(value)) {
+                return valid(UidValidity.of(value));
+            }
+            return invalid(value);
+        }
+
+        class Invalid implements ClientSpecifiedUidValidity {
+            private final long invalidUidValidity;
+
+            public Invalid(long invalidUidValidity) {
+                Preconditions.checkArgument(!UidValidity.isValid(invalidUidValidity), "Need to supply an invalid value");
+                this.invalidUidValidity = invalidUidValidity;
+            }
+
+            @Override
+            public boolean isUnknown() {
+                return false;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return false;
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Invalid) {
+                    Invalid invalid = (Invalid) o;
+
+                    return Objects.equals(this.invalidUidValidity, invalid.invalidUidValidity);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(invalidUidValidity);
+            }
+
+            @Override
+            public String toString() {
+                return String.format("Invalid UidValidity{%d}", invalidUidValidity);
+            }
+        }
 
+        class Valid implements ClientSpecifiedUidValidity {
+            private final UidValidity uidValidity;
+
+            public Valid(UidValidity uidValidity) {
+                this.uidValidity = uidValidity;
+            }
+
+            @Override
+            public boolean isUnknown() {
+                return false;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return this.uidValidity.equals(uidValidity);
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Valid) {
+                    Valid valid = (Valid) o;
+
+                    return Objects.equals(this.uidValidity, valid.uidValidity);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(uidValidity);
+            }
+
+            @Override
+            public String toString() {
+                return String.format("UidValidity{%d}", uidValidity.asLong());
+            }
+        }
+
+        static ClientSpecifiedUidValidity invalid(long invalidValue) {
+            return new Invalid(invalidValue);
+        }
+
+        static ClientSpecifiedUidValidity valid(UidValidity uidValidity) {
+            return new Valid(uidValidity);
+        }
+
+        boolean isUnknown();
+
+        boolean correspondsTo(UidValidity uidValidity);
+    }
 
     private final String mailboxName;
     private final boolean condstore;
-    private final UidValidity lastKnownUidValidity;
+    private final ClientSpecifiedUidValidity lastKnownUidValidity;
     private final Long knownModSeq;
     private final UidRange[] uidSet;
     private final UidRange[] knownUidSet;
     private final IdRange[] knownSequenceSet;
 
-    public AbstractMailboxSelectionRequest(ImapCommand command, String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    public AbstractMailboxSelectionRequest(ImapCommand command, String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(tag, command);
         this.mailboxName = mailboxName;
         this.condstore = condstore;
         this.lastKnownUidValidity = lastKnownUidValidity;
         this.knownModSeq = knownModSeq;
-        if ((lastKnownUidValidity == null && knownModSeq != null) || (this.lastKnownUidValidity != null && knownModSeq == null)) {
+        if ((lastKnownUidValidity.isUnknown() && knownModSeq != null) || (! lastKnownUidValidity.isUnknown() && knownModSeq == null)) {
             throw new IllegalArgumentException();
         }
         this.uidSet = uidSet;
@@ -80,7 +197,7 @@ public abstract class AbstractMailboxSelectionRequest extends AbstractImapReques
      * 
      * @return lastKnownUidValidity
      */
-    public final UidValidity getLastKnownUidValidity() {
+    public final ClientSpecifiedUidValidity getLastKnownUidValidity() {
         return lastKnownUidValidity;
     }
     
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
index a3989e8..208c0d9 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
@@ -22,10 +22,9 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
-import org.apache.james.mailbox.model.UidValidity;
 
 public class ExamineRequest extends AbstractMailboxSelectionRequest {
-    public ExamineRequest(String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    public ExamineRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.EXAMINE_COMMAND, mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
index b8bf1a3..d7b98ad 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
@@ -22,10 +22,9 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
-import org.apache.james.mailbox.model.UidValidity;
 
 public class SelectRequest extends AbstractMailboxSelectionRequest {
-    public SelectRequest(String mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    public SelectRequest(String mailboxName, boolean condstore, ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.SELECT_COMMAND, mailboxName, condstore, lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
index e243fdf..11edf91 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
@@ -38,6 +38,7 @@ import org.apache.james.imap.api.process.SearchResUtil;
 import org.apache.james.imap.api.process.SelectedMailbox;
 import org.apache.james.imap.main.PathConverter;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.response.ExistsResponse;
 import org.apache.james.imap.message.response.RecentResponse;
 import org.apache.james.imap.processor.base.SelectedMailboxImpl;
@@ -98,7 +99,7 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
 
     private void respond(ImapSession session, MailboxPath fullMailboxPath, AbstractMailboxSelectionRequest request, Responder responder) throws MailboxException, MessageRangeException {
 
-        UidValidity lastKnownUidValidity = request.getLastKnownUidValidity();
+        ClientSpecifiedUidValidity lastKnownUidValidity = request.getLastKnownUidValidity();
         Long modSeq = request.getKnownModSeq();
         IdRange[] knownSequences = request.getKnownSequenceSet();
         UidRange[] knownUids = request.getKnownUidSet();
@@ -112,7 +113,7 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
         //    Resynchronization parameter to SELECT/EXAMINE command is specified
         //    and the client hasn't issued "ENABLE QRESYNC" in the current
         //    connection.
-        if (lastKnownUidValidity != null && !EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
+        if (!lastKnownUidValidity.isUnknown() && !EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC)) {
             taggedBad(request, responder, HumanReadableText.QRESYNC_NOT_ENABLED);
             return;
         }
@@ -154,8 +155,8 @@ abstract class AbstractSelectionProcessor<R extends AbstractMailboxSelectionRequ
         // 
         // If the mailbox does not store the mod-sequence in a permanent way its needed to not process the QRESYNC paramters
         // The same is true if none are given ;)
-        if (metaData.isModSeqPermanent() && lastKnownUidValidity != null) {
-            if (lastKnownUidValidity == metaData.getUidValidity()) {
+        if (metaData.isModSeqPermanent() && !lastKnownUidValidity.isUnknown()) {
+            if (lastKnownUidValidity.correspondsTo(metaData.getUidValidity())) {
                 
                 final MailboxManager mailboxManager = getMailboxManager();
                 final MailboxSession mailboxSession = session.getMailboxSession();
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
index 0468251..5486524 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
@@ -20,7 +20,6 @@
 package org.apache.james.imap.processor;
 
 import java.io.Closeable;
-import java.util.Optional;
 
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
@@ -29,7 +28,6 @@ import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.message.request.ExamineRequest;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.events.EventBus;
-import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
 
@@ -49,8 +47,7 @@ public class ExamineProcessor extends AbstractSelectionProcessor<ExamineRequest>
             .addContext("knownModseq", request.getKnownModSeq())
             .addContext("knownUids", UidRange.toString(request.getKnownUidSet()))
             .addContext("knownIdRange", IdRange.toString(request.getKnownSequenceSet()))
-            .addContext("lastKnownUidValidity", Optional.ofNullable(request.getLastKnownUidValidity())
-                .map(UidValidity::asLong))
+            .addContext("lastKnownUidValidity", request.getLastKnownUidValidity())
             .addContext("uidSet", UidRange.toString(request.getUidSet()))
             .build();
     }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
index 12a67aa..6d9bb18 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
@@ -20,7 +20,6 @@
 package org.apache.james.imap.processor;
 
 import java.io.Closeable;
-import java.util.Optional;
 
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
@@ -29,7 +28,6 @@ import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.message.request.SelectRequest;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.events.EventBus;
-import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
 
@@ -49,8 +47,7 @@ public class SelectProcessor extends AbstractSelectionProcessor<SelectRequest> {
             .addContext("knownModseq", message.getKnownModSeq())
             .addContext("knownUids", UidRange.toString(message.getKnownUidSet()))
             .addContext("knownIdRange", IdRange.toString(message.getKnownSequenceSet()))
-            .addContext("lastKnownUidValidity", Optional.ofNullable(message.getLastKnownUidValidity())
-                .map(UidValidity::asLong))
+            .addContext("lastKnownUidValidity", message.getLastKnownUidValidity())
             .addContext("uidSet", UidRange.toString(message.getUidSet()))
             .build();
     }
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
index 683385b..6d73240 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
@@ -30,7 +30,10 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.decode.ImapRequestStreamLineReader;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.SelectRequest;
+import org.apache.james.mailbox.model.UidValidity;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -48,8 +51,18 @@ class SelectCommandParserTest {
 
         SelectRequest selectRequest = (SelectRequest) testee.decode(request, new Tag("0001"), mock(ImapSession.class));
 
-        assertThat(selectRequest.getLastKnownUidValidity().isValid())
-            .isTrue();
+        assertThat(selectRequest.getLastKnownUidValidity())
+            .isEqualTo(ClientSpecifiedUidValidity.invalid(0));
+    }
+
+    @Test
+    void decodeShouldParseValidUidValidity() throws Exception {
+        ImapRequestStreamLineReader request = toRequest("mailbox (QRESYNC (18 42))\n");
+
+        SelectRequest selectRequest = (SelectRequest) testee.decode(request, new Tag("0001"), mock(ImapSession.class));
+
+        assertThat(selectRequest.getLastKnownUidValidity())
+            .isEqualTo(ClientSpecifiedUidValidity.valid(UidValidity.of(18)));
     }
 
     private ImapRequestStreamLineReader toRequest(String input) {
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java b/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
new file mode 100644
index 0000000..8e50036
--- /dev/null
+++ b/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
@@ -0,0 +1,123 @@
+/****************************************************************
+ * 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.imap.message.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
+import org.apache.james.mailbox.model.UidValidity;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Nested;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class AbstractMailboxSelectionRequestTest {
+    private static final ClientSpecifiedUidValidity INVALID = ClientSpecifiedUidValidity.invalid(0);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
+    private static final ClientSpecifiedUidValidity VALID = ClientSpecifiedUidValidity.valid(UID_VALIDITY);
+
+    @Nested
+    class Unknown {
+        @Test
+        void isUnknownShouldReturnTrue() {
+            assertThat(ClientSpecifiedUidValidity.UNKNOWN.isUnknown())
+                .isTrue();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalse() {
+            assertThat(ClientSpecifiedUidValidity.UNKNOWN.correspondsTo(UID_VALIDITY))
+                .isFalse();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(ClientSpecifiedUidValidity.UNKNOWN.toString())
+                .isEqualTo("UidValidity{UNKNOWN}");
+        }
+    }
+
+    @Nested
+    class Invalid {
+        @Test
+        void shouldMatchBeanContract() {
+            EqualsVerifier.forClass(ClientSpecifiedUidValidity.Invalid.class)
+                .verify();
+        }
+
+        @Test
+        void invalidShouldThrowOnValidValue() {
+            assertThatThrownBy(() -> ClientSpecifiedUidValidity.invalid(42))
+                .isInstanceOf(IllegalArgumentException.class);
+        }
+
+        @Test
+        void isUnknownShouldReturnFalse() {
+            assertThat(INVALID.isUnknown())
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalse() {
+            assertThat(INVALID.correspondsTo(UID_VALIDITY))
+                .isFalse();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(INVALID.toString())
+                .isEqualTo("Invalid UidValidity{0}");
+        }
+    }
+
+    @Nested
+    class Valid {
+        @Test
+        void shouldMatchBeanContract() {
+            EqualsVerifier.forClass(ClientSpecifiedUidValidity.Valid.class)
+                .verify();
+        }
+
+        @Test
+        void isUnknownShouldReturnFalse() {
+            assertThat(VALID.isUnknown())
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalseWhenDifferent() {
+            assertThat(VALID.correspondsTo(UidValidity.of(40)))
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnTrueWhenSame() {
+            assertThat(VALID.correspondsTo(UID_VALIDITY))
+                .isTrue();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(VALID.toString())
+                .isEqualTo("UidValidity{42}");
+        }
+    }
+}
\ No newline at end of file


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


[james-project] 12/17: JAMES-3074 Cassandra: On the fly UidValidity sanitizing

Posted by bt...@apache.org.
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

commit 819f25da21c96383e930827e585ecedf0c7078a3
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 14:37:19 2020 +0700

    JAMES-3074 Cassandra: On the fly UidValidity sanitizing
    
    Impact: minor. Statistically, one entry out of 2 billion is affected.
---
 .../cassandra/mail/CassandraMailboxDAO.java        | 52 +++++++++++++----
 .../cassandra/mail/CassandraMailboxDAOTest.java    | 68 ++++++++++++++++++++++
 2 files changed, 108 insertions(+), 12 deletions(-)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
index 02a7b13..87c9505 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
@@ -65,6 +65,7 @@ public class CassandraMailboxDAO {
     private final PreparedStatement deleteStatement;
     private final PreparedStatement insertStatement;
     private final PreparedStatement updateStatement;
+    private final PreparedStatement updateUidValidityStatement;
 
     @Inject
     public CassandraMailboxDAO(Session session, CassandraTypesProvider typesProvider, CassandraUtils cassandraUtils) {
@@ -72,6 +73,7 @@ public class CassandraMailboxDAO {
         this.mailboxBaseTupleUtil = new MailboxBaseTupleUtil(typesProvider);
         this.insertStatement = prepareInsert(session);
         this.updateStatement = prepareUpdate(session);
+        this.updateUidValidityStatement = prepareUpdateUidValidity(session);
         this.deleteStatement = prepareDelete(session);
         this.listStatement = prepareList(session);
         this.readStatement = prepareRead(session);
@@ -98,6 +100,12 @@ public class CassandraMailboxDAO {
             .where(eq(ID, bindMarker(ID))));
     }
 
+    private PreparedStatement prepareUpdateUidValidity(Session session) {
+        return session.prepare(update(TABLE_NAME)
+            .with(set(UIDVALIDITY, bindMarker(UIDVALIDITY)))
+            .where(eq(ID, bindMarker(ID))));
+    }
+
     private PreparedStatement prepareDelete(Session session) {
         return session.prepare(QueryBuilder.delete()
             .from(TABLE_NAME)
@@ -138,27 +146,47 @@ public class CassandraMailboxDAO {
         return executor.executeSingleRow(readStatement.bind()
             .setUUID(ID, mailboxId.asUuid())
             .setConsistencyLevel(QUORUM))
-            .map(row -> mailboxFromRow(row, mailboxId));
+            .flatMap(row -> mailboxFromRow(row, mailboxId));
     }
 
-    private Mailbox mailboxFromRow(Row row, CassandraId cassandraId) {
-        return new Mailbox(
-            new MailboxPath(
-                row.getUDTValue(MAILBOX_BASE).getString(CassandraMailboxTable.MailboxBase.NAMESPACE),
-                Username.of(row.getUDTValue(MAILBOX_BASE).getString(CassandraMailboxTable.MailboxBase.USER)),
-                row.getString(NAME)),
-            UidValidity.of(row.getLong(UIDVALIDITY)),
-            cassandraId);
+    private Mono<Mailbox> mailboxFromRow(Row row, CassandraId cassandraId) {
+        return sanitizeUidValidity(cassandraId, row.getLong(UIDVALIDITY))
+            .map(uidValidity -> new Mailbox(
+                new MailboxPath(
+                    row.getUDTValue(MAILBOX_BASE).getString(CassandraMailboxTable.MailboxBase.NAMESPACE),
+                    Username.of(row.getUDTValue(MAILBOX_BASE).getString(CassandraMailboxTable.MailboxBase.USER)),
+                    row.getString(NAME)),
+                uidValidity,
+                cassandraId));
+    }
+    
+    private Mono<UidValidity> sanitizeUidValidity(CassandraId cassandraId, long uidValidityAsLong) {
+        if (!UidValidity.isValid(uidValidityAsLong)) {
+            UidValidity newUidValidity = UidValidity.generate();
+            return updateUidValidity(cassandraId, newUidValidity)
+                .then(Mono.just(newUidValidity));
+        }
+        return Mono.just(UidValidity.ofValid(uidValidityAsLong));
+    }
+
+    /**
+     * Expected concurrency issue in the absence of performance expensive LightWeight transaction
+     * As the Uid validity is updated only when equal to 0 (1 chance out of 4 billion) the benefits of LWT don't
+     * outweigh the performance costs
+     */
+    private Mono<Void> updateUidValidity(CassandraId cassandraId, UidValidity uidValidity) {
+        return executor.executeVoid(updateUidValidityStatement.bind()
+                .setUUID(ID, cassandraId.asUuid())
+                .setLong(UIDVALIDITY, uidValidity.asLong()));
     }
 
     public Flux<Mailbox> retrieveAllMailboxes() {
         return executor.execute(listStatement.bind())
             .flatMapMany(cassandraUtils::convertToFlux)
-            .map(this::toMailboxWithId);
+            .flatMap(this::toMailboxWithId);
     }
 
-    private Mailbox toMailboxWithId(Row row) {
+    private Mono<Mailbox> toMailboxWithId(Row row) {
         return mailboxFromRow(row, CassandraId.of(row.getUUID(ID)));
     }
-
 }
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
index 9aa1486..54d0134 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
@@ -19,6 +19,10 @@
 
 package org.apache.james.mailbox.cassandra.mail;
 
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
+import static org.apache.james.backends.cassandra.Scenario.Builder.awaitOn;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.List;
@@ -26,6 +30,8 @@ import java.util.Optional;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.backends.cassandra.Scenario;
+import org.apache.james.backends.cassandra.Scenario.Barrier;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionModule;
 import org.apache.james.core.Username;
@@ -37,9 +43,13 @@ import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.UidValidity;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
 class CassandraMailboxDAOTest {
     private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(145);
     private static final UidValidity UID_VALIDITY_2 = UidValidity.ofValid(147);
@@ -84,6 +94,64 @@ class CassandraMailboxDAOTest {
     }
 
     @Test
+    void retrieveMailboxShouldSanitizeInvalidUidValidityUponRead(CassandraCluster cassandra) {
+        testee.save(mailbox1).block();
+
+        // Hack to insert a faulty value
+        cassandra.getConf().execute(update("mailbox")
+            .with(set("uidvalidity", -12))
+            .where(eq("id", CASSANDRA_ID_1.asUuid())));
+
+        Optional<Mailbox> readMailbox = testee.retrieveMailbox(CASSANDRA_ID_1)
+            .blockOptional();
+        assertThat(readMailbox).isPresent()
+            .hasValueSatisfying(mailbox -> assertThat(mailbox.getUidValidity().isValid()).isTrue());
+    }
+
+    @Test
+    void retrieveAllShouldSanitizeInvalidUidValidityUponRead(CassandraCluster cassandra) {
+        testee.save(mailbox1).block();
+
+        // Hack to insert a faulty value
+        cassandra.getConf().execute(update("mailbox")
+            .with(set("uidvalidity", -12))
+            .where(eq("id", CASSANDRA_ID_1.asUuid())));
+
+        List<Mailbox> readMailbox = testee.retrieveAllMailboxes().collectList().block();
+        assertThat(readMailbox).hasSize(1)
+            .allSatisfy(mailbox -> assertThat(mailbox.getUidValidity().isValid()).isTrue());
+    }
+
+    @Disabled("Expected concurrency issue in the absence of performance expensive LightWeight transaction" +
+        "As the Uid validity is updated only when equal to 0 (1 chance out of 4 billion) the benefits of LWT don't" +
+        "outweigh the costs")
+    @Test
+    void retrieveMailboxShouldNotBeSubjectToDataRaceUponUidValiditySanitizing(CassandraCluster cassandra) throws Exception {
+        testee.save(mailbox1).block();
+
+        // Hack to insert a faulty value
+        cassandra.getConf().execute(update("mailbox")
+            .with(set("uidvalidity", -12))
+            .where(eq("id", CASSANDRA_ID_1.asUuid())));
+
+        Barrier barrier = new Barrier(2);
+        cassandra.getConf().registerScenario(awaitOn(barrier)
+            .times(2)
+            .whenQueryStartsWith("UPDATE mailbox SET"));
+
+        Mono<Mailbox> readMailbox1 = testee.retrieveMailbox(CASSANDRA_ID_1).cache();
+        Mono<Mailbox> readMailbox2 = testee.retrieveMailbox(CASSANDRA_ID_1).cache();
+        readMailbox1.subscribeOn(Schedulers.elastic()).subscribe();
+        readMailbox2.subscribeOn(Schedulers.elastic()).subscribe();
+
+        barrier.awaitCaller();
+        barrier.releaseCaller();
+
+        assertThat(readMailbox1.block().getUidValidity())
+            .isEqualTo(readMailbox2.block().getUidValidity());
+    }
+
+    @Test
     void saveShouldOverride() {
         testee.save(mailbox1).block();
 


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


[james-project] 04/17: JAMES-3105 Task for recomputing mailbox counters

Posted by bt...@apache.org.
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

commit e5915c83cf4f39a8807d3a9a59bd8ef09ab80341
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 6 22:45:03 2020 +0700

    JAMES-3105 Task for recomputing mailbox counters
    
    A simple wrapper around the already existing service...
---
 .../mail/task/RecomputeMailboxCountersTask.java    | 102 +++++++++++++++++++++
 ...ailboxCountersTaskAdditionalInformationDTO.java |  87 ++++++++++++++++++
 .../mail/task/RecomputeMailboxCountersTaskDTO.java |  56 +++++++++++
 ...omputeMailboxCountersTaskSerializationTest.java |  66 +++++++++++++
 4 files changed, 311 insertions(+)

diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTask.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTask.java
new file mode 100644
index 0000000..74082f3
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTask.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.task;
+
+import static org.apache.james.mailbox.cassandra.mail.task.RecomputeMailboxCountersService.Context.Snapshot;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Optional;
+
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskExecutionDetails;
+import org.apache.james.task.TaskType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.github.steveash.guavate.Guavate;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.scheduler.Schedulers;
+
+public class RecomputeMailboxCountersTask implements Task {
+    public static final Logger LOGGER = LoggerFactory.getLogger(RecomputeMailboxCountersTask.class);
+    static final TaskType RECOMPUTE_MAILBOX_COUNTERS = TaskType.of("recompute-mailbox-counters");
+
+    public static class Details implements TaskExecutionDetails.AdditionalInformation {
+        private final Instant instant;
+        private final long processedMailboxes;
+        private final ImmutableList<String> failedMailboxes;
+
+        Details(Instant instant, long processedMailboxes, ImmutableList<String> failedMailboxes) {
+            this.instant = instant;
+            this.processedMailboxes = processedMailboxes;
+            this.failedMailboxes = failedMailboxes;
+        }
+
+        @Override
+        public Instant timestamp() {
+            return instant;
+        }
+
+        @JsonProperty("processedMailboxes")
+        long getProcessedMailboxes() {
+            return processedMailboxes;
+        }
+
+        @JsonProperty("failedMailboxes")
+        ImmutableList<String> getFailedMailboxes() {
+            return failedMailboxes;
+        }
+    }
+
+    private final RecomputeMailboxCountersService service;
+    private RecomputeMailboxCountersService.Context context;
+
+    public RecomputeMailboxCountersTask(RecomputeMailboxCountersService service) {
+        this.service = service;
+        this.context = new RecomputeMailboxCountersService.Context();
+    }
+
+    @Override
+    public Result run() {
+        return service.recomputeMailboxCounters(context)
+            .subscribeOn(Schedulers.elastic())
+            .block();
+    }
+
+    @Override
+    public TaskType type() {
+        return RECOMPUTE_MAILBOX_COUNTERS;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        Snapshot snapshot = context.snapshot();
+
+        return Optional.of(new Details(Clock.systemUTC().instant(),
+            snapshot.getProcessedMailboxCount(),
+            snapshot.getFailedMailboxes().stream()
+                .map(MailboxId::serialize)
+                .collect(Guavate.toImmutableList())));
+    }
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskAdditionalInformationDTO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskAdditionalInformationDTO.java
new file mode 100644
index 0000000..e7eef3d
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskAdditionalInformationDTO.java
@@ -0,0 +1,87 @@
+/****************************************************************
+ * 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.task;
+
+import java.time.Instant;
+
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTO;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableList;
+
+public class RecomputeMailboxCountersTaskAdditionalInformationDTO implements AdditionalInformationDTO {
+    private static RecomputeMailboxCountersTaskAdditionalInformationDTO fromDomainObject(RecomputeMailboxCountersTask.Details details, String type) {
+        return new RecomputeMailboxCountersTaskAdditionalInformationDTO(
+            type,
+            details.getProcessedMailboxes(),
+            details.getFailedMailboxes(),
+            details.timestamp());
+    }
+
+    public static final AdditionalInformationDTOModule<RecomputeMailboxCountersTask.Details, RecomputeMailboxCountersTaskAdditionalInformationDTO> MODULE =
+        DTOModule
+            .forDomainObject(RecomputeMailboxCountersTask.Details.class)
+            .convertToDTO(RecomputeMailboxCountersTaskAdditionalInformationDTO.class)
+            .toDomainObjectConverter(RecomputeMailboxCountersTaskAdditionalInformationDTO::toDomainObject)
+            .toDTOConverter(RecomputeMailboxCountersTaskAdditionalInformationDTO::fromDomainObject)
+            .typeName(RecomputeMailboxCountersTask.RECOMPUTE_MAILBOX_COUNTERS.asString())
+            .withFactory(AdditionalInformationDTOModule::new);
+
+    private final String type;
+    private final long processedMailboxes;
+    private final ImmutableList<String> failedMailboxes;
+    private final Instant timestamp;
+
+    public RecomputeMailboxCountersTaskAdditionalInformationDTO(@JsonProperty("type") String type,
+                                                                @JsonProperty("processedMailboxes") long processedMailboxes,
+                                                                @JsonProperty("failedMailboxes") ImmutableList<String> failedMailboxes,
+                                                                @JsonProperty("timestamp") Instant timestamp) {
+        this.type = type;
+        this.processedMailboxes = processedMailboxes;
+        this.failedMailboxes = failedMailboxes;
+        this.timestamp = timestamp;
+    }
+
+    public long getProcessedMailboxes() {
+        return processedMailboxes;
+    }
+
+    public ImmutableList<String> getFailedMailboxes() {
+        return failedMailboxes;
+    }
+
+    @Override
+    public Instant getTimestamp() {
+        return timestamp;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    private RecomputeMailboxCountersTask.Details toDomainObject() {
+        return new RecomputeMailboxCountersTask.Details(timestamp,
+            processedMailboxes,
+            failedMailboxes);
+    }
+}
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskDTO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskDTO.java
new file mode 100644
index 0000000..b8fa112
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskDTO.java
@@ -0,0 +1,56 @@
+/****************************************************************
+ * 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.task;
+
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.TaskDTO;
+import org.apache.james.server.task.json.dto.TaskDTOModule;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RecomputeMailboxCountersTaskDTO implements TaskDTO {
+    private static RecomputeMailboxCountersTaskDTO toDTO(RecomputeMailboxCountersTask domainObject, String typeName) {
+        return new RecomputeMailboxCountersTaskDTO(typeName);
+    }
+
+    public static TaskDTOModule<RecomputeMailboxCountersTask, RecomputeMailboxCountersTaskDTO> module(RecomputeMailboxCountersService service) {
+        return DTOModule
+            .forDomainObject(RecomputeMailboxCountersTask.class)
+            .convertToDTO(RecomputeMailboxCountersTaskDTO.class)
+            .toDomainObjectConverter(dto -> dto.toDomainObject(service))
+            .toDTOConverter(RecomputeMailboxCountersTaskDTO::toDTO)
+            .typeName(RecomputeMailboxCountersTask.RECOMPUTE_MAILBOX_COUNTERS.asString())
+            .withFactory(TaskDTOModule::new);
+    }
+
+    private final String type;
+
+    public RecomputeMailboxCountersTaskDTO(@JsonProperty("type") String type) {
+        this.type = type;
+    }
+
+    private RecomputeMailboxCountersTask toDomainObject(RecomputeMailboxCountersService service) {
+        return new RecomputeMailboxCountersTask(service);
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+}
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskSerializationTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskSerializationTest.java
new file mode 100644
index 0000000..15c787c
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersTaskSerializationTest.java
@@ -0,0 +1,66 @@
+/****************************************************************
+ * 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.task;
+
+import static org.mockito.Mockito.mock;
+
+import java.time.Instant;
+import java.util.UUID;
+
+import org.apache.james.JsonSerializationVerifier;
+import org.apache.james.core.Username;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.model.MailboxConstants;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+class RecomputeMailboxCountersTaskSerializationTest {
+    private static final Instant TIMESTAMP = Instant.parse("2018-11-13T12:00:55Z");
+    private static final String MAILBOX_ID_AS_STRING = "464765a0-e4e7-11e4-aba4-710c1de3782b";
+
+    private static final RecomputeMailboxCountersService SERVICE = mock(RecomputeMailboxCountersService.class);
+    private static final RecomputeMailboxCountersTask TASK = new RecomputeMailboxCountersTask(SERVICE);
+    private static final String SERIALIZED_TASK = "{\"type\": \"recompute-mailbox-counters\"}";
+    private static final RecomputeMailboxCountersTask.Details DETAILS = new RecomputeMailboxCountersTask.Details(TIMESTAMP, 12, ImmutableList.of(MAILBOX_ID_AS_STRING));
+    private static final String SERIALIZED_ADDITIONAL_INFORMATION = "{" +
+        "  \"type\":\"recompute-mailbox-counters\"," +
+        "  \"processedMailboxes\":12," +
+        "  \"failedMailboxes\":[\"464765a0-e4e7-11e4-aba4-710c1de3782b\"]," +
+        "  \"timestamp\":\"2018-11-13T12:00:55Z\"" +
+        "}";
+
+    @Test
+    void taskShouldBeSerializable() throws Exception {
+        JsonSerializationVerifier.dtoModule(RecomputeMailboxCountersTaskDTO.module(SERVICE))
+            .bean(TASK)
+            .json(SERIALIZED_TASK)
+            .verify();
+    }
+
+    @Test
+    void additionalInformationShouldBeSerializable() throws Exception {
+        JsonSerializationVerifier.dtoModule(RecomputeMailboxCountersTaskAdditionalInformationDTO.MODULE)
+            .bean(DETAILS)
+            .json(SERIALIZED_ADDITIONAL_INFORMATION)
+            .verify();
+    }
+}
\ No newline at end of file


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


[james-project] 11/17: JAMES-3074 Maildir: On the fly UidValidity sanitizing

Posted by bt...@apache.org.
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

commit 837299290848cdc11a90c2e73c4af549a0f1106f
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 12:01:12 2020 +0700

    JAMES-3074 Maildir: On the fly UidValidity sanitizing
    
    Impact: major. All the generated value are invalids.
---
 CHANGELOG.md                                             |  1 +
 .../java/org/apache/james/mailbox/model/UidValidity.java |  2 +-
 .../task/SolveMailboxInconsistenciesServiceTest.java     |  4 ++--
 .../org/apache/james/mailbox/maildir/MaildirFolder.java  | 10 +++++++++-
 upgrade-instructions.md                                  | 16 ++++++++++++++++
 5 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 03c3253..b846a76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ of tasks being currently executed.
 - JAMES-2972 Incorrect attribute name in the mailet configuration thanks to jtconsol
 - JAMES-2632 JMAP Draft GetMailboxes performance enhancements when retrieving all mailboxes of a user
 - JAMES-2964 Forbid to create User quota/ Domain quota/ Global quota using negative number
+- JAMES-3074 Maildir backend generated invalid UidValidity. Read upgrade instructions.
 
 ### Removed
 - Classes marked as deprecated whose removal was planned after 3.4.0 release (See JAMES-2703). This includes:
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
index 05e0f03..05d65a9 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
@@ -64,7 +64,7 @@ public class UidValidity {
         return new UidValidity(uidValidity);
     }
 
-    private static boolean isValid(long uidValidityAsLong) {
+    public static boolean isValid(long uidValidityAsLong) {
         return uidValidityAsLong > 0 && uidValidityAsLong < UPPER_EXCLUSIVE_BOUND;
     }
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesServiceTest.java
index 804d6f9..97cc2f2 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesServiceTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMailboxInconsistenciesServiceTest.java
@@ -48,8 +48,8 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class SolveMailboxInconsistenciesServiceTest {
-    private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(145);
-    private static final UidValidity UID_VALIDITY_2 = UidValidity.ofValid(147);
+    private static final UidValidity UID_VALIDITY_1 = UidValidity.of(145);
+    private static final UidValidity UID_VALIDITY_2 = UidValidity.of(147);
     private static final Username USER = Username.of("user");
     private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER, "abc");
     private static final MailboxPath NEW_MAILBOX_PATH = MailboxPath.forUser(USER, "xyz");
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
index 6809c8f..88148ff 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
@@ -281,10 +281,18 @@ public class MaildirFolder {
              InputStreamReader isr = new InputStreamReader(fis)) {
             char[] uidValidity = new char[20];
             int len = isr.read(uidValidity);
-            return UidValidity.of(Long.parseLong(String.valueOf(uidValidity, 0, len).trim()));
+            long uidValidityValue = Long.parseLong(String.valueOf(uidValidity, 0, len).trim());
+            return sanitizeUidValidity(uidValidityValue);
         }
     }
 
+    private UidValidity sanitizeUidValidity(long uidValidityValue) throws IOException {
+        if (!UidValidity.isValid(uidValidityValue)) {
+            return resetUidValidity();
+        }
+        return UidValidity.ofValid(uidValidityValue);
+    }
+
     /**
      * Save the given uidValidity to the file system
      */
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index e77d568..d37ba88 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -27,6 +27,22 @@ Change list:
  - [User mailboxes reIndexing endpoint change](#user-mailboxes-reindexing-endpoint-change)
  - [Hybrid blobStore replaces Union blobStore](#hybrid-blobstore-replaces-union-blobstore)
  - [New forbidden set of characters in Usernames local part](#new-forbidden-set-of-characters-in-usernames-local-part)
+ - [UidValidity and maildir](#uid-validity-and-maildir)
+ 
+### UidValidity and maildir
+
+Date 26/02/2020
+
+SHA-1 XXX
+
+JIRA: https://issues.apache.org/jira/browse/JAMES-3074
+
+Concerned products: Spring with maildir backend
+
+Maildir generated too big values to conform to RFC-3501. We changed the generation mechanism to use valid UidValidity 
+for newly created mailboxes. Regarding persisted mailboxes, we regenerate invalid UidValidity upon reads.
+
+While this sanitizing is transparent to the end user and the admin, it might lead to IMAP client full resynchronisation.
  
 ### New forbidden set of characters in Usernames local part
 


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


[james-project] 13/17: JAMES-3074 JPA: On the fly UidValidity sanitizing

Posted by bt...@apache.org.
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

commit 9c8a18e267b0d320ef8c2f770e036ce132e2bb1a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 14:47:41 2020 +0700

    JAMES-3074 JPA: On the fly UidValidity sanitizing
    
    Impact: minor. Statistically, one entry out of 2 billion is affected.
---
 .../james/mailbox/jpa/mail/model/JPAMailbox.java   | 25 ++++++++++++--
 .../mailbox/jpa/mail/JpaMailboxMapperTest.java     | 38 ++++++++++++++++++++++
 .../store/mail/model/MailboxMapperTest.java        |  4 +--
 3 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
index 730ce90..d775974 100644
--- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
+++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
@@ -35,6 +35,8 @@ import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.UidValidity;
 
+import com.google.common.annotations.VisibleForTesting;
+
 @Entity(name = "Mailbox")
 @Table(name = "JAMES_MAILBOX")
 @NamedQueries({
@@ -107,10 +109,15 @@ public class JPAMailbox {
     }
     
     public JPAMailbox(MailboxPath path, UidValidity uidValidity) {
+        this(path, uidValidity.asLong());
+    }
+
+    @VisibleForTesting
+    public JPAMailbox(MailboxPath path, long uidValidity) {
         this.name = path.getName();
         this.user = path.getUser().asString();
         this.namespace = path.getNamespace();
-        this.uidValidity = uidValidity.asLong();
+        this.uidValidity = uidValidity;
     }
 
     public JPAMailbox(Mailbox mailbox) {
@@ -131,7 +138,17 @@ public class JPAMailbox {
 
     public Mailbox toMailbox() {
         MailboxPath path = new MailboxPath(namespace, Username.of(user), name);
-        return new Mailbox(path, UidValidity.of(uidValidity), new JPAId(mailboxId));
+        return new Mailbox(path, sanitizeUidValidity(), new JPAId(mailboxId));
+    }
+
+    private UidValidity sanitizeUidValidity() {
+        if (UidValidity.isValid(uidValidity)) {
+            return UidValidity.ofValid(uidValidity);
+        }
+        UidValidity sanitizedUidValidity = UidValidity.generate();
+        // Update storage layer thanks to JPA magics!
+        setUidValidity(sanitizedUidValidity.asLong());
+        return sanitizedUidValidity;
     }
 
     public void setMailboxId(long mailboxId) {
@@ -158,6 +175,10 @@ public class JPAMailbox {
         this.namespace = namespace;
     }
 
+    public void setUidValidity(long uidValidity) {
+        this.uidValidity = uidValidity;
+    }
+
     @Override
     public String toString() {
         return "Mailbox ( "
diff --git a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMailboxMapperTest.java b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMailboxMapperTest.java
index a697b87..9be8ff1 100644
--- a/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMailboxMapperTest.java
+++ b/mailbox/jpa/src/test/java/org/apache/james/mailbox/jpa/mail/JpaMailboxMapperTest.java
@@ -19,15 +19,22 @@
 
 package org.apache.james.mailbox.jpa.mail;
 
+import static org.assertj.core.api.Assertions.assertThat;
+
 import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.persistence.EntityManager;
+
 import org.apache.james.backends.jpa.JpaTestCluster;
 import org.apache.james.mailbox.jpa.JPAId;
 import org.apache.james.mailbox.jpa.JPAMailboxFixture;
+import org.apache.james.mailbox.jpa.mail.model.JPAMailbox;
+import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.model.MailboxMapperTest;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
 
 class JpaMailboxMapperTest extends MailboxMapperTest {
 
@@ -49,4 +56,35 @@ class JpaMailboxMapperTest extends MailboxMapperTest {
     void cleanUp() {
         JPA_TEST_CLUSTER.clear(JPAMailboxFixture.MAILBOX_TABLE_NAMES);
     }
+
+    @Test
+    void invalidUidValidityShouldBeSanitized() throws Exception {
+        EntityManager entityManager = JPA_TEST_CLUSTER.getEntityManagerFactory().createEntityManager();
+
+        entityManager.getTransaction().begin();
+        JPAMailbox jpaMailbox = new JPAMailbox(benwaInboxPath, -1L);// set an invalid uid validity
+        jpaMailbox.setUidValidity(-1L);
+        entityManager.persist(jpaMailbox);
+        entityManager.getTransaction().commit();
+
+        Mailbox readMailbox = mailboxMapper.findMailboxByPath(benwaInboxPath);
+
+        assertThat(readMailbox.getUidValidity().isValid()).isTrue();
+    }
+
+    @Test
+    void uidValiditySanitizingShouldPersistTheSanitizedUidValidity() throws Exception {
+        EntityManager entityManager = JPA_TEST_CLUSTER.getEntityManagerFactory().createEntityManager();
+
+        entityManager.getTransaction().begin();
+        JPAMailbox jpaMailbox = new JPAMailbox(benwaInboxPath, -1L);// set an invalid uid validity
+        jpaMailbox.setUidValidity(-1L);
+        entityManager.persist(jpaMailbox);
+        entityManager.getTransaction().commit();
+
+        Mailbox readMailbox1 = mailboxMapper.findMailboxByPath(benwaInboxPath);
+        Mailbox readMailbox2 = mailboxMapper.findMailboxByPath(benwaInboxPath);
+
+        assertThat(readMailbox1.getUidValidity()).isEqualTo(readMailbox2.getUidValidity());
+    }
 }
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
index 7731554..a25c765 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
@@ -51,7 +51,7 @@ public abstract class MailboxMapperTest {
     private static final char DELIMITER = '.';
     private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
     private static final Username BENWA = Username.of("benwa");
-    private static final MailboxPath benwaInboxPath = MailboxPath.forUser(BENWA, "INBOX");
+    protected static final MailboxPath benwaInboxPath = MailboxPath.forUser(BENWA, "INBOX");
     private static final MailboxPath benwaWorkPath = MailboxPath.forUser(BENWA, "INBOX" + DELIMITER + "work");
     private static final MailboxPath benwaWorkTodoPath = MailboxPath.forUser(BENWA, "INBOX" + DELIMITER + "work" + DELIMITER + "todo");
     private static final MailboxPath benwaPersoPath = MailboxPath.forUser(BENWA, "INBOX" + DELIMITER + "perso");
@@ -69,7 +69,7 @@ public abstract class MailboxMapperTest {
     private Mailbox bobInboxMailbox;
     private Mailbox bobDifferentNamespaceMailbox;
 
-    private MailboxMapper mailboxMapper;
+    protected MailboxMapper mailboxMapper;
 
     protected abstract MailboxMapper createMailboxMapper();
 


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


[james-project] 10/17: PROTOCOLS-121 Use enabled cipher suites in createPipelineFactory

Posted by bt...@apache.org.
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

commit a5e9e7230a68abedd2545dfcf4a7eae8516e55ed
Author: janbusch <61...@users.noreply.github.com>
AuthorDate: Wed Mar 4 17:00:19 2020 +0100

    PROTOCOLS-121 Use enabled cipher suites in createPipelineFactory
    
    For the created ChannelPipelineFactory to actually take into account the enabled cipher suites that have been set while constructing the NettyServer instance, they have to be given to the AbstractSSLAwareChannelPipelineFactory constructor.
---
 .../src/main/java/org/apache/james/protocols/netty/NettyServer.java      | 1 +
 1 file changed, 1 insertion(+)

diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
index 2912faf..a523660 100644
--- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
+++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/NettyServer.java
@@ -121,6 +121,7 @@ public class NettyServer extends AbstractAsyncServer {
             maxCurConnections,
             maxCurConnectionsPerIP,
             group,
+            secure != null ? secure.getEnabledCipherSuites() : null,
             eHandler,
             getFrameHandlerFactory(),
             hashedWheelTimer) {


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


[james-project] 01/17: JAMES-3105 Add a MailboxCounters::toString method to ease debugging

Posted by bt...@apache.org.
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

commit 803dcce47bf25abea2f98f0dfcff12cd0aa36532
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 7 10:12:41 2020 +0700

    JAMES-3105 Add a MailboxCounters::toString method to ease debugging
---
 .../java/org/apache/james/mailbox/model/MailboxCounters.java   | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxCounters.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxCounters.java
index 270eb4e..6b5f0fe 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxCounters.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxCounters.java
@@ -21,6 +21,7 @@ package org.apache.james.mailbox.model;
 
 import java.util.Optional;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 
@@ -96,4 +97,13 @@ public class MailboxCounters {
     public final int hashCode() {
         return Objects.hashCode(count, unseen, mailboxId);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+            .add("mailboxId", mailboxId)
+            .add("count", count)
+            .add("unseen", unseen)
+            .toString();
+    }
 }


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


[james-project] 15/17: JAMES-3074 Enforce UidValidity to be always valid

Posted by bt...@apache.org.
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

commit b077642e4e25fb001cb3f1ee902ab01a9934b752
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Feb 26 15:00:54 2020 +0700

    JAMES-3074 Enforce UidValidity to be always valid
    
    We handle invalid values before creating the UidValidity POJO hence
    there are no longer reasons not to do a strong validation here
---
 .../org/apache/james/mailbox/model/UidValidity.java |  6 +-----
 .../mailbox/model/MailboxAssertingToolTest.java     |  4 ++--
 .../apache/james/mailbox/model/UidValidityTest.java | 21 ++++++++++-----------
 .../james/mailbox/backup/MailboxMessageFixture.java |  8 ++++----
 .../mailbox/cassandra/mail/CassandraMailboxDAO.java |  2 +-
 .../mail/CassandraIndexTableHandlerTest.java        |  2 +-
 .../mail/CassandraMailboxCounterDAOTest.java        |  2 +-
 .../cassandra/mail/CassandraMailboxDAOTest.java     |  4 ++--
 .../mail/CassandraMailboxMapperConcurrencyTest.java |  2 +-
 .../cassandra/mail/CassandraMailboxMapperTest.java  |  2 +-
 .../cassandra/mail/CassandraModSeqProviderTest.java |  2 +-
 .../cassandra/mail/CassandraUidProviderTest.java    |  2 +-
 .../mail/migration/MailboxPathV2MigrationTest.java  |  2 +-
 .../task/RecomputeMailboxCountersServiceTest.java   |  2 +-
 .../james/mailbox/jpa/mail/model/JPAMailbox.java    |  2 +-
 .../search/LuceneMailboxMessageSearchIndexTest.java |  6 +++---
 .../apache/james/mailbox/maildir/MaildirFolder.java |  2 +-
 .../spamassassin/SpamAssassinListenerTest.java      |  2 +-
 .../james/mailbox/store/StoreRightManagerTest.java  |  2 +-
 .../store/mail/model/ListMailboxAssertTest.java     |  2 +-
 .../store/mail/model/ListMessageAssertTest.java     |  2 +-
 .../store/mail/model/MailboxMapperACLTest.java      |  2 +-
 .../mailbox/store/mail/model/MailboxMapperTest.java |  2 +-
 .../store/mail/model/MessageIdMapperTest.java       |  2 +-
 .../mailbox/store/mail/model/MessageMapperTest.java |  2 +-
 .../mailbox/store/mail/model/MessageMoveTest.java   |  2 +-
 .../mail/model/MessageWithAttachmentMapperTest.java |  2 +-
 .../quota/DefaultUserQuotaRootResolverTest.java     |  4 ++--
 .../james/mailbox/store/quota/QuotaCheckerTest.java |  2 +-
 .../parser/AbstractSelectionCommandParser.java      |  2 +-
 .../encode/MailboxStatusResponseEncoderTest.java    |  2 +-
 .../james/imap/processor/CopyProcessorTest.java     |  2 +-
 .../james/imap/processor/MoveProcessorTest.java     |  2 +-
 .../processor/base/MailboxEventAnalyserTest.java    |  2 +-
 .../adapter/mailbox/MailboxManagementTest.java      |  2 +-
 35 files changed, 52 insertions(+), 57 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
index 05d65a9..1c1401c 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UidValidity.java
@@ -46,7 +46,7 @@ public class UidValidity {
     static UidValidity fromSupplier(Supplier<Long> longSupplier) {
         long randomValue = Math.abs(longSupplier.get());
         long sanitizedRandomValue = 1 + (randomValue % (UPPER_EXCLUSIVE_BOUND - 1));
-        return ofValid(sanitizedRandomValue);
+        return of(sanitizedRandomValue);
     }
 
     /**
@@ -56,10 +56,6 @@ public class UidValidity {
      * Strongly favor uses of  {@link #ofValid(long)}
      */
     public static UidValidity of(long uidValidity) {
-        return new UidValidity(uidValidity);
-    }
-
-    public static UidValidity ofValid(long uidValidity) {
         Preconditions.checkArgument(isValid(uidValidity), "uidValidity needs to be a non-zero unsigned 32-bit integer");
         return new UidValidity(uidValidity);
     }
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAssertingToolTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAssertingToolTest.java
index a88498e..aa7c7ff 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAssertingToolTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAssertingToolTest.java
@@ -31,8 +31,8 @@ class MailboxAssertingToolTest {
     private static final Username USER = Username.of("user");
     private static final Username USER1 = Username.of("user1");
 
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
-    private static final UidValidity UID_VALIDITY_2 = UidValidity.ofValid(43);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
+    private static final UidValidity UID_VALIDITY_2 = UidValidity.of(43);
     private static final TestId MAILBOX_ID = TestId.of(24);
 
     @Nested
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/UidValidityTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/UidValidityTest.java
index 89e25a4..34fc0e5 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/UidValidityTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/UidValidityTest.java
@@ -21,6 +21,7 @@ package org.apache.james.mailbox.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.util.stream.IntStream;
 
@@ -45,23 +46,21 @@ class UidValidityTest {
     }
 
     @Test
-    void zeroUidValidityShouldBeInvalid() {
-        assertThat(UidValidity.of(0).isValid()).isFalse();
+    void ofShouldThrowWhenZero() {
+        assertThatThrownBy(() -> UidValidity.of(0))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
-    void negativeUidValidityShouldBeInvalid() {
-        assertThat(UidValidity.of(-1).isValid()).isFalse();
+    void ofShouldThrowWhenNegative() {
+        assertThatThrownBy(() -> UidValidity.of(-1))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
-    void tooBigUidValidityShouldBeInvalid() {
-        assertThat(UidValidity.of(4294967296L).isValid()).isFalse();
-    }
-
-    @Test
-    void idValidityShouldBeValid() {
-        assertThat(UidValidity.of(42).isValid()).isTrue();
+    void ofShouldThrowWhenTooBig() {
+        assertThatThrownBy(() -> UidValidity.of(4294967296L))
+            .isInstanceOf(IllegalArgumentException.class);
     }
 
     @Test
diff --git a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java
index f833839..50f8a76 100644
--- a/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java
+++ b/mailbox/backup/src/test/java/org/apache/james/mailbox/backup/MailboxMessageFixture.java
@@ -101,10 +101,10 @@ public interface MailboxMessageFixture {
     MailboxPath MAILBOX_PATH_USER1_MAILBOX2 = MailboxPath.forUser(USER, MAILBOX_2_NAME);
     MailboxPath MAILBOX_PATH_OTHER_USER_MAILBOX1 = MailboxPath.forUser(OTHER_USER, MAILBOX_OTHER_USER_NAME);
 
-    Mailbox MAILBOX_1 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX1, UidValidity.ofValid(42), MAILBOX_ID_1);
-    Mailbox MAILBOX_1_OTHER_USER = new Mailbox(MAILBOX_PATH_OTHER_USER_MAILBOX1, UidValidity.ofValid(42), MAILBOX_ID_11);
-    Mailbox MAILBOX_1_SUB_1 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX1_SUB_1, UidValidity.ofValid(420), TestId.of(11L));
-    Mailbox MAILBOX_2 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX2, UidValidity.ofValid(43), MAILBOX_ID_2);
+    Mailbox MAILBOX_1 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX1, UidValidity.of(42), MAILBOX_ID_1);
+    Mailbox MAILBOX_1_OTHER_USER = new Mailbox(MAILBOX_PATH_OTHER_USER_MAILBOX1, UidValidity.of(42), MAILBOX_ID_11);
+    Mailbox MAILBOX_1_SUB_1 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX1_SUB_1, UidValidity.of(420), TestId.of(11L));
+    Mailbox MAILBOX_2 = new Mailbox(MAILBOX_PATH_USER1_MAILBOX2, UidValidity.of(43), MAILBOX_ID_2);
 
     List<MailboxAnnotation> NO_ANNOTATION = ImmutableList.of();
 
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
index 87c9505..6daf88e 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAO.java
@@ -166,7 +166,7 @@ public class CassandraMailboxDAO {
             return updateUidValidity(cassandraId, newUidValidity)
                 .then(Mono.just(newUidValidity));
         }
-        return Mono.just(UidValidity.ofValid(uidValidityAsLong));
+        return Mono.just(UidValidity.of(uidValidityAsLong));
     }
 
     /**
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
index 4ba6fbb..ae090d1 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
@@ -61,7 +61,7 @@ class CassandraIndexTableHandlerTest {
     private static final CassandraId MAILBOX_ID = CassandraId.timeBased();
     private static final MessageUid MESSAGE_UID = MessageUid.of(18L);
     private static final CassandraMessageId CASSANDRA_MESSAGE_ID = new CassandraMessageId.Factory().generate();
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(15);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(15);
     private static final ModSeq MODSEQ = ModSeq.of(17);
 
     @RegisterExtension
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
index 62aeb01..46451c0 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
@@ -35,7 +35,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class CassandraMailboxCounterDAOTest {
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(15);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(15);
     private static final CassandraId MAILBOX_ID = CassandraId.timeBased();
 
     @RegisterExtension
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
index 54d0134..3fc20cf 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxDAOTest.java
@@ -51,8 +51,8 @@ import reactor.core.publisher.Mono;
 import reactor.core.scheduler.Schedulers;
 
 class CassandraMailboxDAOTest {
-    private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(145);
-    private static final UidValidity UID_VALIDITY_2 = UidValidity.ofValid(147);
+    private static final UidValidity UID_VALIDITY_1 = UidValidity.of(145);
+    private static final UidValidity UID_VALIDITY_2 = UidValidity.of(147);
     private static final Username USER = Username.of("user");
     private static final MailboxPath NEW_MAILBOX_PATH = MailboxPath.forUser(USER, "xyz");
     private static CassandraId CASSANDRA_ID_1 = CassandraId.timeBased();
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 c239e4c..54e7290 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
@@ -42,7 +42,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 
 class CassandraMailboxMapperConcurrencyTest {
 
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(52);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(52);
     private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(Username.of("user"), "name");
     private static final int THREAD_COUNT = 10;
     private static final int OPERATION_COUNT = 10;
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 2a044ec..d16a079 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
@@ -62,7 +62,7 @@ import com.github.fge.lambdas.runnable.ThrowingRunnable;
 import reactor.core.publisher.Mono;
 
 class CassandraMailboxMapperTest {
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(52);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(52);
     private static final Username USER = Username.of("user");
     private static final CassandraId MAILBOX_ID = CassandraId.timeBased();
     private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER, "name");
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProviderTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProviderTest.java
index 9d25206..6c328a4 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProviderTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProviderTest.java
@@ -56,7 +56,7 @@ class CassandraModSeqProviderTest {
     void setUp(CassandraCluster cassandra) {
         modSeqProvider = new CassandraModSeqProvider(cassandra.getConf(), CassandraConfiguration.DEFAULT_CONFIGURATION);
         MailboxPath path = new MailboxPath("gsoc", Username.of("ieugen"), "Trash");
-        mailbox = new Mailbox(path, UidValidity.ofValid(1234), CASSANDRA_ID);
+        mailbox = new Mailbox(path, UidValidity.of(1234), CASSANDRA_ID);
     }
 
     @Test
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProviderTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProviderTest.java
index 53bcdc1..80c43b0 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProviderTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProviderTest.java
@@ -56,7 +56,7 @@ class CassandraUidProviderTest {
     void setUp(CassandraCluster cassandra) {
         uidProvider = new CassandraUidProvider(cassandra.getConf(), CassandraConfiguration.DEFAULT_CONFIGURATION);
         MailboxPath path = new MailboxPath("gsoc", Username.of("ieugen"), "Trash");
-        mailbox = new Mailbox(path, UidValidity.ofValid(1234), CASSANDRA_ID);
+        mailbox = new Mailbox(path, UidValidity.of(1234), CASSANDRA_ID);
     }
 
     @Test
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java
index 7412e05..82ee289 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java
@@ -50,7 +50,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
 class MailboxPathV2MigrationTest {
 
     private static final MailboxPath MAILBOX_PATH_1 = MailboxPath.forUser(Username.of("bob"), "Important");
-    private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(452);
+    private static final UidValidity UID_VALIDITY_1 = UidValidity.of(452);
     private static final CassandraId MAILBOX_ID_1 = CassandraId.timeBased();
 
     public static final CassandraModule MODULES = CassandraModule.aggregateModules(
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
index 334f7b4..79b9c94 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
@@ -55,7 +55,7 @@ import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class RecomputeMailboxCountersServiceTest {
-    private static final UidValidity UID_VALIDITY_1 = UidValidity.ofValid(145);
+    private static final UidValidity UID_VALIDITY_1 = UidValidity.of(145);
     private static final Username USER = Username.of("user");
     private static final MailboxPath MAILBOX_PATH = MailboxPath.forUser(USER, "abc");
     private static final CassandraMessageId.Factory MESSAGE_ID_FACTORY = new CassandraMessageId.Factory();
diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
index d775974..2bedbe5 100644
--- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
+++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/JPAMailbox.java
@@ -143,7 +143,7 @@ public class JPAMailbox {
 
     private UidValidity sanitizeUidValidity() {
         if (UidValidity.isValid(uidValidity)) {
-            return UidValidity.ofValid(uidValidity);
+            return UidValidity.of(uidValidity);
         }
         UidValidity sanitizedUidValidity = UidValidity.generate();
         // Update storage layer thanks to JPA magics!
diff --git a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
index 1b45f08..6ff52fb 100644
--- a/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
+++ b/mailbox/lucene/src/test/java/org/apache/james/mailbox/lucene/search/LuceneMailboxMessageSearchIndexTest.java
@@ -62,9 +62,9 @@ class LuceneMailboxMessageSearchIndexTest {
     static final TestId TEST_ID_3 = TestId.of(2);
 
     static final Username BOB = Username.of("bob");
-    Mailbox mailbox = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.ofValid(18), TEST_ID_1);
-    Mailbox mailbox2 = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.ofValid(19), TEST_ID_2);
-    Mailbox mailbox3 = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.ofValid(12), TEST_ID_3);
+    Mailbox mailbox = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.of(18), TEST_ID_1);
+    Mailbox mailbox2 = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.of(19), TEST_ID_2);
+    Mailbox mailbox3 = new Mailbox(MailboxPath.forUser(BOB, "box"), UidValidity.of(12), TEST_ID_3);
     LuceneMessageSearchIndex index;
     MailboxSession session;
 
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
index 88148ff..6475c3e 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirFolder.java
@@ -290,7 +290,7 @@ public class MaildirFolder {
         if (!UidValidity.isValid(uidValidityValue)) {
             return resetUidValidity();
         }
-        return UidValidity.ofValid(uidValidityValue);
+        return UidValidity.of(uidValidityValue);
     }
 
     /**
diff --git a/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java b/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java
index f4bdc90..4227d3b 100644
--- a/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java
+++ b/mailbox/plugin/spamassassin/src/test/java/org/apache/james/mailbox/spamassassin/SpamAssassinListenerTest.java
@@ -61,7 +61,7 @@ import org.junit.jupiter.api.Test;
 class SpamAssassinListenerTest {
     static final Username USER = Username.of("user");
     static final MailboxSession MAILBOX_SESSION = MailboxSessionUtil.create(USER);
-    static final UidValidity UID_VALIDITY = UidValidity.ofValid(43);
+    static final UidValidity UID_VALIDITY = UidValidity.of(43);
     static final TestMessageId MESSAGE_ID = TestMessageId.of(45);
 
     SpamAssassin spamAssassin;
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
index 008c170..7aa4a76 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreRightManagerTest.java
@@ -59,7 +59,7 @@ import org.junit.jupiter.api.Test;
 class StoreRightManagerTest {
 
     static final MailboxId MAILBOX_ID = TestId.of(42);
-    static final UidValidity UID_VALIDITY = UidValidity.ofValid(3421L);
+    static final UidValidity UID_VALIDITY = UidValidity.of(3421L);
 
     StoreRightManager storeRightManager;
     MailboxSession aliceSession;
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMailboxAssertTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMailboxAssertTest.java
index 97963f6..0d158a6 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMailboxAssertTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMailboxAssertTest.java
@@ -40,7 +40,7 @@ class ListMailboxAssertTest {
     static final String NAME = "name";
     static final Username USER = Username.of("user");
     static final String NAMESPACE = "namespace";
-    static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    static final UidValidity UID_VALIDITY = UidValidity.of(42);
     static final MailboxId MAILBOX_ID_1 = TestId.of(1);
     static final MailboxId MAILBOX_ID_2 = TestId.of(2);
     static final Mailbox MAILBOX_1 = new Mailbox(new MailboxPath(NAMESPACE, USER, NAME), UID_VALIDITY, MAILBOX_ID_1);
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessageAssertTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessageAssertTest.java
index 63b2cbd..99318de 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessageAssertTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/ListMessageAssertTest.java
@@ -50,7 +50,7 @@ class ListMessageAssertTest {
     static final String BODY_CONTENT2 = "Subject: Test2 \n\nBody2\n.\n";
     static final String BODY_CONTENT1 = "Subject: Test1 \n\nBody1\n.\n";
     static final int BODY_START = 16;
-    static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    static final UidValidity UID_VALIDITY = UidValidity.of(42);
     static final MailboxId MAILBOX_ID = TestId.of(1);
     static final MessageUid MESSAGE_UID = MessageUid.of(2);
     static final MessageId MESSAGE_ID = new DefaultMessageId();
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
index 7776c6a..744892f 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
@@ -38,7 +38,7 @@ import org.junit.jupiter.api.Test;
 import com.google.common.collect.ImmutableMap;
 
 public abstract class MailboxMapperACLTest {
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
     private static final boolean POSITIVE = true;
     private static final boolean NEGATIVE = !POSITIVE;
     private static final Username USER = Username.of("user");
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
index a25c765..98bf41a 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperTest.java
@@ -49,7 +49,7 @@ import org.junit.jupiter.api.Test;
 public abstract class MailboxMapperTest {
 
     private static final char DELIMITER = '.';
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
     private static final Username BENWA = Username.of("benwa");
     protected static final MailboxPath benwaInboxPath = MailboxPath.forUser(BENWA, "INBOX");
     private static final MailboxPath benwaWorkPath = MailboxPath.forUser(BENWA, "INBOX" + DELIMITER + "work");
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java
index a14b98d..731f35a 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java
@@ -65,7 +65,7 @@ public abstract class MessageIdMapperTest {
 
     private static final char DELIMITER = '.';
     private static final int BODY_START = 16;
-    protected static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    protected static final UidValidity UID_VALIDITY = UidValidity.of(42);
 
     private MessageMapper messageMapper;
     private MailboxMapper mailboxMapper;
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
index 14341ee..44b45b8 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
@@ -70,7 +70,7 @@ public abstract class MessageMapperTest {
     private static final char DELIMITER = '.';
     private static final int LIMIT = 10;
     private static final int BODY_START = 16;
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
     private static final String USER_FLAG = "userFlag";
 
     private static final String CUSTOMS_USER_FLAGS_VALUE = "CustomsFlags";
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMoveTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMoveTest.java
index 7ef5380..4542f2f 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMoveTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMoveTest.java
@@ -48,7 +48,7 @@ public abstract class MessageMoveTest {
     private static final char DELIMITER = '.';
     private static final int LIMIT = 10;
     private static final int BODY_START = 16;
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
 
     private MapperProvider mapperProvider;
     private MessageMapper messageMapper;
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageWithAttachmentMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageWithAttachmentMapperTest.java
index b096fd1..f10a0b6 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageWithAttachmentMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageWithAttachmentMapperTest.java
@@ -55,7 +55,7 @@ public abstract class MessageWithAttachmentMapperTest {
 
     private static final int LIMIT = 10;
     private static final int BODY_START = 16;
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(42);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
 
     private MapperProvider mapperProvider;
     private MessageMapper messageMapper;
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/DefaultUserQuotaRootResolverTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/DefaultUserQuotaRootResolverTest.java
index 82cf761..d744e15 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/DefaultUserQuotaRootResolverTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/DefaultUserQuotaRootResolverTest.java
@@ -49,9 +49,9 @@ class DefaultUserQuotaRootResolverTest {
     static final Username BENWA = Username.of("benwa");
     static final MailboxId MAILBOX_ID = TestId.of(42);
     static final MailboxPath MAILBOX_PATH = MailboxPath.inbox(BENWA);
-    static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UidValidity.ofValid(10), MAILBOX_ID);
+    static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UidValidity.of(10), MAILBOX_ID);
     static final MailboxPath MAILBOX_PATH_2 = MailboxPath.forUser(BENWA, "test");
-    static final Mailbox MAILBOX_2 = new Mailbox(MAILBOX_PATH_2, UidValidity.ofValid(10), MAILBOX_ID);
+    static final Mailbox MAILBOX_2 = new Mailbox(MAILBOX_PATH_2, UidValidity.of(10), MAILBOX_ID);
     static final QuotaRoot QUOTA_ROOT = QuotaRoot.quotaRoot("#private&benwa", Optional.empty());
     static final MailboxSession MAILBOX_SESSION = null;
 
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/QuotaCheckerTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/QuotaCheckerTest.java
index 35a764d..9c09ab2 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/QuotaCheckerTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/quota/QuotaCheckerTest.java
@@ -49,7 +49,7 @@ class QuotaCheckerTest {
     static final QuotaRoot QUOTA_ROOT = QuotaRoot.quotaRoot("benwa", Optional.empty());
     static final MailboxId MAILBOX_ID = TestId.of(42);
     static final MailboxPath MAILBOX_PATH = MailboxPath.inbox(Username.of("benwa"));
-    static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UidValidity.ofValid(10), MAILBOX_ID);
+    static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UidValidity.of(10), MAILBOX_ID);
 
     QuotaRootResolver mockedQuotaRootResolver;
     QuotaManager mockedQuotaManager;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
index 1bd3841..d183a29 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
@@ -133,7 +133,7 @@ public abstract class AbstractSelectionCommandParser extends AbstractImapCommand
 
     private UidValidity sanitizeUidValidity(long uidValidityAsNumber) {
         if (UidValidity.isValid(uidValidityAsNumber)) {
-            return UidValidity.ofValid(uidValidityAsNumber);
+            return UidValidity.of(uidValidityAsNumber);
         } else {
             // The UidValidity cached by the client is invalid
             // We know that the backend will regenerate it
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
index 5ad5417..110feee 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/MailboxStatusResponseEncoderTest.java
@@ -51,7 +51,7 @@ public class MailboxStatusResponseEncoderTest  {
         final Long messages = 2L;
         final Long recent = 3L;
         final MessageUid uidNext = MessageUid.of(5);
-        final UidValidity uidValidity = UidValidity.ofValid(7L);
+        final UidValidity uidValidity = UidValidity.of(7L);
         final Long unseen = 11L;
         final String mailbox = "A mailbox named desire";
 
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java
index 11876d4..2b4bb63 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java
@@ -60,7 +60,7 @@ import com.google.common.collect.Lists;
 public class CopyProcessorTest {
     private static final Username USERNAME = Username.of("username");
     private static final MailboxPath INBOX = MailboxPath.inbox(USERNAME);
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(58L);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(58L);
 
     private CopyProcessor testee;
     private ImapProcessor mockNextProcessor;
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java
index 3c91299..7837f48 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java
@@ -63,7 +63,7 @@ import com.google.common.collect.Lists;
 public class MoveProcessorTest {
     private static final Username USERNAME = Username.of("username");
     private static final MailboxPath INBOX = MailboxPath.inbox(USERNAME);
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(58L);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(58L);
 
     private MoveProcessor testee;
     private ImapProcessor mockNextProcessor;
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
index 0fca17c..736a0f3 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
@@ -117,7 +117,7 @@ public class MailboxEventAnalyserTest {
     private static final char PATH_DELIMITER = '.';
     private static final MailboxPath MAILBOX_PATH = new MailboxPath("namespace", USER, "name");
     private static final TestId MAILBOX_ID = TestId.of(36);
-    private static final UidValidity UID_VALIDITY = UidValidity.ofValid(1024);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(1024);
     private static final Mailbox DEFAULT_MAILBOX = new Mailbox(MAILBOX_PATH, UID_VALIDITY, MAILBOX_ID);
     private static final MailboxListener.Added ADDED = EventFactory.added()
         .randomEventId()
diff --git a/server/container/mailbox-jmx/src/test/java/org/apache/james/adapter/mailbox/MailboxManagementTest.java b/server/container/mailbox-jmx/src/test/java/org/apache/james/adapter/mailbox/MailboxManagementTest.java
index 945e33e..21c33c7 100644
--- a/server/container/mailbox-jmx/src/test/java/org/apache/james/adapter/mailbox/MailboxManagementTest.java
+++ b/server/container/mailbox-jmx/src/test/java/org/apache/james/adapter/mailbox/MailboxManagementTest.java
@@ -48,7 +48,7 @@ import org.junit.jupiter.api.Test;
 public class MailboxManagementTest {
 
     public static final Username USER = Username.of("user");
-    public static final UidValidity UID_VALIDITY = UidValidity.ofValid(10);
+    public static final UidValidity UID_VALIDITY = UidValidity.of(10);
     public static final int LIMIT = 1;
 
     private MailboxManagerManagement mailboxManagerManagement;


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


[james-project] 08/17: JAMES-3105 Changelog entry

Posted by bt...@apache.org.
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

commit abbd07a38faaec6c2905db379f56ce76f5a67a67
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sat Mar 7 14:06:17 2020 +0700

    JAMES-3105 Changelog entry
---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 098cf2b..03c3253 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ of tasks being currently executed.
 - JAMES-3066 Add "allowed From headers" webadmin endpoint
 - JAMES-3062 EventDeadLettersHealthCheck
 - JAMES-3058 WebAdmin offline task to correct mailbox inconsistencies on top of Cassandra products
+- JAMES-3105 WebAdmin offline task to recompute mailbox counters on top of Cassandra products
 
 ### Changed
 - Multiple changes have been made to enhance ElasticSearch performance:


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