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 ad...@apache.org on 2017/02/07 08:06:50 UTC

[01/10] james-project git commit: JAMES-1874 Add more tests on MessageMapper RECENT handling

Repository: james-project
Updated Branches:
  refs/heads/master 3bdf9182f -> 41c860c17


JAMES-1874 Add more tests on MessageMapper RECENT handling


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

Branch: refs/heads/master
Commit: 9c28aad8f0a8774d0411a3b6a4a04b1047c5219a
Parents: 4297b45
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 11:57:52 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:45 2017 +0700

----------------------------------------------------------------------
 .../store/mail/model/MessageMapperTest.java     | 41 ++++++++++++++++++++
 1 file changed, 41 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/9c28aad8/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
----------------------------------------------------------------------
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 6fdb35a..156744f 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
@@ -304,6 +304,47 @@ public class MessageMapperTest<T extends MapperProvider> {
     }
 
     @ContractTest
+    public void deleteShouldUpdateRecentWhenNeeded() throws MailboxException {
+        saveMessages();
+        messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message2.getUid()));
+        messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message4.getUid()));
+        messageMapper.updateFlags(benwaWorkMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message6.getUid()));
+
+        messageMapper.delete(benwaInboxMailbox, message2);
+
+        assertThat(messageMapper.findRecentMessageUidsInMailbox(benwaInboxMailbox)).containsOnly(message4.getUid());
+    }
+
+    @ContractTest
+    public void deleteShouldNotUpdateRecentWhenNotNeeded() throws MailboxException {
+        saveMessages();
+        messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message2.getUid()));
+        messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message4.getUid()));
+        messageMapper.updateFlags(benwaWorkMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.RECENT), FlagsUpdateMode.REPLACE), MessageRange.one(message6.getUid()));
+
+        messageMapper.delete(benwaInboxMailbox, message1);
+
+        assertThat(messageMapper.findRecentMessageUidsInMailbox(benwaInboxMailbox)).containsOnly(message2.getUid(), message4.getUid());
+    }
+
+    @ContractTest
+    public void addShouldUpdateRecentWhenNeeded() throws MailboxException {
+        message1.setFlags(new Flags(Flags.Flag.RECENT));
+        messageMapper.add(benwaInboxMailbox, message1);
+        message1.setModSeq(messageMapper.getHighestModSeq(benwaInboxMailbox));
+
+        assertThat(messageMapper.findRecentMessageUidsInMailbox(benwaInboxMailbox)).containsOnly(message1.getUid());
+    }
+
+    @ContractTest
+    public void addShouldNotUpdateRecentWhenNotNeeded() throws MailboxException {
+        messageMapper.add(benwaInboxMailbox, message1);
+        message1.setModSeq(messageMapper.getHighestModSeq(benwaInboxMailbox));
+
+        assertThat(messageMapper.findRecentMessageUidsInMailbox(benwaInboxMailbox)).isEmpty();
+    }
+
+    @ContractTest
     public void findFirstUnseenMessageUidShouldReturnNullWhenNoUnseenMessagesCanBeFound() throws MailboxException {
         assertThat(messageMapper.findFirstUnseenMessageUid(benwaInboxMailbox)).isNull();
     }


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


[10/10] james-project git commit: JAMES-1874 Reorder imports in some class of mailbox-cassandra

Posted by ad...@apache.org.
JAMES-1874 Reorder imports in some class of mailbox-cassandra


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

Branch: refs/heads/master
Commit: a9ff7fa8f67e13eec8457c14c816abe558e35d7d
Parents: c8c1282
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 19:41:39 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../modules/CassandraMailboxCounterModule.java         | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/a9ff7fa8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxCounterModule.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxCounterModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxCounterModule.java
index 03ca31a..642ac02 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxCounterModule.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxCounterModule.java
@@ -19,18 +19,19 @@
 
 package org.apache.james.mailbox.cassandra.modules;
 
-import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+import static com.datastax.driver.core.DataType.counter;
+import static com.datastax.driver.core.DataType.timeuuid;
+
+import java.util.Collections;
+import java.util.List;
+
 import org.apache.james.backends.cassandra.components.CassandraIndex;
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.components.CassandraTable;
 import org.apache.james.backends.cassandra.components.CassandraType;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
 
-import java.util.Collections;
-import java.util.List;
-
-import static com.datastax.driver.core.DataType.counter;
-import static com.datastax.driver.core.DataType.timeuuid;
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
 
 public class CassandraMailboxCounterModule implements CassandraModule {
 


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


[06/10] james-project git commit: JAMES-1874 Make constructor of UpdatedFlags private

Posted by ad...@apache.org.
JAMES-1874 Make constructor of UpdatedFlags private


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

Branch: refs/heads/master
Commit: 41c860c171f446ae01e9a54235003b67fafdcff3
Parents: 948b58f
Author: Benoit Tellier <bt...@linagora.com>
Authored: Mon Feb 6 09:55:55 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../james/mailbox/model/UpdatedFlags.java       |   2 +-
 .../mail/CassandraMessageIdMapper.java          |  10 +-
 .../cassandra/mail/CassandraMessageMapper.java  |   7 +-
 ...hListeningMailboxMessageSearchIndexTest.java |  14 +-
 .../mailbox/hbase/mail/HBaseMessageMapper.java  |   8 +-
 .../maildir/mail/MaildirMessageMapper.java      |   7 +-
 .../dto/UpdatedFlagsDataTransferObject.java     |   7 +-
 .../store/mail/AbstractMessageMapper.java       |  10 +-
 .../james/mailbox/store/mail/MessageUtils.java  |  10 +-
 .../AbstractMessageIdManagerSideEffectTest.java |   7 +-
 .../store/MailboxEventDispatcherTest.java       | 360 +++++++++++--------
 .../store/TestMailboxSessionMapperFactory.java  |   7 +-
 .../mailbox/store/json/EventSerializerTest.java |   8 +-
 .../store/mail/model/MessageIdMapperTest.java   |  42 ++-
 .../store/mail/model/MessageMapperTest.java     |  30 +-
 .../registrations/MailboxRegistrationTest.java  |   7 +-
 .../base/MailboxEventAnalyserTest.java          | 139 ++++---
 17 files changed, 422 insertions(+), 253 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
index 989729b..9b7a7ef 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
@@ -86,7 +86,7 @@ public class UpdatedFlags {
     private final Flags modifiedFlags;
     private final long modSeq;
 
-    public UpdatedFlags(MessageUid uid, long modSeq, Flags oldFlags, Flags newFlags) {
+    private UpdatedFlags(MessageUid uid, long modSeq, Flags oldFlags, Flags newFlags) {
        this.uid = uid;
        this.modSeq = modSeq;
        this.oldFlags = oldFlags;

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
index e403268..61045d1 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
@@ -210,10 +210,12 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
             ComposedMessageIdWithMetaData composedMessageIdWithMetaData = pair.getRight();
             Flags oldFlags = pair.getLeft();
             return Stream.of(Pair.of(composedMessageIdWithMetaData.getComposedMessageId().getMailboxId(),
-                    new UpdatedFlags(composedMessageIdWithMetaData.getComposedMessageId().getUid(),
-                        composedMessageIdWithMetaData.getModSeq(),
-                        oldFlags,
-                        composedMessageIdWithMetaData.getFlags())));
+                    UpdatedFlags.builder()
+                        .uid(composedMessageIdWithMetaData.getComposedMessageId().getUid())
+                        .modSeq(composedMessageIdWithMetaData.getModSeq())
+                        .oldFlags(oldFlags)
+                        .newFlags(composedMessageIdWithMetaData.getFlags())
+                        .build()));
         } catch (LightweightTransactionException e) {
             throw Throwables.propagate(e);
         } catch (MailboxDeleteDuringUpdateException e) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
index 91d600e..ff0d084 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
@@ -280,7 +280,12 @@ public class CassandraMessageMapper implements MessageMapper {
             message.setFlags(newFlags);
             message.setModSeq(modSeqProvider.nextModSeq(mailboxSession, mailbox));
             if (updateFlags(message, oldModSeq)) {
-                return Optional.of(new UpdatedFlags(message.getUid(), message.getModSeq(), oldFlags, newFlags));
+                return Optional.of(UpdatedFlags.builder()
+                    .uid(message.getUid())
+                    .modSeq(message.getModSeq())
+                    .oldFlags(oldFlags)
+                    .newFlags(newFlags)
+                    .build());
             } else {
                 return Optional.empty();
             }

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMailboxMessageSearchIndexTest.java
----------------------------------------------------------------------
diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMailboxMessageSearchIndexTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMailboxMessageSearchIndexTest.java
index a9ea500..3efe045 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMailboxMessageSearchIndexTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/events/ElasticSearchListeningMailboxMessageSearchIndexTest.java
@@ -195,7 +195,12 @@ public class ElasticSearchListeningMailboxMessageSearchIndexTest {
 
         Flags flags = new Flags();
         MessageUid messageUid = MessageUid.of(1);
-        UpdatedFlags updatedFlags = new UpdatedFlags(messageUid, MODSEQ, flags, flags);
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .uid(messageUid)
+            .modSeq(MODSEQ)
+            .oldFlags(flags)
+            .newFlags(flags)
+            .build();
         TestId mailboxId = TestId.of(12);
 
         expectLastCall();
@@ -218,7 +223,12 @@ public class ElasticSearchListeningMailboxMessageSearchIndexTest {
         Mailbox mailbox = control.createMock(Mailbox.class);
         Flags flags = new Flags();
         MessageUid messageUid = MessageUid.of(1);
-        UpdatedFlags updatedFlags = new UpdatedFlags(messageUid, MODSEQ, flags, flags);
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .uid(messageUid)
+            .modSeq(MODSEQ)
+            .oldFlags(flags)
+            .newFlags(flags)
+            .build();
         TestId mailboxId = TestId.of(12);
 
         expectLastCall();

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java
index dba79fb..411b15d 100644
--- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java
+++ b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/mail/HBaseMessageMapper.java
@@ -551,8 +551,12 @@ public class HBaseMessageMapper extends NonTransactionalMapper implements Messag
                     messages.flushCommits();
                 }
 
-                UpdatedFlags uFlags = new UpdatedFlags(member.getUid(), member.getModSeq(), originalFlags, newFlags);
-                updatedFlags.add(uFlags);
+                updatedFlags.add(UpdatedFlags.builder()
+                    .uid(member.getUid())
+                    .modSeq(member.getModSeq())
+                    .newFlags(newFlags)
+                    .oldFlags(originalFlags)
+                    .build());
             }
         } catch (IOException e) {
             throw new MailboxException("Error setting flags for messages in " + mailbox, e);

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
index d00220a..4867dde 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
@@ -192,7 +192,12 @@ public class MaildirMessageMapper extends AbstractMessageMapper {
                     }
                     member.setModSeq(modSeq);
 
-                    updatedFlags.add(new UpdatedFlags(member.getUid(), modSeq, originalFlags, newFlags));
+                    updatedFlags.add(UpdatedFlags.builder()
+                        .uid(member.getUid())
+                        .modSeq(member.getModSeq())
+                        .newFlags(newFlags)
+                        .oldFlags(originalFlags)
+                        .build());
 
                     MessageUid uid = member.getUid();
                     folder.update(mailboxSession, uid, newMessageName);

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/main/java/org/apache/james/mailbox/store/json/event/dto/UpdatedFlagsDataTransferObject.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/json/event/dto/UpdatedFlagsDataTransferObject.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/json/event/dto/UpdatedFlagsDataTransferObject.java
index ada06f6..b5cdda0 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/json/event/dto/UpdatedFlagsDataTransferObject.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/json/event/dto/UpdatedFlagsDataTransferObject.java
@@ -45,7 +45,12 @@ public class UpdatedFlagsDataTransferObject {
     }
 
     public UpdatedFlags retrieveUpdatedFlags() {
-        return new UpdatedFlags(MessageUid.of(uid), modseq, oldFlags.getFlags(), newFlags.getFlags());
+        return UpdatedFlags.builder()
+            .uid(MessageUid.of(uid))
+            .modSeq(modseq)
+            .oldFlags(oldFlags.getFlags())
+            .newFlags(newFlags.getFlags())
+            .build();
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java
index 006fee5..de82a8d 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AbstractMessageMapper.java
@@ -85,11 +85,13 @@ public abstract class AbstractMessageMapper extends TransactionalMapper implemen
                 member.setModSeq(modSeq);
                 save(mailbox, member);
             }
-
-            
-            UpdatedFlags uFlags = new UpdatedFlags(member.getUid(), member.getModSeq(), originalFlags, newFlags);
             
-            updatedFlags.add(uFlags);
+            updatedFlags.add(UpdatedFlags.builder()
+                .uid(member.getUid())
+                .modSeq(member.getModSeq())
+                .newFlags(newFlags)
+                .oldFlags(originalFlags)
+                .build());
             
         }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageUtils.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageUtils.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageUtils.java
index 7cdcab0..9e1d958 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageUtils.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageUtils.java
@@ -88,10 +88,12 @@ public class MessageUtils {
                 changedFlags.add(member);
             }
 
-            UpdatedFlags uFlags = new UpdatedFlags(member.getUid(), member.getModSeq(), originalFlags, newFlags);
-
-            updatedFlags.add(uFlags);
-
+            updatedFlags.add(UpdatedFlags.builder()
+                .uid(member.getUid())
+                .modSeq(member.getModSeq())
+                .newFlags(newFlags)
+                .oldFlags(originalFlags)
+                .build());
         }
 
         return new MessageChangedFlags(updatedFlags.build().iterator(), changedFlags.build());

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
index ccf3aae..ec2dac1 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/AbstractMessageIdManagerSideEffectTest.java
@@ -288,7 +288,12 @@ public abstract class AbstractMessageIdManagerSideEffectTest {
         MessageResult messageResult = messages.get(0);
         MessageUid messageUid = messageResult.getUid();
         long modSeq = messageResult.getModSeq();
-        UpdatedFlags updatedFlags = new UpdatedFlags(messageUid, modSeq, FLAGS, newFlags);
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .uid(messageUid)
+            .modSeq(modSeq)
+            .oldFlags(FLAGS)
+            .newFlags(newFlags)
+            .build();
 
         verify(dispatcher, times(1)).flagsUpdated(session, messageUid, mailbox2, updatedFlags);
         verifyNoMoreInteractions(dispatcher);

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
index 41140dd..552ac21 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/MailboxEventDispatcherTest.java
@@ -19,18 +19,13 @@
 
 package org.apache.james.mailbox.store;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.util.Arrays;
-import java.util.Iterator;
-
 import javax.mail.Flags;
 
+import org.apache.james.mailbox.FlagsBuilder;
 import org.apache.james.mailbox.MailboxListener;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageUid;
@@ -40,18 +35,25 @@ import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.event.MailboxEventDispatcher;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.util.EventCollector;
+import org.assertj.core.api.Condition;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.google.common.collect.ImmutableList;
+
 public class MailboxEventDispatcherTest {
     private static final int sessionId = 10;
+    private static final int MOD_SEQ = -1;
+    public static final Condition<MailboxListener.Event> INSTANCE_OF_EVENT_FLAGS_UPDATED = new Condition<MailboxListener.Event>() {
+        @Override
+        public boolean matches(MailboxListener.Event event) {
+            return event instanceof MailboxListener.FlagsUpdated;
+        }
+    };
 
     private MailboxEventDispatcher dispatcher;
-
     private EventCollector collector;
-
     private MessageResult result;
-
     private Mailbox mailbox;
 
     private MailboxSession session = new MockMailboxSession("test") {
@@ -78,220 +80,286 @@ public class MailboxEventDispatcherTest {
 
     @Test
     public void testShouldReturnNoChangesWhenSystemFlagsUnchanged() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1,  new Flags(
-                Flags.Flag.DELETED), new Flags(Flags.Flag.DELETED))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags(Flags.Flag.DELETED))
+                .oldFlags(new Flags(Flags.Flag.DELETED))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator()).isEmpty();
     }
 
     @Test
     public void testShouldShowAnsweredAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.ANSWERED))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(
+                UpdatedFlags.builder()
+                    .uid(result.getUid())
+                    .modSeq(MOD_SEQ)
+                    .newFlags(new Flags(Flags.Flag.ANSWERED))
+                    .oldFlags(new Flags())
+                    .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.ANSWERED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.ANSWERED);
     }
 
     @Test
     public void testShouldShowAnsweredRemoved() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.ANSWERED), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(
+                UpdatedFlags.builder()
+                    .uid(result.getUid())
+                    .modSeq(MOD_SEQ)
+                    .newFlags(new Flags(Flags.Flag.ANSWERED))
+                    .oldFlags(new Flags())
+                    .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.ANSWERED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.ANSWERED);
     }
 
     @Test
     public void testShouldShowDeletedAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.DELETED))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.DELETED))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.DELETED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.DELETED);
     }
 
     @Test
     public void testShouldShowDeletedRemoved() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.DELETED), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags(Flags.Flag.DELETED))
+                .oldFlags(new Flags())
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.DELETED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.DELETED);
     }
 
     @Test
     public void testShouldShowDraftAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.DRAFT))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.DRAFT))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.DRAFT, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.DRAFT);
     }
 
     @Test
     public void testShouldShowDraftRemoved() {
-        dispatcher.flagsUpdated(session,Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.DRAFT), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.DRAFT))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.DRAFT, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.DRAFT);
     }
 
     @Test
     public void testShouldShowFlaggedAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.FLAGGED))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags(Flags.Flag.FLAGGED))
+                .oldFlags(new Flags())
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.FLAGGED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.FLAGGED);
     }
 
     @Test
     public void testShouldShowFlaggedRemoved() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.FLAGGED), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.FLAGGED))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.FLAGGED, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.FLAGGED);
     }
 
     @Test
     public void testShouldShowRecentAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.RECENT))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags(Flags.Flag.RECENT))
+                .oldFlags(new Flags())
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.RECENT, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.RECENT);
     }
 
     @Test
     public void testShouldShowRecentRemoved() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.RECENT), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.RECENT))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.RECENT, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.RECENT);
     }
 
     @Test
     public void testShouldShowSeenAdded() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(),
-                new Flags(Flags.Flag.SEEN))));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags(Flags.Flag.SEEN))
+                .oldFlags(new Flags())
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.SEEN, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.SEEN);
     }
 
     @Test
     public void testShouldShowSeenRemoved() {
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, new Flags(
-                Flags.Flag.SEEN), new Flags())));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(new Flags())
+                .oldFlags(new Flags(Flags.Flag.SEEN))
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
-                .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.SEEN, iterator.next());
-        assertFalse(iterator.hasNext());
+            .get(0);
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.SEEN);
     }
 
     @Test
     public void testShouldShowMixedChanges() {
-        Flags originals = new Flags();
-        originals.add(Flags.Flag.DRAFT);
-        originals.add(Flags.Flag.RECENT);
-        Flags updated = new Flags();
-        updated.add(Flags.Flag.ANSWERED);
-        updated.add(Flags.Flag.DRAFT);
-        updated.add(Flags.Flag.SEEN);
-
-        dispatcher.flagsUpdated(session, Arrays.asList(result.getUid()), mailbox, Arrays.asList(new UpdatedFlags(result.getUid(), -1, originals, updated)));
-        assertEquals(1, collector.getEvents().size());
-        assertTrue(collector.getEvents().get(0) instanceof MailboxListener.FlagsUpdated);
+        dispatcher.flagsUpdated(session,
+            ImmutableList.of(result.getUid()),
+            mailbox,
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(result.getUid())
+                .modSeq(MOD_SEQ)
+                .newFlags(FlagsBuilder.builder()
+                    .add(Flags.Flag.ANSWERED, Flags.Flag.DRAFT, Flags.Flag.SEEN)
+                    .build())
+                .oldFlags(FlagsBuilder.builder()
+                    .add(Flags.Flag.DRAFT, Flags.Flag.RECENT)
+                    .build())
+                .build()));
+
+        assertThat(collector.getEvents()).hasSize(1)
+            .are(INSTANCE_OF_EVENT_FLAGS_UPDATED);
         MailboxListener.FlagsUpdated event = (MailboxListener.FlagsUpdated) collector.getEvents()
                 .get(0);
-        Iterator<Flags.Flag> iterator = event.getUpdatedFlags().get(0).systemFlagIterator();
-        assertNotNull(iterator);
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.ANSWERED, iterator.next());
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.RECENT, iterator.next());
-        assertTrue(iterator.hasNext());
-        assertEquals(Flags.Flag.SEEN, iterator.next());
-        assertFalse(iterator.hasNext());
+        assertThat(event.getUpdatedFlags().get(0).systemFlagIterator())
+            .containsOnly(Flags.Flag.SEEN, Flags.Flag.RECENT, Flags.Flag.ANSWERED);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/TestMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/TestMailboxSessionMapperFactory.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/TestMailboxSessionMapperFactory.java
index e90e982..98e51f4 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/TestMailboxSessionMapperFactory.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/TestMailboxSessionMapperFactory.java
@@ -345,7 +345,12 @@ public class TestMailboxSessionMapperFactory extends MailboxSessionMapperFactory
             public Map.Entry<MailboxId, UpdatedFlags> apply(MailboxMessage input) {
                 Preconditions.checkState(updateMode.equals(MessageManager.FlagsUpdateMode.ADD));
                 return new AbstractMap.SimpleEntry<MailboxId, UpdatedFlags>(input.getMailboxId(),
-                    new UpdatedFlags(input.getUid(), input.getModSeq(), input.createFlags(), newState));
+                    UpdatedFlags.builder()
+                        .uid(input.getUid())
+                        .modSeq(input.getModSeq())
+                        .newFlags(newState)
+                        .oldFlags(input.createFlags())
+                        .build());
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/json/EventSerializerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/json/EventSerializerTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/json/EventSerializerTest.java
index 6d92eff..900225a 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/json/EventSerializerTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/json/EventSerializerTest.java
@@ -48,12 +48,18 @@ public abstract class EventSerializerTest {
 
     public static final MessageUid UID = MessageUid.of(42);
     public static final long MOD_SEQ = 24L;
-    public static final UpdatedFlags UPDATED_FLAGS = new UpdatedFlags(UID, MOD_SEQ, new Flags(), new Flags(Flags.Flag.SEEN));
     public static final Flags FLAGS = new Flags();
+    public static final UpdatedFlags UPDATED_FLAGS = UpdatedFlags.builder()
+        .uid(UID)
+        .modSeq(MOD_SEQ)
+        .oldFlags(FLAGS)
+        .newFlags(new Flags(Flags.Flag.SEEN))
+        .build();
     public static final long SIZE = 45L;
     private static final MessageId MESSAGE_ID = new TestMessageId.Factory().generate();
     public static final SimpleMessageMetaData MESSAGE_META_DATA = new SimpleMessageMetaData(UID, MOD_SEQ, FLAGS, SIZE, null, MESSAGE_ID);
     public static final MailboxPath FROM = new MailboxPath("namespace", "user", "name");
+
     private EventSerializer serializer;
     private EventFactory eventFactory;
     private MailboxSession mailboxSession;

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageIdMapperTest.java
----------------------------------------------------------------------
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 0770ec1..b5c1070 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
@@ -329,7 +329,12 @@ public class MessageIdMapperTest<T extends MapperProvider> {
         Map<MailboxId, UpdatedFlags> flags = sut.setFlags(messageId, ImmutableList.of(message1.getMailboxId()), newFlags, FlagsUpdateMode.ADD);
 
         long modSeq = mapperProvider.highestModSeq(benwaInboxMailbox);
-        UpdatedFlags expectedUpdatedFlags = new UpdatedFlags(message1.getUid(), modSeq, new Flags(), newFlags);
+        UpdatedFlags expectedUpdatedFlags = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeq)
+            .oldFlags(new Flags())
+            .newFlags(newFlags)
+            .build();
         assertThat(flags).containsOnly(MapEntry.entry(benwaInboxMailbox.getMailboxId(), expectedUpdatedFlags));
     }
 
@@ -351,7 +356,12 @@ public class MessageIdMapperTest<T extends MapperProvider> {
         Map<MailboxId, UpdatedFlags> flags = sut.setFlags(messageId, ImmutableList.of(message1.getMailboxId()), newFlags, FlagsUpdateMode.REPLACE);
 
         long modSeq = mapperProvider.highestModSeq(benwaInboxMailbox);
-        UpdatedFlags expectedUpdatedFlags = new UpdatedFlags(message1.getUid(), modSeq, messageFlags, newFlags);
+        UpdatedFlags expectedUpdatedFlags = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeq)
+            .oldFlags(messageFlags)
+            .newFlags(newFlags)
+            .build();
 
         assertThat(flags).contains(MapEntry.entry(benwaInboxMailbox.getMailboxId(), expectedUpdatedFlags));
     }
@@ -374,7 +384,12 @@ public class MessageIdMapperTest<T extends MapperProvider> {
         Map<MailboxId, UpdatedFlags> flags = sut.setFlags(messageId, ImmutableList.of(message1.getMailboxId()), newFlags, FlagsUpdateMode.REMOVE);
 
         long modSeq = mapperProvider.highestModSeq(benwaInboxMailbox);
-        UpdatedFlags expectedUpdatedFlags = new UpdatedFlags(message1.getUid(), modSeq, messageFlags, new Flags(Flags.Flag.RECENT));
+        UpdatedFlags expectedUpdatedFlags = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeq)
+            .oldFlags(messageFlags)
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .build();
 
         assertThat(flags).contains(MapEntry.entry(benwaInboxMailbox.getMailboxId(), expectedUpdatedFlags));
     }
@@ -440,7 +455,12 @@ public class MessageIdMapperTest<T extends MapperProvider> {
             .add(Flag.ANSWERED)
             .build();
         long modSeq = mapperProvider.highestModSeq(benwaInboxMailbox);
-        UpdatedFlags expectedUpdatedFlags = new UpdatedFlags(message1.getUid(), modSeq, initialFlags, newFlags);
+        UpdatedFlags expectedUpdatedFlags = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeq)
+            .oldFlags(initialFlags)
+            .newFlags(newFlags)
+            .build();
         assertThat(flags).containsOnly(MapEntry.entry(benwaInboxMailbox.getMailboxId(), expectedUpdatedFlags));
     }
 
@@ -461,8 +481,18 @@ public class MessageIdMapperTest<T extends MapperProvider> {
 
         long modSeqBenwaInboxMailbox = mapperProvider.highestModSeq(benwaInboxMailbox);
         long modSeqBenwaWorkMailbox = mapperProvider.highestModSeq(benwaWorkMailbox);
-        UpdatedFlags expectedUpdatedFlags = new UpdatedFlags(message1.getUid(), modSeqBenwaInboxMailbox, new Flags(), newFlags);
-        UpdatedFlags expectedUpdatedFlags2 = new UpdatedFlags(message1InOtherMailbox.getUid(), modSeqBenwaWorkMailbox, new Flags(), newFlags);
+        UpdatedFlags expectedUpdatedFlags = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeqBenwaInboxMailbox)
+            .oldFlags(new Flags())
+            .newFlags(newFlags)
+            .build();
+        UpdatedFlags expectedUpdatedFlags2 = UpdatedFlags.builder()
+            .uid(message1.getUid())
+            .modSeq(modSeqBenwaWorkMailbox)
+            .oldFlags(new Flags())
+            .newFlags(newFlags)
+            .build();
         assertThat(flags).containsOnly(MapEntry.entry(benwaInboxMailbox.getMailboxId(), expectedUpdatedFlags),
                 MapEntry.entry(benwaWorkMailbox.getMailboxId(), expectedUpdatedFlags2));
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
----------------------------------------------------------------------
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 156744f..f7a315d 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
@@ -593,7 +593,12 @@ public class MessageMapperTest<T extends MapperProvider> {
         Iterator<UpdatedFlags> updatedFlags = messageMapper.updateFlags(benwaInboxMailbox, 
                 new FlagsUpdateCalculator(new Flags(Flags.Flag.FLAGGED), FlagsUpdateMode.REPLACE), MessageRange.one(message1.getUid()));
         assertThat(Lists.newArrayList(updatedFlags))
-            .containsOnly(new UpdatedFlags(message1.getUid(), modSeq + 1, new Flags(), new Flags(Flags.Flag.FLAGGED)));
+            .containsOnly(UpdatedFlags.builder()
+                .uid(message1.getUid())
+                .modSeq(modSeq + 1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags(Flags.Flag.FLAGGED))
+                .build());
     }
 
     @ContractTest
@@ -602,7 +607,12 @@ public class MessageMapperTest<T extends MapperProvider> {
         messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.FLAGGED), FlagsUpdateMode.REPLACE), MessageRange.one(message1.getUid()));
         long modSeq = messageMapper.getHighestModSeq(benwaInboxMailbox);
         assertThat(messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.SEEN), FlagsUpdateMode.ADD), MessageRange.one(message1.getUid())))
-            .containsOnly(new UpdatedFlags(message1.getUid(), modSeq + 1, new Flags(Flags.Flag.FLAGGED), new FlagsBuilder().add(Flags.Flag.SEEN, Flags.Flag.FLAGGED).build()));
+            .containsOnly(UpdatedFlags.builder()
+                    .uid(message1.getUid())
+                    .modSeq(modSeq + 1)
+                    .oldFlags(new Flags(Flags.Flag.FLAGGED))
+                    .newFlags(new FlagsBuilder().add(Flags.Flag.SEEN, Flags.Flag.FLAGGED).build())
+                    .build());
     }
 
     @ContractTest
@@ -619,7 +629,13 @@ public class MessageMapperTest<T extends MapperProvider> {
         messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new FlagsBuilder().add(Flags.Flag.FLAGGED, Flags.Flag.SEEN).build(), FlagsUpdateMode.REPLACE), MessageRange.one(message1.getUid()));
         long modSeq = messageMapper.getHighestModSeq(benwaInboxMailbox);
         assertThat(messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(Flags.Flag.SEEN), FlagsUpdateMode.REMOVE), MessageRange.one(message1.getUid())))
-            .containsOnly(new UpdatedFlags(message1.getUid(), modSeq + 1, new FlagsBuilder().add(Flags.Flag.SEEN, Flags.Flag.FLAGGED).build(), new Flags(Flags.Flag.FLAGGED)));
+            .containsOnly(
+                UpdatedFlags.builder()
+                    .uid(message1.getUid())
+                    .modSeq(modSeq + 1)
+                    .oldFlags(new FlagsBuilder().add(Flags.Flag.SEEN, Flags.Flag.FLAGGED).build())
+                    .newFlags(new Flags(Flags.Flag.FLAGGED))
+                    .build());
     }
 
     @ContractTest
@@ -734,7 +750,13 @@ public class MessageMapperTest<T extends MapperProvider> {
         saveMessages();
         long modSeq = messageMapper.getHighestModSeq(benwaInboxMailbox);
         assertThat(messageMapper.updateFlags(benwaInboxMailbox, new FlagsUpdateCalculator(new Flags(USER_FLAG), FlagsUpdateMode.ADD), MessageRange.one(message1.getUid())))
-            .containsOnly(new UpdatedFlags(message1.getUid(), modSeq + 1, new Flags(), new Flags(USER_FLAG)));
+            .containsOnly(
+                UpdatedFlags.builder()
+                    .uid(message1.getUid())
+                    .modSeq(modSeq + 1)
+                    .oldFlags(new Flags())
+                    .newFlags(new Flags(USER_FLAG))
+                    .build());
     }
 
     @ContractTest

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/mailbox/tool/src/test/java/org/apache/james/mailbox/indexer/registrations/MailboxRegistrationTest.java
----------------------------------------------------------------------
diff --git a/mailbox/tool/src/test/java/org/apache/james/mailbox/indexer/registrations/MailboxRegistrationTest.java b/mailbox/tool/src/test/java/org/apache/james/mailbox/indexer/registrations/MailboxRegistrationTest.java
index 4db7021..e965aec 100644
--- a/mailbox/tool/src/test/java/org/apache/james/mailbox/indexer/registrations/MailboxRegistrationTest.java
+++ b/mailbox/tool/src/test/java/org/apache/james/mailbox/indexer/registrations/MailboxRegistrationTest.java
@@ -91,7 +91,12 @@ public class MailboxRegistrationTest {
         MailboxListener.Event event = eventFactory.flagsUpdated(SESSION,
             Lists.newArrayList(UID),
             MAILBOX,
-            Lists.newArrayList(new UpdatedFlags(UID, MOD_SEQ, new Flags(), NEW_FLAGS)));
+            Lists.newArrayList(UpdatedFlags.builder()
+                .uid(UID)
+                .modSeq(MOD_SEQ)
+                .oldFlags(new Flags())
+                .newFlags(NEW_FLAGS)
+                .build()));
         mailboxRegistration.event(event);
         assertThat(mailboxRegistration.getImpactingEvents(UID)).containsExactly(new FlagsMessageEvent(INBOX, UID, NEW_FLAGS));
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/41c860c1/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
----------------------------------------------------------------------
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 abc895a..b687c53 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
@@ -26,7 +26,6 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.EnumSet;
@@ -48,7 +47,6 @@ import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.exception.BadCredentialsException;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.Content;
@@ -78,6 +76,8 @@ import org.apache.james.mailbox.store.mail.model.DefaultMessageId;
 import org.junit.Test;
 import org.slf4j.Logger;
 
+import com.google.common.collect.ImmutableList;
+
 public class MailboxEventAnalyserTest {
 
     private static final long BASE_SESSION_ID = 99;
@@ -159,7 +159,7 @@ public class MailboxEventAnalyserTest {
         }
         
         
-        public MailboxSession login(String userid, String passwd, Logger log) throws BadCredentialsException, MailboxException {
+        public MailboxSession login(String userid, String passwd, Logger log) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
 
         }
@@ -181,13 +181,11 @@ public class MailboxEventAnalyserTest {
                 public long getMessageCount(MailboxSession mailboxSession) throws MailboxException {
                     return 1;
                 }
-
                 
                 public boolean isWriteable(MailboxSession session) {
                     return false;
                 }
 
-                
                 public boolean isModSeqPermanent(MailboxSession session) {
                     return false;
                 }
@@ -195,29 +193,22 @@ public class MailboxEventAnalyserTest {
                 @Override
                 public Iterator<MessageUid> search(SearchQuery searchQuery, MailboxSession mailboxSession) throws MailboxException {
                     throw new UnsupportedOperationException("Not implemented");
-
                 }
 
                 @Override
                 public Iterator<MessageUid> expunge(MessageRange set, MailboxSession mailboxSession) throws MailboxException {
                     throw new UnsupportedOperationException("Not implemented");
-
                 }
 
-                
                 @Override
                 public Map<MessageUid, Flags> setFlags(Flags flags, FlagsUpdateMode mode, MessageRange set, MailboxSession mailboxSession) throws MailboxException {
                     throw new UnsupportedOperationException("Not implemented");
-
                 }
-
                 
                 public ComposedMessageId appendMessage(InputStream msgIn, Date internalDate, MailboxSession mailboxSession, boolean isRecent, Flags flags) throws MailboxException {
                     throw new UnsupportedOperationException("Not implemented");
-
                 }
 
-                
                 public MessageResultIterator getMessages(MessageRange set, FetchGroup fetchGroup, MailboxSession mailboxSession) throws MailboxException {
                     return new MessageResultIterator() {
                         boolean done = false;
@@ -225,8 +216,7 @@ public class MailboxEventAnalyserTest {
                         public void remove() {
                             throw new UnsupportedOperationException("Not implemented");
                         }
-                        
-                        
+
                         public MessageResult next() {
                             done = true;
                             return new MessageResult() {
@@ -253,98 +243,73 @@ public class MailboxEventAnalyserTest {
                                 public long getModSeq() {
                                     return 0;
                                 }
-
                                 
                                 public Flags getFlags() {
                                     return new Flags();
                                 }
 
-                                
                                 public long getSize() {
                                     return 0;
                                 }
-
                                 
                                 public Date getInternalDate() {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
 
-                                
                                 public MimeDescriptor getMimeDescriptor() throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
                                 }
-
                                 
                                 public Iterator<Header> iterateHeaders(MimePath path) throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
-
                                 
                                 public Iterator<Header> iterateMimeHeaders(MimePath path) throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
                                 }
-
                                 
                                 public Content getFullContent() throws MailboxException, IOException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
-
                                 
                                 public Content getFullContent(MimePath path) throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
 
-                                
                                 public Content getBody() throws MailboxException, IOException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
 
-                                
                                 public Content getBody(MimePath path) throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
 
-                                
                                 public Content getMimeBody(MimePath path) throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
 
-                                
                                 public Headers getHeaders() throws MailboxException {
                                     throw new UnsupportedOperationException("Not implemented");
-
                                 }
                                 
                                 public List<MessageAttachment> getAttachments() {
                                     throw new UnsupportedOperationException("Not implemented");
                                 }
-                                
                             };
                         }
                         
-                        
                         public boolean hasNext() {
                             return !done;
                         }
-                        
-                        
+
                         public MailboxException getException() {
                             return null;
                         }
                     };
                 }
-
                 
                 public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup fetchGroup) throws MailboxException {
                     throw new UnsupportedOperationException("Not implemented");
-
                 }
                 
                 public MailboxId getId() {
@@ -364,36 +329,29 @@ public class MailboxEventAnalyserTest {
         public char getDelimiter() {
             return '.';
         }
-        
-        
+
         public void deleteMailbox(MailboxPath mailboxPath, MailboxSession session) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
-
         }
         
-        
-        public MailboxSession createSystemSession(String userName, Logger log) throws BadCredentialsException, MailboxException {
+        public MailboxSession createSystemSession(String userName, Logger log) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
         }
         
-        
         public void createMailbox(MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
-            
         }
         
-        
         public List<MessageRange> copyMessages(MessageRange set, MailboxPath from, MailboxPath to, MailboxSession session) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
         }
 
         public java.util.List<MessageRange> copyMessages(MessageRange set, MailboxId from, MailboxId to, MailboxSession session) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
-        };
+        }
         
         public List<MessageRange> moveMessages(MessageRange set, MailboxPath from, MailboxPath to, MailboxSession session) throws MailboxException {
             throw new UnsupportedOperationException("Not implemented");
-
         }
 
         public boolean hasRight(MailboxPath mailboxPath, MailboxACLRight mailboxACLRight, MailboxSession mailboxSession) throws MailboxException {
@@ -619,7 +577,7 @@ public class MailboxEventAnalyserTest {
         
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        analyser.event(new FakeMailboxListenerAdded(mSession, Arrays.asList(MessageUid.of(11)), mailboxPath));
+        analyser.event(new FakeMailboxListenerAdded(mSession, ImmutableList.of(MessageUid.of(11)), mailboxPath));
         assertTrue(analyser.isSizeChanged());
     }
 
@@ -631,7 +589,7 @@ public class MailboxEventAnalyserTest {
         
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        analyser.event(new FakeMailboxListenerAdded(mSession,  Arrays.asList(MessageUid.of(11)), mailboxPath));
+        analyser.event(new FakeMailboxListenerAdded(mSession,  ImmutableList.of(MessageUid.of(11)), mailboxPath));
         analyser.resetEvents();
         assertFalse(analyser.isSizeChanged());
     }
@@ -644,8 +602,15 @@ public class MailboxEventAnalyserTest {
         
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                mSession,  Arrays.asList(MessageUid.of(90L)),  Arrays.asList(new UpdatedFlags(MessageUid.of(90), -1, new Flags(), new Flags())), mailboxPath);
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(mSession,
+            ImmutableList.of(MessageUid.of(90L)),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(MessageUid.of(90))
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags())
+                .build()),
+            mailboxPath);
         analyser.event(update);
         assertNotNull(analyser.flagUpdateUids());
         assertFalse(analyser.flagUpdateUids().iterator().hasNext());
@@ -660,8 +625,15 @@ public class MailboxEventAnalyserTest {
         
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                new MyMailboxSession(41), Arrays.asList(uid), Arrays.asList(new UpdatedFlags(uid, -1, new Flags(), new Flags(Flags.Flag.ANSWERED))), mailboxPath);
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(new MyMailboxSession(41),
+            ImmutableList.of(uid),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(uid)
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags(Flags.Flag.ANSWERED))
+                .build()),
+            mailboxPath);
         analyser.event(update);
         final Iterator<MessageUid> iterator = analyser.flagUpdateUids().iterator();
         assertNotNull(iterator);
@@ -677,8 +649,15 @@ public class MailboxEventAnalyserTest {
         MyImapSession imapsession = new MyImapSession(mSession);
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                mSession, Arrays.asList(uid), Arrays.asList(new UpdatedFlags(uid, -1, new Flags(), new Flags(Flags.Flag.ANSWERED))), mailboxPath);
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(mSession,
+            ImmutableList.of(uid),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(uid)
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags(Flags.Flag.ANSWERED))
+                .build()),
+            mailboxPath);
         analyser.event(update);
         analyser.event(update);
         analyser.deselect();
@@ -695,8 +674,15 @@ public class MailboxEventAnalyserTest {
         MyImapSession imapsession = new MyImapSession(mSession);
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                new MyMailboxSession(BASE_SESSION_ID), Arrays.asList(uid), Arrays.asList(new UpdatedFlags(uid, -1, new Flags(), new Flags(Flags.Flag.ANSWERED))), mailboxPath);
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(new MyMailboxSession(BASE_SESSION_ID),
+            ImmutableList.of(uid),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(uid)
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags(Flags.Flag.ANSWERED))
+                .build()),
+            mailboxPath);
         analyser.event(update);
         analyser.setSilentFlagChanges(true);
         analyser.event(update);
@@ -715,8 +701,15 @@ public class MailboxEventAnalyserTest {
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
         
         
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                mSession, Arrays.asList(MessageUid.of(345)), Arrays.asList(new UpdatedFlags(MessageUid.of(345), -1, new Flags(), new Flags())), mailboxPath);
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(mSession,
+            ImmutableList.of(MessageUid.of(345)),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(MessageUid.of(345))
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags())
+                .build()),
+            mailboxPath);
         analyser.event(update);
         analyser.setSilentFlagChanges(true);
         analyser.event(update);
@@ -730,20 +723,20 @@ public class MailboxEventAnalyserTest {
         MyMailboxSession mSession = new MyMailboxSession(BASE_SESSION_ID);
         MyImapSession imapsession = new MyImapSession(mSession);
         SelectedMailboxImpl analyser = new SelectedMailboxImpl(mockManager, imapsession, mailboxPath);
-        
-        
-        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(
-                mSession, Arrays.asList(MessageUid.of(886)), Arrays.asList(new UpdatedFlags(MessageUid.of(886), -1, new Flags(), new Flags(Flags.Flag.RECENT))), mailboxPath);
+
+
+        final FakeMailboxListenerFlagsUpdate update = new FakeMailboxListenerFlagsUpdate(mSession,
+            ImmutableList.of(MessageUid.of(886)),
+            ImmutableList.of(UpdatedFlags.builder()
+                .uid(MessageUid.of(886))
+                .modSeq(-1)
+                .oldFlags(new Flags())
+                .newFlags(new Flags(Flags.Flag.RECENT))
+                .build()),
+            mailboxPath);
         analyser.event(update);
         final Iterator<MessageUid> iterator = analyser.flagUpdateUids().iterator();
         assertNotNull(iterator);
         assertFalse(iterator.hasNext());
     }
-
-    /**
-     * @see org.apache.james.mailbox.MessageManager#myRights(org.apache.james.mailbox.MailboxSession)
-     */
-    public MailboxACLRights myRights(MailboxSession session) throws MailboxException {
-        return null;
-    }
 }


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


[07/10] james-project git commit: JAMES-1874 Remove Javadoc parameters without description in MessageManager

Posted by ad...@apache.org.
JAMES-1874 Remove Javadoc parameters without description in MessageManager

(as contained in method signature)


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

Branch: refs/heads/master
Commit: 796f8eb65b6f3a2ebff94c8fa5c0ab1eda84e69a
Parents: a9ff7fa
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 18:24:25 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../org/apache/james/mailbox/MessageManager.java    | 16 +---------------
 1 file changed, 1 insertion(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/796f8eb6/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
index 572c438..3f87ed4 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
@@ -54,28 +54,16 @@ public interface MessageManager {
 
     /**
      * Return the count of messages in the mailbox
-     * 
-     * @param mailboxSession
-     * @return count
-     * @throws MailboxException
      */
     long getMessageCount(MailboxSession mailboxSession) throws MailboxException;
 
     /**
      * Return the count of unseen messages in the mailbox
-     *
-     * @param mailboxSession
-     * @return count
-     * @throws MailboxException
      */
     long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException;
 
     /**
      * Return if the Mailbox is writable
-     * 
-     * @param session
-     * @return writable
-     * @throws MailboxException
      * @deprecated use
      *             {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)}
      */
@@ -85,9 +73,7 @@ public interface MessageManager {
     /**
      * Return true if {@link MessageResult#getModSeq()} is stored in a permanent
      * way.
-     * 
-     * @param session
-     * @return modSeqPermanent
+     *
      * @deprecated use
      *             {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)}
      */


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


[04/10] james-project git commit: JAMES-1874 Optimize GetMailboxes: do not search all mailboxes when a list of IDs is given

Posted by ad...@apache.org.
JAMES-1874 Optimize GetMailboxes: do not search all mailboxes when a list of IDs is given

It's a rework from a commit of Antoine, but includes:

  - Factorized code
  - No need to filter by mailbox as this filtering is already handled (gain ~ 3ms)


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

Branch: refs/heads/master
Commit: 0054b41a68fb0a36f2a30bc132e826b318d7357c
Parents: 3bdf918
Author: Antoine Duprat <ad...@linagora.com>
Authored: Thu Feb 2 09:57:03 2017 +0100
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:45 2017 +0700

----------------------------------------------------------------------
 .../james/jmap/methods/GetMailboxesMethod.java  | 35 ++++++++++----------
 1 file changed, 18 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/0054b41a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMailboxesMethod.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMailboxesMethod.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMailboxesMethod.java
index 446336f..d926d21 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMailboxesMethod.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/GetMailboxesMethod.java
@@ -19,10 +19,10 @@
 
 package org.apache.james.jmap.methods;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Predicate;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
@@ -89,29 +89,30 @@ public class GetMailboxesMethod implements Method {
     private GetMailboxesResponse getMailboxesResponse(GetMailboxesRequest mailboxesRequest, MailboxSession mailboxSession) {
         GetMailboxesResponse.Builder builder = GetMailboxesResponse.builder();
         try {
-            retrieveUserMailboxes(mailboxSession)
-                .stream()
-                .map(MailboxMetaData::getPath)
-                .map(mailboxPath -> mailboxFactory.fromMailboxPath(mailboxPath, mailboxSession))
+            Optional<ImmutableList<MailboxId>> mailboxIds = mailboxesRequest.getIds();
+            retrieveMailboxIds(mailboxIds, mailboxSession)
+                .map(mailboxId -> mailboxFactory.fromMailboxId(mailboxId, mailboxSession))
                 .filter(Optional::isPresent)
                 .map(Optional::get)
-                .filter(filterMailboxesById(mailboxesRequest.getIds()))
-                .sorted((m1, m2) -> m1.getSortOrder().compareTo(m2.getSortOrder()))
-                .forEach(mailbox -> builder.add(mailbox));
+                .sorted(Comparator.comparing(Mailbox::getSortOrder))
+                .forEach(builder::add);
             return builder.build();
         } catch (MailboxException e) {
             throw Throwables.propagate(e);
         }
     }
 
-    private Predicate<? super Mailbox> filterMailboxesById(Optional<ImmutableList<MailboxId>> ids) {
-        return (mailbox -> ids.map(list -> list.contains(mailbox.getId())).orElse(true));
-    }
-
-    private List<MailboxMetaData> retrieveUserMailboxes(MailboxSession session) throws MailboxException {
-        return mailboxManager.search(
-                MailboxQuery.builder(session).privateUserMailboxes().build(),
-                session);
+    private Stream<MailboxId> retrieveMailboxIds(Optional<ImmutableList<MailboxId>> mailboxIds, MailboxSession mailboxSession) throws MailboxException {
+        if (mailboxIds.isPresent()) {
+            return mailboxIds.get()
+                .stream();
+        } else {
+            List<MailboxMetaData> userMailboxes = mailboxManager.search(
+                MailboxQuery.builder(mailboxSession).privateUserMailboxes().build(),
+                mailboxSession);
+            return userMailboxes
+                .stream()
+                .map(MailboxMetaData::getId);
+        }
     }
-
 }


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


[08/10] james-project git commit: JAMES-1874 Cassandra optimization : Add a table for recent messages

Posted by ad...@apache.org.
JAMES-1874 Cassandra optimization : Add a table for recent messages


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

Branch: refs/heads/master
Commit: c8c12826015ac8706d75d7d423aea5f38cc146c4
Parents: 9c28aad
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 19:41:02 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../CassandraMailboxSessionMapperFactory.java   |  15 ++-
 .../mail/CassandraMailboxCounterDAO.java        |   3 +
 .../mail/CassandraMailboxRecentsDAO.java        | 105 ++++++++++++++++++
 .../mail/CassandraMessageIdMapper.java          |  29 +++--
 .../cassandra/mail/CassandraMessageMapper.java  |  55 +++++++---
 .../modules/CassandraMailboxRecentsModule.java  |  68 ++++++++++++
 .../table/CassandraMailboxRecentsTable.java     |  26 +++++
 .../cassandra/CassandraMailboxManagerTest.java  |  11 +-
 .../CassandraSubscriptionManagerTest.java       |   8 +-
 .../cassandra/CassandraTestSystemFixture.java   |  10 +-
 .../CassandraMailboxManagerAttachmentTest.java  |   6 +-
 .../mail/CassandraMailboxRecentDAOTest.java     | 107 +++++++++++++++++++
 .../cassandra/mail/CassandraMapperProvider.java |   6 +-
 .../cassandra/host/CassandraHostSystem.java     |   8 +-
 .../modules/mailbox/CassandraMailboxModule.java |   1 +
 15 files changed, 428 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index e3ce7c7..b384297 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -27,6 +27,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdMapper;
@@ -59,20 +60,24 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     private final CassandraMessageDAO messageDAO;
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
-    private CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
     private int maxRetry;
 
+
     @Inject
     public CassandraMailboxSessionMapperFactory(UidProvider uidProvider, ModSeqProvider modSeqProvider, 
             Session session, CassandraTypesProvider typesProvider,
-            CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO) {
+            CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
+            CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.session = session;
         this.messageDAO = messageDAO;
         this.messageIdDAO = messageIdDAO;
         this.imapUidDAO = imapUidDAO;
-        this.mailboxCounterDAO = new CassandraMailboxCounterDAO(session);
+        this.mailboxCounterDAO = mailboxCounterDAO;
+        this.mailboxRecentsDAO = mailboxRecentsDAO;
         this.maxRetry = DEFAULT_MAX_RETRY;
         this.typesProvider = typesProvider;
     }
@@ -84,13 +89,13 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     @Override
     public CassandraMessageMapper createMessageMapper(MailboxSession mailboxSession) {
         return new CassandraMessageMapper(uidProvider, modSeqProvider, null, maxRetry, createAttachmentMapper(mailboxSession),
-                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO);
+                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);
     }
 
     @Override
     public MessageIdMapper createMessageIdMapper(MailboxSession mailboxSession) throws MailboxException {
         return new CassandraMessageIdMapper(getMailboxMapper(mailboxSession), getAttachmentMapper(mailboxSession),
-                imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, modSeqProvider, mailboxSession);
+                imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, mailboxRecentsDAO, modSeqProvider, mailboxSession);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
----------------------------------------------------------------------
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 32a1fee..7f3c35a 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
@@ -29,6 +29,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 
+import javax.inject.Inject;
+
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.mailbox.cassandra.CassandraId;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
@@ -49,6 +51,7 @@ public class CassandraMailboxCounterDAO {
     private final PreparedStatement decrementUnseenCountStatement;
     private final PreparedStatement decrementMessageCountStatement;
 
+    @Inject
     public CassandraMailboxCounterDAO(Session session) {
         cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
         readStatement = createReadStatement(session);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
new file mode 100644
index 0000000..06df836
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.delete;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxRecentsTable;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import com.github.steveash.guavate.Guavate;
+
+public class CassandraMailboxRecentsDAO {
+
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement readStatement;
+    private final PreparedStatement deleteStatement;
+    private final PreparedStatement addStatement;
+
+    @Inject
+    public CassandraMailboxRecentsDAO(Session session) {
+        cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        readStatement = createReadStatement(session);
+        deleteStatement = createDeleteStatement(session);
+        addStatement = createAddStatement(session);
+    }
+
+    private PreparedStatement createReadStatement(Session session) {
+        return session.prepare(
+            select(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)
+                .from(CassandraMailboxRecentsTable.TABLE_NAME)
+                .where(eq(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID))));
+    }
+
+    private PreparedStatement createDeleteStatement(Session session) {
+        return session.prepare(
+            delete()
+                .from(CassandraMailboxRecentsTable.TABLE_NAME)
+                .where(eq(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID)))
+                .and(eq(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bindMarker(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID))));
+    }
+
+    private PreparedStatement createAddStatement(Session session) {
+        return session.prepare(
+            insertInto(CassandraMailboxRecentsTable.TABLE_NAME)
+                .value(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID))
+                .value(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bindMarker(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)));
+    }
+
+    public CompletableFuture<List<MessageUid>> getRecentMessageUidsInMailbox(CassandraId mailboxId) {
+        return cassandraAsyncExecutor.execute(bindWithMailbox(mailboxId, readStatement))
+            .thenApply(CassandraUtils::convertToStream)
+            .thenApply(stream -> stream.map(row -> row.getLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)))
+            .thenApply(stream -> stream.map(MessageUid::of))
+            .thenApply(stream -> stream.collect(Guavate.toImmutableList()));
+    }
+
+    private BoundStatement bindWithMailbox(CassandraId mailboxId, PreparedStatement statement) {
+        return statement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid());
+    }
+
+    public CompletableFuture<Void> removeFromRecent(CassandraId mailboxId, MessageUid messageUid) {
+        return cassandraAsyncExecutor.executeVoid(deleteStatement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid())
+            .setLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, messageUid.asLong()));
+    }
+
+    public CompletableFuture<Void> addToRecent(CassandraId mailboxId, MessageUid messageUid) {
+        return cassandraAsyncExecutor.executeVoid(addStatement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid())
+            .setLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, messageUid.asLong()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
index b4e0474..41d8749 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
@@ -69,18 +69,20 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageDAO messageDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
     private final ModSeqProvider modSeqProvider;
     private final MailboxSession mailboxSession;
 
     public CassandraMessageIdMapper(MailboxMapper mailboxMapper, AttachmentMapper attachmentMapper,
                                     CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO,
-                                    CassandraMailboxCounterDAO cassandraMailboxCounterDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
+                                    CassandraMailboxCounterDAO cassandraMailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
         this.mailboxMapper = mailboxMapper;
         this.attachmentMapper = attachmentMapper;
         this.imapUidDAO = imapUidDAO;
         this.messageIdDAO = messageIdDAO;
         this.messageDAO = messageDAO;
         this.mailboxCounterDAO = cassandraMailboxCounterDAO;
+        this.mailboxRecentsDAO = mailboxRecentsDAO;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
     }
@@ -149,6 +151,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
                 imapUidDAO.insert(composedMessageIdWithMetaData),
                 messageIdDAO.insert(composedMessageIdWithMetaData)))
             .thenCompose(voidValue -> CompletableFuture.allOf(
+                mailboxRecentsDAO.addToRecent(mailboxId, mailboxMessage.getUid()),
                 mailboxCounterDAO.incrementCount(mailboxId),
                 incrementUnseenOnSave(mailboxId, mailboxMessage.createFlags())))
             .join();
@@ -196,6 +199,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
             messageIdDAO.delete(mailboxId, metaData.getComposedMessageId().getUid()))
             .thenCompose(voidValue -> CompletableFuture.allOf(
                 mailboxCounterDAO.decrementCount(mailboxId),
+                mailboxRecentsDAO.removeFromRecent(mailboxId, metaData.getComposedMessageId().getUid()),
                 decrementUnseenOnDelete(mailboxId, metaData.getFlags())));
     }
 
@@ -243,20 +247,31 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
     private CompletableFuture<Pair<MailboxId, UpdatedFlags>> updateCounts(Pair<MailboxId, UpdatedFlags> pair) {
         CassandraId cassandraId = (CassandraId) pair.getLeft();
         return CompletableFuture.allOf(
-            incrementCountIfNeeded(pair.getRight().getOldFlags(), pair.getRight().getNewFlags(), cassandraId),
-            decrementCountIfNeeded(pair.getRight().getOldFlags(), pair.getRight().getNewFlags(), cassandraId))
+            manageRecent(pair.getRight(), cassandraId),
+            incrementCountIfNeeded(pair.getRight(), cassandraId),
+            decrementCountIfNeeded(pair.getRight(), cassandraId))
             .thenApply(voidValue -> pair);
     }
 
-    private CompletableFuture<Void> incrementCountIfNeeded(Flags oldFlags, Flags newFlags, CassandraId cassandraId) {
-        if (oldFlags.contains(Flags.Flag.SEEN) && !newFlags.contains(Flags.Flag.SEEN)) {
+    private CompletableFuture<Void> manageRecent(UpdatedFlags updatedFlags, CassandraId cassandraId) {
+        if (updatedFlags.isSet(Flags.Flag.RECENT)) {
+            return mailboxRecentsDAO.addToRecent(cassandraId, updatedFlags.getUid());
+        }
+        if (updatedFlags.isUnset(Flags.Flag.RECENT)){
+            return mailboxRecentsDAO.removeFromRecent(cassandraId, updatedFlags.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+    private CompletableFuture<Void> incrementCountIfNeeded(UpdatedFlags updatedFlags, CassandraId cassandraId) {
+        if (updatedFlags.isUnset(Flags.Flag.SEEN)) {
             return mailboxCounterDAO.incrementUnseen(cassandraId);
         }
         return CompletableFuture.completedFuture(null);
     }
 
-    private CompletableFuture<Void> decrementCountIfNeeded(Flags oldFlags, Flags newFlags, CassandraId cassandraId) {
-        if (!oldFlags.contains(Flags.Flag.SEEN) && newFlags.contains(Flags.Flag.SEEN)) {
+    private CompletableFuture<Void> decrementCountIfNeeded(UpdatedFlags updatedFlags,  CassandraId cassandraId) {
+        if (updatedFlags.isSet(Flags.Flag.SEEN)) {
             return mailboxCounterDAO.decrementUnseen(cassandraId);
         }
         return CompletableFuture.completedFuture(null);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
index 79763ee..38d27b8 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
@@ -75,10 +75,12 @@ public class CassandraMessageMapper implements MessageMapper {
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentDAO;
 
     public CassandraMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider,
                                   MailboxSession mailboxSession, int maxRetries, AttachmentMapper attachmentMapper,
-                                  CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMailboxCounterDAO mailboxCounterDAO) {
+                                  CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
+                                  CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
@@ -88,6 +90,7 @@ public class CassandraMessageMapper implements MessageMapper {
         this.messageIdDAO = messageIdDAO;
         this.imapUidDAO = imapUidDAO;
         this.mailboxCounterDAO = mailboxCounterDAO;
+        this.mailboxRecentDAO = mailboxRecentDAO;
     }
 
     @Override
@@ -123,6 +126,7 @@ public class CassandraMessageMapper implements MessageMapper {
             imapUidDAO.delete(messageId, mailboxId),
             messageIdDAO.delete(mailboxId, uid)
         ).thenCompose(voidValue -> CompletableFuture.allOf(
+            removeRecentOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags(), composedMessageId.getUid()),
             mailboxCounterDAO.decrementCount(mailboxId),
             decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags())));
     }
@@ -134,6 +138,14 @@ public class CassandraMessageMapper implements MessageMapper {
         return mailboxCounterDAO.decrementUnseen(mailboxId);
     }
 
+    private CompletableFuture<Void> removeRecentOnDelete(CassandraId mailboxId, Flags flags, MessageUid uid) {
+        if (flags.contains(Flag.RECENT)) {
+            return mailboxRecentDAO.removeFromRecent(mailboxId, uid);
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+
     private CompletableFuture<Optional<ComposedMessageIdWithMetaData>> retrieveMessageId(CassandraId mailboxId, MailboxMessage message) {
         return messageIdDAO.retrieve(mailboxId, message.getUid());
     }
@@ -167,13 +179,8 @@ public class CassandraMessageMapper implements MessageMapper {
     @Override
     public List<MessageUid> findRecentMessageUidsInMailbox(Mailbox mailbox) throws MailboxException {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        return retrieveMessages(retrieveMessageIds(mailboxId, MessageRange.all()), FetchType.Metadata, Optional.empty())
-                .filter(MailboxMessage::isRecent)
-                .flatMap(message -> imapUidDAO.retrieve((CassandraMessageId) message.getMessageId(), Optional.ofNullable(mailboxId)).join())
-                .map(ComposedMessageIdWithMetaData::getComposedMessageId)
-                .map(ComposedMessageId::getUid)
-                .sorted()
-                .collect(Collectors.toList());
+        return mailboxRecentDAO.getRecentMessageUidsInMailbox(mailboxId)
+                .join();
     }
 
     @Override
@@ -225,6 +232,7 @@ public class CassandraMessageMapper implements MessageMapper {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         save(mailbox, message)
             .thenCompose(voidValue -> CompletableFuture.allOf(
+                addRecentOnSave(mailboxId, message),
                 incrementUnseenOnSave(mailboxId, message.createFlags()),
                 mailboxCounterDAO.incrementCount(mailboxId)))
             .join();
@@ -238,12 +246,19 @@ public class CassandraMessageMapper implements MessageMapper {
         return mailboxCounterDAO.incrementUnseen(mailboxId);
     }
 
+    private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) {
+        if (message.createFlags().contains(Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, message.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
     @Override
     public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MessageRange set) throws MailboxException {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         return retrieveMessages(retrieveMessageIds(mailboxId, set), FetchType.Metadata, Optional.empty())
                 .flatMap(message -> updateFlagsOnMessage(mailbox, flagUpdateCalculator, message))
-                .map((UpdatedFlags updatedFlags) -> manageUnseenMessageCounts(mailbox, updatedFlags.getOldFlags(), updatedFlags.getNewFlags())
+                .map((UpdatedFlags updatedFlags) -> manageCounters(mailbox, updatedFlags)
                     .thenApply(voidValue -> updatedFlags))
                 .map(CompletableFuture::join)
                 .collect(Collectors.toList()) // This collect is here as we need to consume all the stream before returning result
@@ -282,17 +297,33 @@ public class CassandraMessageMapper implements MessageMapper {
                 imapUidDAO.insert(composedMessageIdWithMetaData));
     }
 
-    private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, Flags oldFlags, Flags newFlags) {
+    private CompletableFuture<Void> manageCounters(Mailbox mailbox, UpdatedFlags updatedFlags) {
+        return CompletableFuture.allOf(manageUnseenMessageCounts(mailbox, updatedFlags),
+                manageRecents(mailbox, updatedFlags));
+    }
+
+    private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, UpdatedFlags updatedFlags) {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        if (oldFlags.contains(Flag.SEEN) && !newFlags.contains(Flag.SEEN)) {
+        if (updatedFlags.isUnset(Flag.SEEN)) {
             return mailboxCounterDAO.incrementUnseen(mailboxId);
         }
-        if (!oldFlags.contains(Flag.SEEN) && newFlags.contains(Flag.SEEN)) {
+        if (updatedFlags.isSet(Flag.SEEN)) {
             return mailboxCounterDAO.decrementUnseen(mailboxId);
         }
         return CompletableFuture.completedFuture(null);
     }
 
+    private CompletableFuture<Void> manageRecents(Mailbox mailbox, UpdatedFlags updatedFlags) {
+        CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
+        if (updatedFlags.isUnset(Flag.RECENT)) {
+            return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid());
+        }
+        if (updatedFlags.isSet(Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
     private Stream<UpdatedFlags> updateFlagsOnMessage(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MailboxMessage message) {
         return tryMessageFlagsUpdate(flagUpdateCalculator, mailbox, message)
             .map(Stream::of)

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
new file mode 100644
index 0000000..6cccbd8
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.modules;
+
+import static com.datastax.driver.core.DataType.bigint;
+import static com.datastax.driver.core.DataType.list;
+import static com.datastax.driver.core.DataType.timeuuid;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.james.backends.cassandra.components.CassandraIndex;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.components.CassandraTable;
+import org.apache.james.backends.cassandra.components.CassandraType;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxRecentsTable;
+
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+
+public class CassandraMailboxRecentsModule implements CassandraModule {
+
+    private final List<CassandraTable> tables;
+    private final List<CassandraIndex> index;
+    private final List<CassandraType> types;
+
+    public CassandraMailboxRecentsModule() {
+        tables = Collections.singletonList(
+            new CassandraTable(CassandraMailboxRecentsTable.TABLE_NAME,
+                SchemaBuilder.createTable(CassandraMailboxRecentsTable.TABLE_NAME)
+                    .ifNotExists()
+                    .addPartitionKey(CassandraMailboxRecentsTable.MAILBOX_ID, timeuuid())
+                    .addClusteringColumn(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bigint())));
+        index = Collections.emptyList();
+        types = Collections.emptyList();
+    }
+
+    @Override
+    public List<CassandraTable> moduleTables() {
+        return tables;
+    }
+
+    @Override
+    public List<CassandraIndex> moduleIndex() {
+        return index;
+    }
+
+    @Override
+    public List<CassandraType> moduleTypes() {
+        return types;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
new file mode 100644
index 0000000..d729ae4
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.table;
+
+public interface CassandraMailboxRecentsTable {
+    String TABLE_NAME = "mailboxRecents";
+    String MAILBOX_ID = "mailboxId";
+    String RECENT_MESSAGE_UID = "recent_mesage_uid";
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
index 633bfbc..caadf4b 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
@@ -24,6 +24,8 @@ import org.apache.james.mailbox.acl.GroupMembershipResolver;
 import org.apache.james.mailbox.acl.MailboxACLResolver;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -34,6 +36,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraSubscriptionModule;
@@ -60,6 +63,7 @@ public class CassandraMailboxManagerTest {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraUidModule(),
         new CassandraModSeqModule(),
         new CassandraSubscriptionModule(),
@@ -77,13 +81,18 @@ public class CassandraMailboxManagerTest {
             CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(CASSANDRA.getConf(), messageIdFactory);
             CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(CASSANDRA.getConf(), messageIdFactory);
             CassandraMessageDAO messageDAO = new CassandraMessageDAO(CASSANDRA.getConf(), CASSANDRA.getTypesProvider(), messageIdFactory);
+            CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(CASSANDRA.getConf());
+            CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(CASSANDRA.getConf());
+
             CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider,
                 modSeqProvider,
                 CASSANDRA.getConf(),
                 CASSANDRA.getTypesProvider(),
                 messageDAO,
                 messageIdDAO,
-                imapUidDAO);
+                imapUidDAO,
+                mailboxCounterDAO,
+                mailboxRecentsDAO);
 
             MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
             GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
index f253f53..5d5e6c1 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
@@ -23,6 +23,8 @@ import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
 import org.apache.james.mailbox.AbstractSubscriptionManagerTest;
 import org.apache.james.mailbox.SubscriptionManager;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -45,6 +47,8 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
         CassandraMessageIdToImapUidDAO imapUidDAO = null;
         CassandraMessageDAO messageDAO = null;
         CassandraMessageIdDAO messageIdDAO = null;
+        CassandraMailboxCounterDAO mailboxCounterDAO = null;
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = null;
         return new CassandraSubscriptionManager(
             new CassandraMailboxSessionMapperFactory(
                 new CassandraUidProvider(cassandra.getConf()),
@@ -53,7 +57,9 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
                 cassandra.getTypesProvider(),
                 messageDAO,
                 messageIdDAO,
-                imapUidDAO
+                imapUidDAO,
+                mailboxCounterDAO,
+                mailboxRecentsDAO
             )
         );
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
index fdf3abe..0999c8e 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
@@ -23,6 +23,8 @@ import static org.mockito.Mockito.mock;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -33,6 +35,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -50,6 +53,7 @@ public class CassandraTestSystemFixture {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraUidModule(),
         new CassandraModSeqModule(),
         new CassandraAttachmentModule(),
@@ -64,13 +68,17 @@ public class CassandraTestSystemFixture {
         CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(CASSANDRA.getConf(), messageIdFactory);
         CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(CASSANDRA.getConf(), messageIdFactory);
         CassandraMessageDAO messageDAO = new CassandraMessageDAO(CASSANDRA.getConf(), CASSANDRA.getTypesProvider(), messageIdFactory);
+        CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(CASSANDRA.getConf());
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(CASSANDRA.getConf());
         return new CassandraMailboxSessionMapperFactory(uidProvider,
             modSeqProvider,
             CASSANDRA.getConf(),
             CASSANDRA.getTypesProvider(),
             messageDAO,
             messageIdDAO,
-            imapUidDAO);
+            imapUidDAO,
+            mailboxCounterDAO,
+            mailboxRecentsDAO);
     }
 
     public static CassandraMailboxManager createMailboxManager(CassandraMailboxSessionMapperFactory mapperFactory) throws Exception{

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
index b923c1f..93228aa 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
@@ -32,6 +32,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAclModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -47,6 +48,7 @@ public class CassandraMailboxManagerAttachmentTest extends AbstractMailboxManage
             new CassandraMailboxModule(),
             new CassandraMessageModule(),
             new CassandraMailboxCounterModule(),
+            new CassandraMailboxRecentsModule(),
             new CassandraModSeqModule(),
             new CassandraUidModule(),
             new CassandraAttachmentModule()));
@@ -64,7 +66,9 @@ public class CassandraMailboxManagerAttachmentTest extends AbstractMailboxManage
                 cassandra.getTypesProvider(),
                 new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), messageIdFactory),
                 new CassandraMessageIdDAO(cassandra.getConf(), messageIdFactory),
-                new CassandraMessageIdToImapUidDAO(cassandra.getConf(), messageIdFactory));
+                new CassandraMessageIdToImapUidDAO(cassandra.getConf(), messageIdFactory),
+                new CassandraMailboxCounterDAO(cassandra.getConf()),
+                new CassandraMailboxRecentsDAO(cassandra.getConf()));
         Authenticator noAuthenticator = null;
         mailboxManager = new CassandraMailboxManager(mailboxSessionMapperFactory, noAuthenticator, new NoMailboxPathLocker(), new MessageParser(), messageIdFactory); 
         mailboxManager.init();

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
new file mode 100644
index 0000000..81bb313
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
@@ -0,0 +1,107 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CassandraMailboxRecentDAOTest {
+    public static final MessageUid UID1 = MessageUid.of(36L);
+    public static final MessageUid UID2 = MessageUid.of(37L);
+    public static final CassandraId CASSANDRA_ID = CassandraId.timeBased();
+
+    private CassandraCluster cassandra;
+    private CassandraMailboxRecentsDAO testee;
+
+    @Before
+    public void setUp() {
+        cassandra = CassandraCluster.create(new CassandraMailboxRecentsModule());
+        cassandra.ensureAllTables();
+
+        testee = new CassandraMailboxRecentsDAO(cassandra.getConf());
+    }
+
+    @After
+    public void tearDown() {
+        cassandra.clearAllTables();
+    }
+
+    @Test
+    public void getRecentMessageUidsInMailboxShouldBeEmptyByDefault() throws Exception {
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void addToRecentShouldAddUidWhenEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+
+    @Test
+    public void removeFromRecentShouldRemoveUidWhenOnlyOneUid() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        testee.removeFromRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void removeFromRecentShouldNotFailIfNotExisting() throws Exception {
+        testee.removeFromRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void addToRecentShouldAddUidWhenNotEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        testee.addToRecent(CASSANDRA_ID, UID2).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1, UID2);
+    }
+
+    @Test
+    public void removeFromRecentShouldOnlyRemoveUidWhenNotEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+        testee.addToRecent(CASSANDRA_ID, UID2).join();
+
+        testee.removeFromRecent(CASSANDRA_ID, UID2).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+
+    @Test
+    public void addToRecentShouldBeIdempotent() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
index 2516944..ba39d49 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -59,6 +60,7 @@ public class CassandraMapperProvider implements MapperProvider {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraModSeqModule(),
         new CassandraUidModule(),
         new CassandraAttachmentModule(),
@@ -100,7 +102,9 @@ public class CassandraMapperProvider implements MapperProvider {
             cassandra.getTypesProvider(),
             new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), MESSAGE_ID_FACTORY),
             new CassandraMessageIdDAO(cassandra.getConf(), MESSAGE_ID_FACTORY),
-            new CassandraMessageIdToImapUidDAO(cassandra.getConf(), MESSAGE_ID_FACTORY));
+            new CassandraMessageIdToImapUidDAO(cassandra.getConf(), MESSAGE_ID_FACTORY),
+            new CassandraMailboxCounterDAO(cassandra.getConf()),
+            new CassandraMailboxRecentsDAO(cassandra.getConf()));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
----------------------------------------------------------------------
diff --git a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
index 16ef829..3b8b46c 100644
--- a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
+++ b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
@@ -29,6 +29,8 @@ import org.apache.james.mailbox.SubscriptionManager;
 import org.apache.james.mailbox.cassandra.CassandraMailboxManager;
 import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory;
 import org.apache.james.mailbox.cassandra.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -39,6 +41,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraQuotaModule;
@@ -78,6 +81,7 @@ public class CassandraHostSystem extends JamesImapHostSystem {
             new CassandraMailboxModule(),
             new CassandraMessageModule(),
             new CassandraMailboxCounterModule(),
+            new CassandraMailboxRecentsModule(),
             new CassandraUidModule(),
             new CassandraModSeqModule(),
             new CassandraSubscriptionModule(),
@@ -94,9 +98,11 @@ public class CassandraHostSystem extends JamesImapHostSystem {
         CassandraMessageDAO messageDAO = new CassandraMessageDAO(session, typesProvider, messageIdFactory);
         CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(session, messageIdFactory);
         CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(session, messageIdFactory);
+        CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(session);
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(session);
 
         CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider, modSeqProvider, 
-                session, typesProvider, messageDAO, messageIdDAO, imapUidDAO);
+                session, typesProvider, messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);
         
         mailboxManager = new CassandraMailboxManager(mapperFactory, userManager, new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory); 
         QuotaRootResolver quotaRootResolver = new DefaultQuotaRootResolver(mapperFactory);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 7ad3a28..df0693f 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -98,6 +98,7 @@ public class CassandraMailboxModule extends AbstractModule {
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraAclModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule.class);
+        cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMessageModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraSubscriptionModule.class);


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


[02/10] james-project git commit: JAMES-1874 Add missing tests for CassandraMailboxCounterDAO

Posted by ad...@apache.org.
JAMES-1874 Add missing tests for CassandraMailboxCounterDAO


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

Branch: refs/heads/master
Commit: 4297b454e7b4ec30ea56433d2228195a192eb7d9
Parents: bbe8e8a
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 10:41:18 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:45 2017 +0700

----------------------------------------------------------------------
 .../mail/CassandraMailboxCounterDAOTest.java    | 152 +++++++++++++++++++
 1 file changed, 152 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4297b454/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..013f20c
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAOTest.java
@@ -0,0 +1,152 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CassandraMailboxCounterDAOTest {
+    public static final int UID_VALIDITY = 15;
+    public static final CassandraId MAILBOX_ID = CassandraId.timeBased();
+
+    private CassandraCluster cassandra;
+    private CassandraMailboxCounterDAO testee;
+    private SimpleMailbox mailbox;
+
+    @Before
+    public void setUp() {
+        cassandra = CassandraCluster.create(new CassandraMailboxCounterModule());
+        cassandra.ensureAllTables();
+
+        testee = new CassandraMailboxCounterDAO(cassandra.getConf());
+
+        mailbox = new SimpleMailbox(new MailboxPath("#private", "user", "name"), UID_VALIDITY, MAILBOX_ID);
+    }
+
+    @After
+    public void tearDown() {
+        cassandra.clearAllTables();
+    }
+
+    @Test
+    public void countMessagesInMailboxShouldReturnEmptyByDefault() throws Exception {
+        assertThat(testee.countMessagesInMailbox(mailbox).join().isPresent()).isFalse();
+    }
+
+    @Test
+    public void countUnseenMessagesInMailboxShouldReturnEmptyByDefault() throws Exception {
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isFalse();
+    }
+
+    @Test
+    public void incrementCountShouldAddOneWhenAbsent() throws Exception {
+        testee.incrementCount(MAILBOX_ID).join();
+
+        assertThat(testee.countMessagesInMailbox(mailbox).join().get()).isEqualTo(1);
+    }
+
+    @Test
+    public void incrementUnseenShouldAddOneWhenAbsent() throws Exception {
+        testee.incrementUnseen(MAILBOX_ID).join();
+
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(1);
+    }
+
+    @Test
+    public void decrementCountShouldRemoveOne() throws Exception {
+        testee.incrementCount(MAILBOX_ID).join();
+
+        testee.decrementCount(MAILBOX_ID).join();
+
+        assertThat(testee.countMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countMessagesInMailbox(mailbox).join().get()).isEqualTo(0);
+    }
+
+    @Test
+    public void decrementUnseenShouldRemoveOne() throws Exception {
+        testee.incrementUnseen(MAILBOX_ID).join();
+
+        testee.decrementUnseen(MAILBOX_ID).join();
+
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(0);
+    }
+
+    @Test
+    public void incrementUnseenShouldHaveNoImpactOnMessageCount() throws Exception {
+        testee.incrementUnseen(MAILBOX_ID).join();
+
+        assertThat(testee.countMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countMessagesInMailbox(mailbox).join().get()).isEqualTo(0);
+    }
+
+    @Test
+    public void incrementCountShouldHaveNoEffectOnUnseenCount() throws Exception {
+        testee.incrementCount(MAILBOX_ID).join();
+
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(0);
+    }
+
+    @Test
+    public void decrementUnseenShouldHaveNoEffectOnMessageCount() throws Exception {
+        testee.incrementCount(MAILBOX_ID).join();
+
+        testee.decrementUnseen(MAILBOX_ID).join();
+
+        assertThat(testee.countMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countMessagesInMailbox(mailbox).join().get()).isEqualTo(1);
+    }
+
+    @Test
+    public void decrementCountShouldHaveNoEffectOnUnseenCount() throws Exception {
+        testee.incrementUnseen(MAILBOX_ID).join();
+
+        testee.decrementCount(MAILBOX_ID).join();
+
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(1);
+    }
+
+    @Test
+    public void decrementCountCanLeadToNegativeValue() throws Exception {
+        testee.decrementCount(MAILBOX_ID).join();
+
+        assertThat(testee.countMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countMessagesInMailbox(mailbox).join().get()).isEqualTo(-1);
+    }
+
+    @Test
+    public void decrementUnseenCanLeadToNegativeValue() throws Exception {
+        testee.decrementUnseen(MAILBOX_ID).join();
+
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isTrue();
+        assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(-1);
+    }
+}


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


[09/10] james-project git commit: JAMES-1874 Factorize more logic between CassandraMessageIdMapper and CassandraMessageMapper

Posted by ad...@apache.org.
JAMES-1874 Factorize more logic between CassandraMessageIdMapper and CassandraMessageMapper


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

Branch: refs/heads/master
Commit: 948b58f8a34345f5231d985d63c179cbf0cdc97f
Parents: 796f8eb
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 18:51:04 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../CassandraMailboxSessionMapperFactory.java   |   8 +-
 .../mail/CassandraIndexTableHandler.java        | 103 ++++++
 .../mail/CassandraMessageIdMapper.java          |  61 +--
 .../cassandra/mail/CassandraMessageMapper.java  |  70 +---
 .../mail/CassandraIndexTableHandlerTest.java    | 369 +++++++++++++++++++
 5 files changed, 489 insertions(+), 122 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index b384297..42dd429 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -25,6 +25,7 @@ import org.apache.james.backends.cassandra.init.CassandraTypesProvider;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper;
+import org.apache.james.mailbox.cassandra.mail.CassandraIndexTableHandler;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
@@ -62,9 +63,9 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
     private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
+    private final CassandraIndexTableHandler indexTableHandler;
     private int maxRetry;
 
-
     @Inject
     public CassandraMailboxSessionMapperFactory(UidProvider uidProvider, ModSeqProvider modSeqProvider, 
             Session session, CassandraTypesProvider typesProvider,
@@ -78,6 +79,7 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
         this.imapUidDAO = imapUidDAO;
         this.mailboxCounterDAO = mailboxCounterDAO;
         this.mailboxRecentsDAO = mailboxRecentsDAO;
+        this.indexTableHandler = new CassandraIndexTableHandler(mailboxRecentsDAO, mailboxCounterDAO);
         this.maxRetry = DEFAULT_MAX_RETRY;
         this.typesProvider = typesProvider;
     }
@@ -89,13 +91,13 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     @Override
     public CassandraMessageMapper createMessageMapper(MailboxSession mailboxSession) {
         return new CassandraMessageMapper(uidProvider, modSeqProvider, null, maxRetry, createAttachmentMapper(mailboxSession),
-                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);
+                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO, indexTableHandler);
     }
 
     @Override
     public MessageIdMapper createMessageIdMapper(MailboxSession mailboxSession) throws MailboxException {
         return new CassandraMessageIdMapper(getMailboxMapper(mailboxSession), getAttachmentMapper(mailboxSession),
-                imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, mailboxRecentsDAO, modSeqProvider, mailboxSession);
+                imapUidDAO, messageIdDAO, messageDAO, indexTableHandler, modSeqProvider, mailboxSession);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java
new file mode 100644
index 0000000..860a632
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandler.java
@@ -0,0 +1,103 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.UpdatedFlags;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+
+public class CassandraIndexTableHandler {
+
+    private final CassandraMailboxRecentsDAO mailboxRecentDAO;
+    private final CassandraMailboxCounterDAO mailboxCounterDAO;
+
+    @Inject
+    public CassandraIndexTableHandler(CassandraMailboxRecentsDAO mailboxRecentDAO,
+                                      CassandraMailboxCounterDAO mailboxCounterDAO) {
+        this.mailboxRecentDAO = mailboxRecentDAO;
+        this.mailboxCounterDAO = mailboxCounterDAO;
+    }
+
+    public CompletableFuture<Void> updateIndexOnDelete(ComposedMessageIdWithMetaData composedMessageIdWithMetaData, CassandraId mailboxId) {
+        return CompletableFuture.allOf(
+            mailboxRecentDAO.removeFromRecent(mailboxId, composedMessageIdWithMetaData.getComposedMessageId().getUid()),
+            mailboxCounterDAO.decrementCount(mailboxId),
+            decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags()));
+    }
+
+    public CompletableFuture<Void> updateIndexOnAdd(MailboxMessage message, CassandraId mailboxId) {
+        return CompletableFuture.allOf(
+            addRecentOnSave(mailboxId, message),
+            incrementUnseenOnSave(mailboxId, message.createFlags()),
+            mailboxCounterDAO.incrementCount(mailboxId));
+    }
+
+    public CompletableFuture<Void> updateIndexOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) {
+        return CompletableFuture.allOf(manageUnseenMessageCountsOnFlagsUpdate(mailboxId, updatedFlags),
+            manageRecentOnFlagsUpdate(mailboxId, updatedFlags));
+    }
+
+    private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) {
+        if (flags.contains(Flags.Flag.SEEN)) {
+            return CompletableFuture.completedFuture(null);
+        }
+        return mailboxCounterDAO.decrementUnseen(mailboxId);
+    }
+
+    private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) {
+        if (flags.contains(Flags.Flag.SEEN)) {
+            return CompletableFuture.completedFuture(null);
+        }
+        return mailboxCounterDAO.incrementUnseen(mailboxId);
+    }
+
+    private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) {
+        if (message.createFlags().contains(Flags.Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, message.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+    private CompletableFuture<Void> manageUnseenMessageCountsOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) {
+        if (updatedFlags.isModifiedToUnset(Flags.Flag.SEEN)) {
+            return mailboxCounterDAO.incrementUnseen(mailboxId);
+        }
+        if (updatedFlags.isModifiedToSet(Flags.Flag.SEEN)) {
+            return mailboxCounterDAO.decrementUnseen(mailboxId);
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+    private CompletableFuture<Void> manageRecentOnFlagsUpdate(CassandraId mailboxId, UpdatedFlags updatedFlags) {
+        if (updatedFlags.isModifiedToUnset(Flags.Flag.RECENT)) {
+            return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid());
+        }
+        if (updatedFlags.isModifiedToSet(Flags.Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
index 41d8749..e403268 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
@@ -68,21 +68,19 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageDAO messageDAO;
-    private final CassandraMailboxCounterDAO mailboxCounterDAO;
-    private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
+    private final CassandraIndexTableHandler indexTableHandler;
     private final ModSeqProvider modSeqProvider;
     private final MailboxSession mailboxSession;
 
     public CassandraMessageIdMapper(MailboxMapper mailboxMapper, AttachmentMapper attachmentMapper,
                                     CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO,
-                                    CassandraMailboxCounterDAO cassandraMailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
+                                    CassandraIndexTableHandler indexTableHandler, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
         this.mailboxMapper = mailboxMapper;
         this.attachmentMapper = attachmentMapper;
         this.imapUidDAO = imapUidDAO;
         this.messageIdDAO = messageIdDAO;
         this.messageDAO = messageDAO;
-        this.mailboxCounterDAO = cassandraMailboxCounterDAO;
-        this.mailboxRecentsDAO = mailboxRecentsDAO;
+        this.indexTableHandler = indexTableHandler;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
     }
@@ -150,20 +148,10 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
             .thenCompose(voidValue -> CompletableFuture.allOf(
                 imapUidDAO.insert(composedMessageIdWithMetaData),
                 messageIdDAO.insert(composedMessageIdWithMetaData)))
-            .thenCompose(voidValue -> CompletableFuture.allOf(
-                mailboxRecentsDAO.addToRecent(mailboxId, mailboxMessage.getUid()),
-                mailboxCounterDAO.incrementCount(mailboxId),
-                incrementUnseenOnSave(mailboxId, mailboxMessage.createFlags())))
+            .thenCompose(voidValue -> indexTableHandler.updateIndexOnAdd(mailboxMessage, mailboxId))
             .join();
     }
 
-    private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) {
-        if (flags.contains(Flags.Flag.SEEN)) {
-            return CompletableFuture.completedFuture(null);
-        }
-        return mailboxCounterDAO.incrementUnseen(mailboxId);
-    }
-
     @Override
     public void delete(MessageId messageId, List<MailboxId> mailboxIds) {
         CassandraMessageId cassandraMessageId = (CassandraMessageId) messageId;
@@ -197,17 +185,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
         return CompletableFuture.allOf(
             imapUidDAO.delete(messageId, mailboxId),
             messageIdDAO.delete(mailboxId, metaData.getComposedMessageId().getUid()))
-            .thenCompose(voidValue -> CompletableFuture.allOf(
-                mailboxCounterDAO.decrementCount(mailboxId),
-                mailboxRecentsDAO.removeFromRecent(mailboxId, metaData.getComposedMessageId().getUid()),
-                decrementUnseenOnDelete(mailboxId, metaData.getFlags())));
-    }
-
-    private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) {
-        if (flags.contains(Flags.Flag.SEEN)) {
-            return CompletableFuture.completedFuture(null);
-        }
-        return mailboxCounterDAO.decrementUnseen(mailboxId);
+            .thenCompose(voidValue -> indexTableHandler.updateIndexOnDelete(metaData, mailboxId));
     }
 
     @Override
@@ -246,37 +224,10 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
 
     private CompletableFuture<Pair<MailboxId, UpdatedFlags>> updateCounts(Pair<MailboxId, UpdatedFlags> pair) {
         CassandraId cassandraId = (CassandraId) pair.getLeft();
-        return CompletableFuture.allOf(
-            manageRecent(pair.getRight(), cassandraId),
-            incrementCountIfNeeded(pair.getRight(), cassandraId),
-            decrementCountIfNeeded(pair.getRight(), cassandraId))
+        return indexTableHandler.updateIndexOnFlagsUpdate(cassandraId, pair.getRight())
             .thenApply(voidValue -> pair);
     }
 
-    private CompletableFuture<Void> manageRecent(UpdatedFlags updatedFlags, CassandraId cassandraId) {
-        if (updatedFlags.isSet(Flags.Flag.RECENT)) {
-            return mailboxRecentsDAO.addToRecent(cassandraId, updatedFlags.getUid());
-        }
-        if (updatedFlags.isUnset(Flags.Flag.RECENT)){
-            return mailboxRecentsDAO.removeFromRecent(cassandraId, updatedFlags.getUid());
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    private CompletableFuture<Void> incrementCountIfNeeded(UpdatedFlags updatedFlags, CassandraId cassandraId) {
-        if (updatedFlags.isUnset(Flags.Flag.SEEN)) {
-            return mailboxCounterDAO.incrementUnseen(cassandraId);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    private CompletableFuture<Void> decrementCountIfNeeded(UpdatedFlags updatedFlags,  CassandraId cassandraId) {
-        if (updatedFlags.isSet(Flags.Flag.SEEN)) {
-            return mailboxCounterDAO.decrementUnseen(cassandraId);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
     private Optional<Pair<Flags, ComposedMessageIdWithMetaData>> tryFlagsUpdate(Flags newState, MessageManager.FlagsUpdateMode updateMode, MailboxId mailboxId, MessageId messageId) {
         try {
             return updateFlags(mailboxId, messageId, newState, updateMode);

http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
index 38d27b8..91d600e 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
@@ -76,11 +76,12 @@ public class CassandraMessageMapper implements MessageMapper {
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
     private final CassandraMailboxRecentsDAO mailboxRecentDAO;
+    private final CassandraIndexTableHandler indexTableHandler;
 
     public CassandraMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider,
                                   MailboxSession mailboxSession, int maxRetries, AttachmentMapper attachmentMapper,
                                   CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
-                                  CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO) {
+                                  CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO, CassandraIndexTableHandler indexTableHandler) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
@@ -91,6 +92,7 @@ public class CassandraMessageMapper implements MessageMapper {
         this.imapUidDAO = imapUidDAO;
         this.mailboxCounterDAO = mailboxCounterDAO;
         this.mailboxRecentDAO = mailboxRecentDAO;
+        this.indexTableHandler = indexTableHandler;
     }
 
     @Override
@@ -125,27 +127,9 @@ public class CassandraMessageMapper implements MessageMapper {
         return CompletableFuture.allOf(
             imapUidDAO.delete(messageId, mailboxId),
             messageIdDAO.delete(mailboxId, uid)
-        ).thenCompose(voidValue -> CompletableFuture.allOf(
-            removeRecentOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags(), composedMessageId.getUid()),
-            mailboxCounterDAO.decrementCount(mailboxId),
-            decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags())));
+        ).thenCompose(voidValue -> indexTableHandler.updateIndexOnDelete(composedMessageIdWithMetaData, mailboxId));
     }
 
-    private CompletableFuture<Void> decrementUnseenOnDelete(CassandraId mailboxId, Flags flags) {
-        if (flags.contains(Flags.Flag.SEEN)) {
-            return CompletableFuture.completedFuture(null);
-        }
-        return mailboxCounterDAO.decrementUnseen(mailboxId);
-    }
-
-    private CompletableFuture<Void> removeRecentOnDelete(CassandraId mailboxId, Flags flags, MessageUid uid) {
-        if (flags.contains(Flag.RECENT)) {
-            return mailboxRecentDAO.removeFromRecent(mailboxId, uid);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-
     private CompletableFuture<Optional<ComposedMessageIdWithMetaData>> retrieveMessageId(CassandraId mailboxId, MailboxMessage message) {
         return messageIdDAO.retrieve(mailboxId, message.getUid());
     }
@@ -231,34 +215,19 @@ public class CassandraMessageMapper implements MessageMapper {
         message.setModSeq(modSeqProvider.nextModSeq(mailboxSession, mailbox));
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         save(mailbox, message)
-            .thenCompose(voidValue -> CompletableFuture.allOf(
-                addRecentOnSave(mailboxId, message),
-                incrementUnseenOnSave(mailboxId, message.createFlags()),
-                mailboxCounterDAO.incrementCount(mailboxId)))
+            .thenCompose(voidValue -> indexTableHandler.updateIndexOnAdd(message, mailboxId))
             .join();
         return new SimpleMessageMetaData(message);
     }
 
-    private CompletableFuture<Void> incrementUnseenOnSave(CassandraId mailboxId, Flags flags) {
-        if (flags.contains(Flags.Flag.SEEN)) {
-            return CompletableFuture.completedFuture(null);
-        }
-        return mailboxCounterDAO.incrementUnseen(mailboxId);
-    }
 
-    private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) {
-        if (message.createFlags().contains(Flag.RECENT)) {
-            return mailboxRecentDAO.addToRecent(mailboxId, message.getUid());
-        }
-        return CompletableFuture.completedFuture(null);
-    }
 
     @Override
     public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MessageRange set) throws MailboxException {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         return retrieveMessages(retrieveMessageIds(mailboxId, set), FetchType.Metadata, Optional.empty())
                 .flatMap(message -> updateFlagsOnMessage(mailbox, flagUpdateCalculator, message))
-                .map((UpdatedFlags updatedFlags) -> manageCounters(mailbox, updatedFlags)
+                .map((UpdatedFlags updatedFlags) -> indexTableHandler.updateIndexOnFlagsUpdate(mailboxId, updatedFlags)
                     .thenApply(voidValue -> updatedFlags))
                 .map(CompletableFuture::join)
                 .collect(Collectors.toList()) // This collect is here as we need to consume all the stream before returning result
@@ -297,33 +266,6 @@ public class CassandraMessageMapper implements MessageMapper {
                 imapUidDAO.insert(composedMessageIdWithMetaData));
     }
 
-    private CompletableFuture<Void> manageCounters(Mailbox mailbox, UpdatedFlags updatedFlags) {
-        return CompletableFuture.allOf(manageUnseenMessageCounts(mailbox, updatedFlags),
-                manageRecents(mailbox, updatedFlags));
-    }
-
-    private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, UpdatedFlags updatedFlags) {
-        CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        if (updatedFlags.isUnset(Flag.SEEN)) {
-            return mailboxCounterDAO.incrementUnseen(mailboxId);
-        }
-        if (updatedFlags.isSet(Flag.SEEN)) {
-            return mailboxCounterDAO.decrementUnseen(mailboxId);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    private CompletableFuture<Void> manageRecents(Mailbox mailbox, UpdatedFlags updatedFlags) {
-        CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        if (updatedFlags.isUnset(Flag.RECENT)) {
-            return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid());
-        }
-        if (updatedFlags.isSet(Flag.RECENT)) {
-            return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid());
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
     private Stream<UpdatedFlags> updateFlagsOnMessage(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MailboxMessage message) {
         return tryMessageFlagsUpdate(flagUpdateCalculator, mailbox, message)
             .map(Stream::of)

http://git-wip-us.apache.org/repos/asf/james-project/blob/948b58f8/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..93d14e4
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
@@ -0,0 +1,369 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Optional;
+
+import javax.mail.Flags;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.UpdatedFlags;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CassandraIndexTableHandlerTest {
+
+    public static final CassandraId MAILBOX_ID = CassandraId.timeBased();
+    public static final MessageUid MESSAGE_UID = MessageUid.of(18L);
+    public static final CassandraMessageId CASSANDRA_MESSAGE_ID = new CassandraMessageId.Factory().generate();
+    public static final int UID_VALIDITY = 15;
+    public static final long MODSEQ = 17;
+
+    private CassandraCluster cassandra;
+    private CassandraMailboxCounterDAO mailboxCounterDAO;
+    private CassandraMailboxRecentsDAO mailboxRecentsDAO;
+    private CassandraIndexTableHandler testee;
+    private Mailbox mailbox;
+
+    @Before
+    public void setUp() {
+        cassandra = CassandraCluster.create(
+            new CassandraModuleComposite(
+                new CassandraMailboxCounterModule(),
+                new CassandraMailboxRecentsModule()));
+        cassandra.ensureAllTables();
+
+        mailboxCounterDAO = new CassandraMailboxCounterDAO(cassandra.getConf());
+        mailboxRecentsDAO = new CassandraMailboxRecentsDAO(cassandra.getConf());
+        testee = new CassandraIndexTableHandler(mailboxRecentsDAO, mailboxCounterDAO);
+
+        mailbox = new SimpleMailbox(new MailboxPath("#private", "user", "name"),
+            UID_VALIDITY,
+            MAILBOX_ID);
+    }
+
+    @After
+    public void tearDown() {
+        cassandra.clearAllTables();
+    }
+
+    @Test
+    public void updateIndexOnAddShouldIncrementMessageCount() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnAddShouldIncrementUnseenMessageCountWhenUnseen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnAddShouldNotIncrementUnseenMessageCountWhenSeen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateIndexOnAddShouldNotAddRecentWhenNoRecent() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .isEmpty();
+    }
+
+    @Test
+    public void updateIndexOnAddShouldAddRecentWhenRecent() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .containsOnly(MESSAGE_UID);
+    }
+
+    @Test
+    public void updateIndexOnDeleteShouldDecrementMessageCount() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
+                new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
+                new Flags(Flags.Flag.RECENT),
+                MODSEQ),
+            MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateIndexOnDeleteShouldDecrementUnseenMessageCountWhenUnseen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
+                new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
+                new Flags(),
+                MODSEQ),
+            MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateIndexOnDeleteShouldNotDecrementUnseenMessageCountWhenSeen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
+                new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
+                new Flags(Flags.Flag.SEEN),
+                MODSEQ),
+            MAILBOX_ID).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnDeleteShouldRemoveRecentWhenRecent() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
+            new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
+                new Flags(Flags.Flag.RECENT),
+                MODSEQ),
+            MAILBOX_ID).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .isEmpty();
+    }
+
+    @Test
+    public void updateIndexOnDeleteShouldRemoveUidFromRecentAnyway() throws Exception {
+        // Clean up strategy if some flags updates missed
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
+                new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
+                new Flags(),
+                MODSEQ),
+            MAILBOX_ID).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .isEmpty();
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldNotChangeMessageCount() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags())
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldDecrementUnseenMessageCountWhenSeenIsSet() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags(Flags.Flag.SEEN))
+            .oldFlags(new Flags())
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldIncrementUnseenMessageCountWhenSeenIsUnset() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags())
+            .oldFlags(new Flags(Flags.Flag.SEEN))
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldNotChangeUnseenCountWhenBothSeen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.SEEN));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags(Flags.Flag.SEEN))
+            .oldFlags(new Flags(Flags.Flag.SEEN))
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(0);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldNotChangeUnseenCountWhenBothUnSeen() throws Exception {
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags())
+            .oldFlags(new Flags())
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        Optional<Long> actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).join();
+        assertThat(actual.isPresent()).isTrue();
+        assertThat(actual.get()).isEqualTo(1);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldAddRecentOnSettingRecentFlag() throws Exception {
+        // Clean up strategy if some flags updates missed
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags());
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags())
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .containsOnly(MESSAGE_UID);
+    }
+
+    @Test
+    public void updateIndexOnFlagsUpdateShouldRemoveRecentOnUnsettingRecentFlag() throws Exception {
+        // Clean up strategy if some flags updates missed
+        MailboxMessage message = mock(MailboxMessage.class);
+        when(message.createFlags()).thenReturn(new Flags(Flags.Flag.RECENT));
+        when(message.getUid()).thenReturn(MESSAGE_UID);
+        testee.updateIndexOnAdd(message, MAILBOX_ID).join();
+
+        testee.updateIndexOnFlagsUpdate(MAILBOX_ID, UpdatedFlags.builder()
+            .uid(MESSAGE_UID)
+            .newFlags(new Flags())
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .modSeq(MODSEQ)
+            .build()).join();
+
+        assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID).join())
+            .isEmpty();
+    }
+
+}


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


[05/10] james-project git commit: JAMES-1874 GetMailboxes should not rely at all on the entire list of recent UIDs

Posted by ad...@apache.org.
JAMES-1874 GetMailboxes should not rely at all on the entire list of recent UIDs

Message manager should offer a way to get only the part of the Metadata we want.

My work implied de-deprecating a method. I checked it was not called.

I believe it was deprecated to force IMAP implementation to use IMAP business object.


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

Branch: refs/heads/master
Commit: 82c934e3c12c00e4fda1235fcc81bfca596bd391
Parents: 0054b41
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 10:14:42 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:45 2017 +0700

----------------------------------------------------------------------
 .../java/org/apache/james/mailbox/MessageManager.java | 14 ++++++++++----
 .../james/mailbox/store/StoreMessageManager.java      |  6 ++++++
 .../imap/processor/base/MailboxEventAnalyserTest.java |  6 +++++-
 .../org/apache/james/jmap/model/MailboxFactory.java   | 10 ++--------
 4 files changed, 23 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/82c934e3/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
index 1dc4c33..572c438 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageManager.java
@@ -53,18 +53,24 @@ public interface MessageManager {
     }
 
     /**
-     * Return the count
+     * Return the count of messages in the mailbox
      * 
      * @param mailboxSession
      * @return count
      * @throws MailboxException
-     * @deprecated use
-     *             {@link #getMetaData(boolean, MailboxSession, org.apache.james.mailbox.MessageManager.MetaData.FetchGroup)}
      */
-    @Deprecated
     long getMessageCount(MailboxSession mailboxSession) throws MailboxException;
 
     /**
+     * Return the count of unseen messages in the mailbox
+     *
+     * @param mailboxSession
+     * @return count
+     * @throws MailboxException
+     */
+    long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException;
+
+    /**
      * Return if the Mailbox is writable
      * 
      * @param session

http://git-wip-us.apache.org/repos/asf/james-project/blob/82c934e3/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
index 4a394d3..a7febbe 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
@@ -230,6 +230,12 @@ public class StoreMessageManager implements org.apache.james.mailbox.MessageMana
         return new Flags(MINIMAL_PERMANET_FLAGS);
     }
 
+    @Override
+    public long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException {
+        return mapperFactory.createMessageMapper(mailboxSession)
+            .countUnseenMessagesInMailbox(mailbox);
+    }
+
     /**
      * Returns the flags which are shared for the current mailbox, i.e. the
      * flags set up so that changes to those flags are visible to another user.

http://git-wip-us.apache.org/repos/asf/james-project/blob/82c934e3/protocols/imap/src/test/java/org/apache/james/imap/processor/base/MailboxEventAnalyserTest.java
----------------------------------------------------------------------
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 f68f9dd..abc895a 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
@@ -173,7 +173,11 @@ public class MailboxEventAnalyserTest {
         public MessageManager getMailbox(MailboxPath mailboxPath, MailboxSession session) throws MailboxException {
             return new MessageManager() {
 
-                
+                @Override
+                public long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException {
+                    return 0;
+                }
+
                 public long getMessageCount(MailboxSession mailboxSession) throws MailboxException {
                     return 1;
                 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/82c934e3/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MailboxFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MailboxFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MailboxFactory.java
index 7ac9e42..352eafc 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MailboxFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MailboxFactory.java
@@ -39,7 +39,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Splitter;
 
 public class MailboxFactory {
-    private static final boolean DONT_RESET_RECENT = false;
     private static final Logger LOGGER = LoggerFactory.getLogger(MailboxFactory.class);
 
     private final MailboxManager mailboxManager;
@@ -71,22 +70,17 @@ public class MailboxFactory {
     private Optional<Mailbox> fromMessageManager(MessageManager messageManager, MailboxSession mailboxSession) throws MailboxException {
         MailboxPath mailboxPath = messageManager.getMailboxPath();
         Optional<Role> role = Role.from(mailboxPath.getName());
-        MessageManager.MetaData mailboxMetaData = getMailboxMetaData(messageManager, mailboxSession);
         return Optional.ofNullable(Mailbox.builder()
                 .id(messageManager.getId())
                 .name(getName(mailboxPath, mailboxSession))
                 .parentId(getParentIdFromMailboxPath(mailboxPath, mailboxSession).orElse(null))
                 .role(role)
-                .unreadMessages(mailboxMetaData.getUnseenCount())
-                .totalMessages(mailboxMetaData.getMessageCount())
+                .unreadMessages(messageManager.getUnseenMessageCount(mailboxSession))
+                .totalMessages(messageManager.getMessageCount(mailboxSession))
                 .sortOrder(SortOrder.getSortOrder(role))
                 .build());
     }
 
-    private MessageManager.MetaData getMailboxMetaData(MessageManager messageManager, MailboxSession mailboxSession) throws MailboxException {
-        return messageManager.getMetaData(DONT_RESET_RECENT, mailboxSession, MessageManager.MetaData.FetchGroup.UNSEEN_COUNT);
-    }
-
     @VisibleForTesting String getName(MailboxPath mailboxPath, MailboxSession mailboxSession) {
         String name = mailboxPath.getName();
         if (name.contains(String.valueOf(mailboxSession.getPathDelimiter()))) {


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


[03/10] james-project git commit: JAMES-1874 Makes flag change detection easier, and tested

Posted by ad...@apache.org.
JAMES-1874 Makes flag change detection easier, and tested


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

Branch: refs/heads/master
Commit: bbe8e8a14fd42ecb52a5a0b3f9c74db118b93c9b
Parents: 82c934e
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 11:25:08 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:45 2017 +0700

----------------------------------------------------------------------
 .../org/apache/james/mailbox/FlagsBuilder.java  |   4 +
 .../james/mailbox/model/UpdatedFlags.java       |  58 +++++-
 .../james/mailbox/model/UpdatedFlagsTest.java   | 178 +++++++++++++++++++
 3 files changed, 239 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/bbe8e8a1/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
index 62e0ee1..1414cbf 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/FlagsBuilder.java
@@ -23,6 +23,10 @@ import javax.mail.Flags;
 
 public class FlagsBuilder {
 
+    public static FlagsBuilder builder() {
+        return new FlagsBuilder();
+    }
+
     private final Flags internalFlags;
 
     public FlagsBuilder() {

http://git-wip-us.apache.org/repos/asf/james-project/blob/bbe8e8a1/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
index 329ed41..989729b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/UpdatedFlags.java
@@ -28,6 +28,8 @@ import org.apache.james.mailbox.MessageUid;
 
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
 
 /**
  * Represent a Flag update for a message
@@ -36,6 +38,48 @@ import com.google.common.base.Objects;
  */
 public class UpdatedFlags {
 
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static class Builder {
+        private MessageUid uid;
+        private Flags oldFlags;
+        private Flags newFlags;
+        private Optional<Long> modSeq = Optional.absent();
+
+        private Builder() {
+        }
+
+        public Builder uid(MessageUid uid) {
+            this.uid = uid;
+            return this;
+        }
+
+        public Builder oldFlags(Flags oldFlags) {
+            this.oldFlags = oldFlags;
+            return this;
+        }
+
+        public Builder newFlags(Flags newFlags) {
+            this.newFlags = newFlags;
+            return this;
+        }
+
+        public Builder modSeq(long modSeq) {
+            this.modSeq = Optional.of(modSeq);
+            return this;
+        }
+
+        public UpdatedFlags build() {
+            Preconditions.checkState(uid != null);
+            Preconditions.checkState(newFlags != null);
+            Preconditions.checkState(oldFlags != null);
+            Preconditions.checkState(modSeq.isPresent());
+            return new UpdatedFlags(uid, modSeq.get(), oldFlags, newFlags);
+        }
+    }
+
     private final MessageUid uid;
     private final Flags oldFlags;
     private final Flags newFlags;
@@ -104,7 +148,19 @@ public class UpdatedFlags {
     public Flags getOldFlags() {
         return oldFlags;
     }
-    
+
+    public boolean isModifiedToSet(Flags.Flag flag) {
+        return newFlags.contains(flag) && !oldFlags.contains(flag);
+    }
+
+    public boolean isModifiedToUnset(Flags.Flag flag) {
+        return !newFlags.contains(flag) && oldFlags.contains(flag);
+    }
+
+    public boolean isUnchanged(Flags.Flag flag) {
+        return !isModifiedToSet(flag) && !isModifiedToUnset(flag);
+    }
+
     /**
      * Return the new {@link Flags} for the message
      * 

http://git-wip-us.apache.org/repos/asf/james-project/blob/bbe8e8a1/mailbox/api/src/test/java/org/apache/james/mailbox/model/UpdatedFlagsTest.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/UpdatedFlagsTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/UpdatedFlagsTest.java
new file mode 100644
index 0000000..e660dbc
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/UpdatedFlagsTest.java
@@ -0,0 +1,178 @@
+/****************************************************************
+ * 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.model;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.MessageUid;
+import org.junit.Test;
+
+public class UpdatedFlagsTest {
+
+    public static final MessageUid UID = MessageUid.of(45L);
+    public static final long MOD_SEQ = 47L;
+
+    @Test
+    public void isModifiedToSetShouldReturnTrueWhenFlagOnlyInNewFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToSet(Flags.Flag.RECENT)).isTrue();
+    }
+
+    @Test
+    public void isModifiedToSetShouldReturnFalseWhenFlagOnlyInOldFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToSet(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isModifiedToSetShouldReturnFalseWhenFlagIsOnNone() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToSet(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isModifiedToSetShouldReturnFalseWhenFlagIsOnBoth() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToSet(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isModifiedToUnsetShouldReturnFalseWhenFlagOnlyInNewFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToUnset(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isModifiedToUnsetShouldReturnTrueWhenFlagOnlyInOldFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToUnset(Flags.Flag.RECENT)).isTrue();
+    }
+
+    @Test
+    public void isModifiedToUnsetShouldReturnFalseWhenFlagIsOnNone() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToSet(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isModifiedToUnsetShouldReturnFalseWhenFlagIsOnBoth() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isModifiedToUnset(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isUnchangedShouldReturnFalseWhenFlagOnlyInNewFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isUnchanged(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isUnchangedShouldReturnFalseWhenFlagOnlyInOldFlag() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isUnchanged(Flags.Flag.RECENT)).isFalse();
+    }
+
+    @Test
+    public void isUnchangedShouldReturnTrueWhenFlagIsOnNone() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags())
+            .oldFlags(new Flags())
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isUnchanged(Flags.Flag.RECENT)).isTrue();
+    }
+
+    @Test
+    public void isUnchangedShouldReturnTrueWhenFlagIsOnBoth() {
+        UpdatedFlags updatedFlags = UpdatedFlags.builder()
+            .newFlags(new Flags(Flags.Flag.RECENT))
+            .oldFlags(new Flags(Flags.Flag.RECENT))
+            .uid(UID)
+            .modSeq(MOD_SEQ)
+            .build();
+
+        assertThat(updatedFlags.isUnchanged(Flags.Flag.RECENT)).isTrue();
+    }
+
+}


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