diff -uNr james-mailbox/cassandra/pom.xml james-mailbox-new/cassandra/pom.xml
--- james-mailbox/cassandra/pom.xml	2015-03-09 10:46:43.144295928 +0100
+++ james-mailbox-new/cassandra/pom.xml	2015-03-11 17:57:07.885494619 +0100
@@ -70,6 +70,11 @@
 	    <version>${cassandra-driver-core.version}</version>
         </dependency>
         <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.3.3</version>
+        </dependency>
+        <dependency>
     	    <groupId>org.cassandraunit</groupId>
    	    <artifactId>cassandra-unit</artifactId>
             <version>${cassandra-unit.version}</version>
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipResolver.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipResolver.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipResolver.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipResolver.java	2015-03-09 11:25:46.000000000 +0100
@@ -0,0 +1,78 @@
+/****************************************************************
+ * 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;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Session;
+import org.apache.james.mailbox.acl.GroupMembershipResolver;
+import org.apache.james.mailbox.cassandra.table.CassandraGroupMembershipTable;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
+
+
+/**
+ * A group membership resolver with data stored in Cassandra
+ */
+public class CassandraGroupMembershipResolver implements GroupMembershipResolver {
+
+    Session session;
+
+    public CassandraGroupMembershipResolver(Session session) {
+        this.session = session;
+    }
+
+    public void addMembership(String group, String user) {
+        session.execute(
+                insertInto(CassandraGroupMembershipTable.TABLE)
+                        .value(CassandraGroupMembershipTable.GROUP, group)
+                        .value(CassandraGroupMembershipTable.USER, user)
+                        .ifNotExists()
+        );
+    }
+
+    public void removeMembership(String group, String user) {
+        session.execute(
+                delete()
+                        .from(CassandraGroupMembershipTable.TABLE)
+                        .where(
+                                eq(CassandraGroupMembershipTable.GROUP, group)
+                        )
+                        .and(
+                                eq(CassandraGroupMembershipTable.USER, user)
+                        )
+        );
+    }
+
+    @Override
+    public boolean isMember(String user, String group) {
+        ResultSet resultSet = session.execute(
+                select(CassandraGroupMembershipTable.USER)
+                        .from(CassandraGroupMembershipTable.TABLE)
+                        .where(
+                                eq(CassandraGroupMembershipTable.USER, user)
+                        )
+                        .and(
+                                eq(CassandraGroupMembershipTable.GROUP, group)
+                        )
+        );
+        return !resultSet.isExhausted();
+    }
+
+
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java	2015-03-09 10:46:43.256296483 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java	2015-03-09 11:25:46.000000000 +0100
@@ -25,13 +25,13 @@
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.cassandra.mail.model.CassandraMailbox;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.Authenticator;
 import org.apache.james.mailbox.store.StoreMailboxManager;
 import org.apache.james.mailbox.store.StoreMessageManager;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
 
 /**
  * Cassandra implementation of {@link StoreMailboxManager}
@@ -46,7 +46,8 @@
 
     @Override
     protected Mailbox<UUID> doCreateMailbox(MailboxPath mailboxPath, MailboxSession session) throws MailboxException {
-        return new SimpleMailbox<UUID>(mailboxPath, randomUidValidity());
+        CassandraMailboxSessionMapperFactory cassandraMailboxSessionMapperFactory = (CassandraMailboxSessionMapperFactory)getMapperFactory();
+        return new CassandraMailbox<UUID>(mailboxPath, randomUidValidity(), cassandraMailboxSessionMapperFactory.getSession());
     }
 
     @Override
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java	2015-03-09 11:25:46.000000000 +0100
@@ -71,4 +71,8 @@
     public UidProvider<UUID> getUidProvider() {
         return uidProvider;
     }
+
+    Session getSession() {
+        return session;
+    }
 }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java	2015-03-09 10:46:43.204296226 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMessageManager.java	2015-03-09 11:25:46.000000000 +0100
@@ -27,12 +27,18 @@
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.cassandra.mail.model.CassandraMailbox;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.UnsupportedRightException;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.SimpleMailboxACL;
 import org.apache.james.mailbox.store.MailboxEventDispatcher;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
 import org.apache.james.mailbox.store.StoreMessageManager;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Cassandra implementation of {@link StoreMessageManager}
@@ -40,9 +46,18 @@
  */
 public class CassandraMessageManager extends StoreMessageManager<UUID> {
 
-    public CassandraMessageManager(MailboxSessionMapperFactory<UUID> mapperFactory, MessageSearchIndex<UUID> index, MailboxEventDispatcher<UUID> dispatcher, MailboxPathLocker locker, Mailbox<UUID> mailbox) throws MailboxException {
+    private static final int ACL_MAX_RETRY = 100000;
+
+    private static final Logger LOG = LoggerFactory.getLogger(CassandraMessageManager.class);
+    private int aclMaxRetry;
+
+    public CassandraMessageManager(MailboxSessionMapperFactory<UUID> mapperFactory, MessageSearchIndex<UUID> index, MailboxEventDispatcher<UUID> dispatcher, MailboxPathLocker locker, Mailbox<UUID> mailbox, int aclMaxRetry) throws MailboxException {
         super(mapperFactory, index, dispatcher, locker, mailbox, new UnionMailboxACLResolver(), new SimpleGroupMembershipResolver());
+        this.aclMaxRetry = aclMaxRetry;
+    }
 
+    public CassandraMessageManager(MailboxSessionMapperFactory<UUID> mapperFactory, MessageSearchIndex<UUID> index, MailboxEventDispatcher<UUID> dispatcher, MailboxPathLocker locker, Mailbox<UUID> mailbox) throws MailboxException {
+        this(mapperFactory, index, dispatcher, locker, mailbox, ACL_MAX_RETRY);
     }
 
     /**
@@ -54,4 +69,54 @@
         flags.add(Flags.Flag.USER);
         return flags;
     }
+
+    /**
+     * @throws UnsupportedRightException
+     * @see MessageManager#setRights(MailboxACL.MailboxACLEntryKey, MailboxACL.EditMode, MailboxACL.MailboxACLRights) (String, MailboxACL.EditMode, MailboxACL.MailboxACLRights)
+     */
+    @Override
+    public void setRights(MailboxACL.MailboxACLEntryKey mailboxACLEntryKey, MailboxACL.EditMode editMode, MailboxACL.MailboxACLRights mailboxAclRights) throws UnsupportedRightException {
+        boolean succeeded = false;
+        CassandraMailbox<UUID> cassandraMailbox;
+        try {
+            cassandraMailbox = (CassandraMailbox<UUID>) getMailboxEntity();
+        } catch(MailboxException me) {
+            LOG.error("Can not retrieve mailbox while setting ACLs");
+            return;
+        }
+        int tries = 0;
+        while(!succeeded && tries < aclMaxRetry) {
+            tries++;
+            succeeded = tryToUpdateACL(mailboxACLEntryKey, editMode, mailboxAclRights, cassandraMailbox);
+        }
+        if(!succeeded) {
+            LOG.error("Can not apply lightweight transaction for setting ACL rights on " + cassandraMailbox.getUser() +
+                    ":" + cassandraMailbox.getNamespace() + ":" + cassandraMailbox.getName() + " after " + tries + " tries.");
+        }
+    }
+
+    private boolean tryToUpdateACL(MailboxACL.MailboxACLEntryKey mailboxACLEntryKey, MailboxACL.EditMode editMode, MailboxACL.MailboxACLRights mailboxAclRights, CassandraMailbox<UUID> cassandraMailbox) throws UnsupportedRightException {
+        boolean succeeded;
+        CassandraMailbox<UUID>.ACLWithVersion aclWithVersion = cassandraMailbox.getACLWithVersion();
+        MailboxACL acl = aclWithVersion.acl;
+        if (acl == null ) {
+            acl = SimpleMailboxACL.EMPTY;
+        }
+        acl = manageMailboxACL(mailboxACLEntryKey, editMode, mailboxAclRights, acl);
+        succeeded = cassandraMailbox.setACLWithVersion(acl, aclWithVersion.version);
+        return succeeded;
+    }
+
+    private MailboxACL manageMailboxACL(MailboxACL.MailboxACLEntryKey mailboxACLEntryKey, MailboxACL.EditMode editMode, MailboxACL.MailboxACLRights mailboxAclRights, MailboxACL acl) throws UnsupportedRightException {
+        switch (editMode) {
+            case ADD:
+                return acl.union(mailboxACLEntryKey, mailboxAclRights);
+            case REMOVE:
+                return acl.except(mailboxACLEntryKey, mailboxAclRights);
+            case REPLACE:
+                return acl.replace(mailboxACLEntryKey, mailboxAclRights);
+            default:
+                throw new IllegalStateException("Unexpected " + MailboxACL.EditMode.class.getName() + "." + editMode);
+        }
+    }
 }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraSession.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraSession.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraSession.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraSession.java	2015-03-13 15:28:48.000000000 +0100
@@ -29,9 +29,11 @@
 import com.datastax.driver.core.Statement;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import java.util.List;
+
 /**
  * A Cassandra session with the default keyspace
- * 
+ *
  */
 public class CassandraSession implements Session {
     private final static String DEFAULT_CLUSTER_IP = "localhost";
@@ -41,8 +43,44 @@
 
     private Session session;
 
+    public CassandraSession(List<String> ips, List<Integer> ports, String keyspace, int replicationFactor, String userName, String password) {
+        Cluster.Builder clusterBuilder = Cluster.builder();
+        for(int i = 0; i < ips.size(); i++) {
+            clusterBuilder.addContactPoint(ips.get(i)).withPort(ports.get(i));
+        }
+        clusterBuilder.withCredentials(userName, password);
+        finishInitialisation(keyspace, replicationFactor, clusterBuilder);
+    }
+
+    public CassandraSession(List<String> ips, List<Integer> ports, String keyspace, int replicationFactor) {
+        Cluster.Builder clusterBuilder = Cluster.builder();
+        for(int i = 0; i < ips.size(); i++) {
+            clusterBuilder.addContactPoint(ips.get(i)).withPort(ports.get(i));
+        }
+        finishInitialisation(keyspace, replicationFactor, clusterBuilder);
+    }
+
+    public CassandraSession(List<String> ips, int port, String keyspace, int replicationFactor) {
+        Cluster.Builder clusterBuilder = Cluster.builder();
+        for(String ip : ips) {
+            clusterBuilder.addContactPoint(ip).withPort(port);
+        }
+        finishInitialisation(keyspace, replicationFactor, clusterBuilder);
+    }
+
+    public CassandraSession(String ip, int port, String keyspace, int replicationFactor, String userName, String password) {
+        Cluster.Builder clusterBuilder =  Cluster.builder().addContactPoint(ip).withPort(port).withCredentials(userName, password);
+        finishInitialisation(keyspace, replicationFactor, clusterBuilder);
+    }
+
     public CassandraSession(String ip, int port, String keyspace, int replicationFactor) {
-        Cluster cluster = Cluster.builder().addContactPoint(ip).withPort(port).build();
+        Cluster.Builder clusterBuilder =  Cluster.builder().addContactPoint(ip).withPort(port);
+        finishInitialisation(keyspace, replicationFactor, clusterBuilder);
+    }
+
+
+    private void finishInitialisation(String keyspace, int replicationFactor, Cluster.Builder clusterBuilder) {
+        Cluster cluster = clusterBuilder.build();
         if (cluster.getMetadata().getKeyspace(keyspace) == null) {
             initDatabase(cluster, keyspace, replicationFactor);
         }
@@ -52,13 +90,24 @@
     private void initDatabase(Cluster cluster, String keyspace, int replicationFactor) {
         session = cluster.connect();
         session.execute("CREATE KEYSPACE IF NOT EXISTS " + keyspace + " WITH replication " + "= {'class':'SimpleStrategy', 'replication_factor':" + replicationFactor + "};");
-        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".mailbox (" + "id uuid PRIMARY KEY," + "name text, namespace text," + "uidvalidity bigint," + "user text," + "path text" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".mailbox (" + "id uuid PRIMARY KEY," + "name text, namespace text," + "uidvalidity bigint," + "user text," + "path text," + "acl text," + "acl_version bigint," + ");");
         session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".mailbox(path);");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".mailbox(namespace);");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".mailbox(user);");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".mailbox(name);");
         session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".messageCounter (" + "mailboxId UUID PRIMARY KEY," + "nextUid bigint," + ");");
-        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".mailboxCounters (" + "mailboxId UUID PRIMARY KEY," + "count counter," + "unseen counter," + "nextModSeq counter" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".mailboxCounters (" + "mailboxId UUID PRIMARY KEY," + "count counter," + "unseen counter," + ");");
         session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".message (" + "mailboxId UUID," + "uid bigint," + "internalDate timestamp," + "bodyStartOctet int," + "content blob," + "modSeq bigint," + "mediaType text," + "subType text," + "fullContentOctets int," + "bodyOctets int,"
-                + "textualLineCount bigint," + "bodyContent blob," + "headerContent blob," + "flagAnswered boolean," + "flagDeleted boolean," + "flagDraft boolean," + "flagRecent boolean," + "flagSeen boolean," + "flagFlagged boolean," + "flagUser boolean," + "flagVersion bigint," + "PRIMARY KEY (mailboxId, uid)" + ");");
+                + "textualLineCount bigint," + "bodyContent blob," + "headerContent blob," + "flagAnswered boolean," + "flagDeleted boolean," + "flagDraft boolean," + "flagRecent boolean," + "flagSeen boolean," + "flagFlagged boolean," + "flagUser boolean," + "userFlags text," + "flagVersion bigint," + "PRIMARY KEY (mailboxId, uid)" + ");");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".message(flagRecent);");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".message(flagSeen);");
+        session.execute("CREATE INDEX IF NOT EXISTS ON " + keyspace + ".message(flagDeleted);");
         session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".subscription (" + "user text," + "mailbox text," + "PRIMARY KEY (mailbox, user)" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".quota (" + "user text PRIMARY KEY," + "size_quota counter," + "count_quota counter" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".quota_max (" + "user text PRIMARY KEY," + "size_quota_max bigint," + "count_quota_max bigint" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".default_max_quota_table (" + "id int PRIMARY KEY," + "default_max_size_quota bigint," + "default_max_count_quota bigint" + ");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".group_membership ("+"user text,"+"group text,"+"PRIMARY KEY (user, group)"+");");
+        session.execute("CREATE TABLE IF NOT EXISTS " + keyspace + ".modseq (" + "mailboxId uuid PRIMARY KEY," + "nextModseq bigint" + ");");
         session.close();
     }
 
@@ -145,5 +194,4 @@
     public Cluster getCluster() {
         return session.getCluster();
     }
-
 }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java	2015-03-09 10:46:43.204296226 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java	2015-03-13 15:21:36.000000000 +0100
@@ -22,7 +22,6 @@
 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
-
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.FIELDS;
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.ID;
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.NAME;
@@ -31,16 +30,18 @@
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.TABLE_NAME;
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.UIDVALIDITY;
 import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.USER;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.ACL;
+import static org.apache.james.mailbox.cassandra.table.CassandraMailboxTable.ACL_VERSION;
 
 import java.util.List;
 import java.util.UUID;
 
+import org.apache.james.mailbox.cassandra.mail.model.CassandraMailbox;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
-import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
 
 import com.datastax.driver.core.ResultSet;
 import com.datastax.driver.core.Row;
@@ -76,19 +77,22 @@
         }
     }
 
-    private SimpleMailbox<UUID> mailbox(Row row) {
-        SimpleMailbox<UUID> mailbox = new SimpleMailbox<UUID>(new MailboxPath(row.getString(NAMESPACE), row.getString(USER), row.getString(NAME)), row.getLong(UIDVALIDITY));
+    private CassandraMailbox<UUID> mailbox(Row row) {
+        CassandraMailbox<UUID> mailbox = new CassandraMailbox<UUID>(new MailboxPath(row.getString(NAMESPACE), row.getString(USER), row.getString(NAME)), row.getLong(UIDVALIDITY), session);
         mailbox.setMailboxId(row.getUUID(ID));
         return mailbox;
     }
 
     @Override
     public List<Mailbox<UUID>> findMailboxWithPathLike(MailboxPath path) throws MailboxException {
-        final String regexWithUser = ".*" + path.getNamespace() + ".*" + path.getUser() + ".*" + path.getName() + ".*";
-        final String regexWithoutUser = ".*" + path.getNamespace() + ".*null.*" + path.getName() + ".*";
+        String user = path.getUser();
+        if(user == null) {
+            user = "null";
+        }
+        String regex = path.getName().replace("%", ".*");
         Builder<Mailbox<UUID>> result = ImmutableList.<Mailbox<UUID>> builder();
-        for (Row row : session.execute(select(FIELDS).from(TABLE_NAME))) {
-            if (row.getString(PATH).matches(regexWithUser) || row.getString(PATH).matches(regexWithoutUser)) {
+        for (Row row : session.execute(select(FIELDS).from(TABLE_NAME).allowFiltering().where(eq(NAMESPACE, path.getNamespace())).and(eq(USER, user)))) {
+            if (row.getString(NAME).matches(regex) ) {
                 result.add(mailbox(row));
             }
         }
@@ -97,16 +101,16 @@
 
     @Override
     public void save(Mailbox<UUID> mailbox) throws MailboxException {
-        Preconditions.checkArgument(mailbox instanceof SimpleMailbox<?>);
-        SimpleMailbox<UUID> simpleMailbox = (SimpleMailbox<UUID>) mailbox;
-        if (simpleMailbox.getMailboxId() == null) {
-            simpleMailbox.setMailboxId(UUID.randomUUID());
+        Preconditions.checkArgument(mailbox instanceof CassandraMailbox);
+        CassandraMailbox<UUID> cassandraMailbox = (CassandraMailbox<UUID>) mailbox;
+        if (cassandraMailbox.getMailboxId() == null) {
+            cassandraMailbox.setMailboxId(UUID.randomUUID());
         }
-        upsertMailbox(simpleMailbox);
+        upsertMailbox(cassandraMailbox);
     }
 
-    private void upsertMailbox(SimpleMailbox<UUID> mailbox) {
-        session.execute(insertInto(TABLE_NAME).value(ID, mailbox.getMailboxId()).value(NAME, mailbox.getName()).value(NAMESPACE, mailbox.getNamespace()).value(UIDVALIDITY, mailbox.getUidValidity()).value(USER, mailbox.getUser()).value(PATH, path(mailbox).toString()));
+    private void upsertMailbox(CassandraMailbox mailbox) {
+        session.execute(insertInto(TABLE_NAME).value(ID, mailbox.getMailboxId()).value(NAME, mailbox.getName()).value(NAMESPACE, mailbox.getNamespace()).value(UIDVALIDITY, mailbox.getUidValidity()).value(USER, mailbox.getUser()).value(PATH, path(mailbox).toString()).value(ACL, "{}").value(ACL_VERSION, 0));
     }
 
     private MailboxPath path(Mailbox<?> mailbox) {
@@ -120,10 +124,13 @@
 
     @Override
     public boolean hasChildren(Mailbox<UUID> mailbox, char delimiter) {
-        final String regexWithUser = ".*" + mailbox.getNamespace() + ".*" + mailbox.getUser() + ".*" + mailbox.getName() + delimiter + ".*";
-        final String regexWithoutUser = ".*" + mailbox.getNamespace() + ".*null.*" + mailbox.getName() + delimiter + ".*";
-        for (Row row : session.execute(select(PATH).from(TABLE_NAME))) {
-            if (row.getString(PATH).matches(regexWithUser) || row.getString(PATH).matches(regexWithoutUser)) {
+        String user = mailbox.getUser();
+        if(user == null) {
+            user = "null";
+        }
+        final String regex = mailbox.getName() + delimiter + ".*";
+        for (Row row : session.execute(select(NAME).from(TABLE_NAME).allowFiltering().where(eq(NAMESPACE, mailbox.getNamespace())).and(eq(USER, user)))) {
+            if (row.getString(NAME).matches(regex) ) {
                 return true;
             }
         }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java	2015-03-13 15:28:48.000000000 +0100
@@ -19,14 +19,15 @@
 
 package org.apache.james.mailbox.cassandra.mail;
 
-import static com.datastax.driver.core.querybuilder.QueryBuilder.asc;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.decr;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.gt;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.gte;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.incr;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.lt;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.lte;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
@@ -35,11 +36,13 @@
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.BODY_START_OCTET;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.FIELDS;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.FULL_CONTENT_OCTETS;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.Flag.USER_FLAGS;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.HEADER_CONTENT;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.IMAP_UID;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.INTERNAL_DATE;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.MAILBOX_ID;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.MEDIA_TYPE;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.METADATA_FIELDS;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.MOD_SEQ;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.SUB_TYPE;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.TABLE_NAME;
@@ -53,6 +56,7 @@
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.Flag.SEEN;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.Flag.USER;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.FLAG_VERSION;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageTable.UIDS_FIELD;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -66,7 +70,7 @@
 import javax.mail.Flags.Flag;
 import javax.mail.util.SharedByteArrayInputStream;
 
-import com.datastax.driver.core.querybuilder.Update;
+import com.datastax.driver.core.querybuilder.Select;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
 import org.apache.james.mailbox.cassandra.table.CassandraMessageTable;
@@ -104,6 +108,8 @@
  */
 public class CassandraMessageMapper implements MessageMapper<UUID> {
 
+    private static final char UserFlagSeparator = ',';
+    
     private Session session;
     private ModSeqProvider<UUID> modSeqProvider;
     private MailboxSession mailboxSession;
@@ -179,13 +185,30 @@
 
     @Override
     public Iterator<Message<UUID>> findInMailbox(Mailbox<UUID> mailbox, MessageRange set, FetchType ftype, int max) throws MailboxException {
-        Builder<Message<UUID>> result = ImmutableSortedSet.<Message<UUID>> naturalOrder();
-        ResultSet rows = session.execute(buildQuery(mailbox, set));
+        Builder<Message<UUID>> result = ImmutableSortedSet.naturalOrder();
+        ResultSet rows;
+        String[] fields = getCorrespondingFields(ftype);
+        CQLSelectBuilder cqlSelectBuilder = new CQLSelectBuilder(set, fields, mailbox);
+        if( max > 0) {
+            cqlSelectBuilder.setLimit(max);
+        }
+        rows = session.execute(
+                cqlSelectBuilder.build()
+        );
         for (Row row : rows) {
-            result.add(message(row));
+            result.add(message(row, ftype));
         }
         return result.build().iterator();
     }
+    
+    private String[] getCorrespondingFields(FetchType fetchType) {
+        switch (fetchType) {
+            case Metadata:
+                return METADATA_FIELDS;
+            default:
+                return FIELDS;
+        }
+    }
 
     private byte[] getFullContent(Row row) {
         byte[] headerContent = new byte[row.getBytes(HEADER_CONTENT).remaining()];
@@ -202,6 +225,17 @@
                 flags.add(JAVAX_MAIL_FLAG.get(flag));
             }
         }
+        return addCustomFlags(row, flags);
+    }
+
+    private Flags addCustomFlags(Row row, Flags flags) {
+        String userFlagsString = row.getString(CassandraMessageTable.Flag.USER_FLAGS);
+        if(userFlagsString.contains(Character.toString(UserFlagSeparator))) {
+            String[] userFlags = userFlagsString.split(Character.toString(UserFlagSeparator));
+            for (String userFlag : userFlags) {
+                flags.add(userFlag);
+            }
+        }
         return flags;
     }
 
@@ -213,76 +247,132 @@
         return property;
     }
 
-    private Message<UUID> message(Row row) {
-        SimpleMessage<UUID> message = new SimpleMessage<UUID>(row.getDate(INTERNAL_DATE), row.getInt(FULL_CONTENT_OCTETS), row.getInt(BODY_START_OCTET), new SharedByteArrayInputStream(getFullContent(row)), getFlags(row), getPropertyBuilder(row), row.getUUID(MAILBOX_ID));
+    private Message<UUID> message(Row row, FetchType fetchType) {
+        SimpleMessage<UUID> message;
+        if( fetchType == FetchType.Metadata ) {
+            byte[] emptyBuffer = new byte[0];
+            message = new SimpleMessage<UUID>(
+                    row.getDate(INTERNAL_DATE),
+                    row.getInt(FULL_CONTENT_OCTETS),
+                    row.getInt(BODY_START_OCTET),
+                    new SharedByteArrayInputStream(emptyBuffer),
+                    getFlags(row),
+                    getPropertyBuilder(row),
+                    row.getUUID(MAILBOX_ID)
+            );
+        }  else {
+            message = new SimpleMessage<UUID>(
+                    row.getDate(INTERNAL_DATE),
+                    row.getInt(FULL_CONTENT_OCTETS),
+                    row.getInt(BODY_START_OCTET),
+                    new SharedByteArrayInputStream(getFullContent(row)),
+                    getFlags(row),
+                    getPropertyBuilder(row),
+                    row.getUUID(MAILBOX_ID)
+            );
+        }
         message.setUid(row.getLong(IMAP_UID));
+        message.setModSeq(row.getLong(MOD_SEQ));
         return message;
     }
+    
 
-    private Where buildQuery(Mailbox<UUID> mailbox, MessageRange set) {
-        final MessageRange.Type type = set.getType();
-        switch (type) {
-        case ALL:
-            return selectAll(mailbox);
-        case FROM:
-            return selectFrom(mailbox, set.getUidFrom());
-        case RANGE:
-            return selectRange(mailbox, set.getUidFrom(), set.getUidTo());
-        case ONE:
-            return selectMessage(mailbox, set.getUidFrom());
+    private class CQLSelectBuilder {
+        private Boolean filtering;
+        private Integer  limit;
+        private MessageRange set;
+        private String[] fields;
+        private Mailbox<UUID> mailbox;
+
+        public CQLSelectBuilder(MessageRange set, String[] fields, Mailbox<UUID> mailbox) {
+            this.set = set;
+            this.fields = fields;
+            this.mailbox = mailbox;
+        }
+
+        public CQLSelectBuilder allowFiltering() {
+            this.filtering = true;
+            return this;
+        }
+
+        public CQLSelectBuilder setLimit(Integer limit) {
+            this.limit = limit;
+            return this;
+        }
+        
+        private Where build() {
+            Select selectStatement = select(fields).from(TABLE_NAME);
+            if(filtering != null && filtering) {
+                selectStatement = selectStatement.allowFiltering();
+            }
+            if(limit != null && limit > 0) {
+                selectStatement = selectStatement.limit(limit);
+            }
+            return restrictToRange(restrictToMailbox(selectStatement));
+        }
+        
+        private Where restrictToMailbox(Select selectStatement) {
+            return selectStatement.where(eq(MAILBOX_ID, mailbox.getMailboxId()));
+        }
+        
+        private Where restrictToRange(Where statement) {
+            final MessageRange.Type type = set.getType();
+            switch (type) {
+                case ALL:
+                    return statement;
+                case FROM:
+                    return statement.and(gte(IMAP_UID, set.getUidFrom()));
+                case RANGE:
+                    return statement.and(gte(IMAP_UID, set.getUidFrom())).and(lte(IMAP_UID, set.getUidTo()));
+                case ONE:
+                    return statement.and(eq(IMAP_UID, set.getUidFrom()));
+            }
+            throw new UnsupportedOperationException();
         }
-        throw new UnsupportedOperationException();
-    }
-
-    private Where selectAll(Mailbox<UUID> mailbox) {
-        return select(FIELDS).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId()));
-    }
-
-    private Where selectFrom(Mailbox<UUID> mailbox, long uid) {
-        return select(FIELDS).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId())).and(gt(IMAP_UID, uid));
-    }
-
-    private Where selectRange(Mailbox<UUID> mailbox, long from, long to) {
-        return select(FIELDS).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId())).and(gt(IMAP_UID, from)).and(lt(IMAP_UID, to));
-    }
-
-    private Where selectMessage(Mailbox<UUID> mailbox, long uid) {
-        return select(FIELDS).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId())).and(eq(IMAP_UID, uid));
     }
-
+    
     @Override
     public List<Long> findRecentMessageUidsInMailbox(Mailbox<UUID> mailbox) throws MailboxException {
-        ImmutableList.Builder<Long> result = ImmutableList.<Long> builder();
-        ResultSet rows = session.execute(selectAll(mailbox).orderBy(asc(IMAP_UID)));
+        ImmutableList.Builder<Long> result = ImmutableList.builder();
+        ResultSet rows = session.execute(
+             new CQLSelectBuilder(MessageRange.all(), UIDS_FIELD, mailbox).allowFiltering().build().and(eq(RECENT, true))
+        );
         for (Row row : rows) {
-            if (row.getBool(RECENT)) {
-                result.add(row.getLong(IMAP_UID));
-            }
+            result.add(row.getLong(IMAP_UID));
         }
         return result.build();
     }
 
     @Override
     public Long findFirstUnseenMessageUid(Mailbox<UUID> mailbox) throws MailboxException {
-        ResultSet rows = session.execute(selectAll(mailbox).orderBy(asc(IMAP_UID)));
+        ResultSet rows = session.execute(
+              new CQLSelectBuilder(MessageRange.all(), UIDS_FIELD, mailbox).allowFiltering().build().and(eq(SEEN, false))
+        );
+        long firstUnseenMessage = -1;
+        long uid;
         for (Row row : rows) {
-            if (!row.getBool(SEEN)) {
-                return row.getLong(IMAP_UID);
+            uid = row.getLong(IMAP_UID);
+            if(uid < firstUnseenMessage || firstUnseenMessage == -1) {
+                firstUnseenMessage = uid;
             }
         }
-        return null;
+        if(firstUnseenMessage == -1) {
+            return null;
+        } else {
+            return firstUnseenMessage;
+        }
     }
 
     @Override
     public Map<Long, MessageMetaData> expungeMarkedForDeletionInMailbox(final Mailbox<UUID> mailbox, MessageRange set) throws MailboxException {
         ImmutableMap.Builder<Long, MessageMetaData> deletedMessages = ImmutableMap.builder();
-        ResultSet messages = session.execute(buildQuery(mailbox, set));
+        ResultSet messages = session.execute(
+                new CQLSelectBuilder(MessageRange.all(), METADATA_FIELDS, mailbox).allowFiltering().build().and(eq(DELETED, true))
+        );
         for (Row row : messages) {
-            if (row.getBool(DELETED)) {
-                Message<UUID> message = message(row);
-                delete(mailbox, message);
-                deletedMessages.put(message.getUid(), new SimpleMessageMetaData(message));
-            }
+            Message<UUID> message = message(row, FetchType.Metadata);
+            delete(mailbox, message);
+            deletedMessages.put(message.getUid(), new SimpleMessageMetaData(message));
         }
         return deletedMessages.build();
     }
@@ -323,7 +413,7 @@
         try {
             Insert query = insertInto(TABLE_NAME).value(MAILBOX_ID, mailbox.getMailboxId()).value(IMAP_UID, message.getUid()).value(MOD_SEQ, message.getModSeq()).value(INTERNAL_DATE, message.getInternalDate()).value(MEDIA_TYPE, message.getMediaType())
                     .value(BODY_START_OCTET, message.getFullContentOctets() - message.getBodyOctets()).value(SUB_TYPE, message.getSubType()).value(FULL_CONTENT_OCTETS, message.getFullContentOctets()).value(BODY_OCTECTS, message.getBodyOctets()).value(ANSWERED, message.isAnswered())
-                    .value(DELETED, message.isDeleted()).value(DRAFT, message.isDraft()).value(FLAGGED, message.isFlagged()).value(RECENT, message.isRecent()).value(SEEN, message.isSeen()).value(USER, message.createFlags().contains(Flag.USER)).value(BODY_CONTENT, bindMarker())
+                    .value(DELETED, message.isDeleted()).value(DRAFT, message.isDraft()).value(FLAGGED, message.isFlagged()).value(RECENT, message.isRecent()).value(SEEN, message.isSeen()).value(USER, message.createFlags().contains(Flag.USER)).value(USER_FLAGS, getUserFlagsString(message.createFlags())).value(BODY_CONTENT, bindMarker())
                     .value(HEADER_CONTENT, bindMarker()).value(TEXTUAL_LINE_COUNT, message.getTextualLineCount()).value(FLAG_VERSION, 0);
             PreparedStatement preparedStatement = session.prepare(query.toString());
             BoundStatement boundStatement = preparedStatement.bind(toByteBuffer(message.getBodyContent()), toByteBuffer(message.getHeaderContent()));
@@ -334,7 +424,7 @@
         }
     }
 
-    private boolean conditionalSave(Mailbox<UUID> mailbox, Message<UUID> message, long flagVersion) throws MailboxException {
+    private boolean conditionalSave(Message<UUID> message, long flagVersion) throws MailboxException {
         ResultSet resultSet = session.execute(
                 update(TABLE_NAME)
                         .with(set(ANSWERED, message.isAnswered()))
@@ -344,13 +434,33 @@
                         .and(set(RECENT, message.isRecent()))
                         .and(set(SEEN, message.isSeen()))
                         .and(set(USER, message.createFlags().contains(Flag.USER)))
+                        .and(set(USER_FLAGS, getUserFlagsString(message.createFlags())))
                         .and(set(FLAG_VERSION, flagVersion + 1))
+                        .and(set(MOD_SEQ, message.getModSeq()))
                         .where(eq(IMAP_UID, message.getUid()))
                         .and(eq(MAILBOX_ID, message.getMailboxId()))
                         .onlyIf(eq(FLAG_VERSION, flagVersion))
         );
         return resultSet.one().getBool(applied);
     }
+    
+    private String getUserFlagsString(Flags flags) {
+        String[] userFlags = flags.getUserFlags();
+        if(userFlags.length == 0) {
+            return "";
+        } else if (userFlags.length == 1) {
+            return userFlags[0];
+        } else {
+            StringBuilder flagsStringBuilder = new StringBuilder();
+            for(int i = 0; i < userFlags.length; i++) {
+                flagsStringBuilder.append(userFlags[i]);
+                if(i < userFlags.length - 1) {
+                    flagsStringBuilder.append(UserFlagSeparator);
+                }
+            }
+            return flagsStringBuilder.toString();
+        }
+    }
 
     private ByteBuffer toByteBuffer(InputStream stream) throws IOException {
         return ByteBuffer.wrap(ByteStreams.toByteArray(stream));
@@ -369,7 +479,7 @@
     @Override
     public Iterator<UpdatedFlags> updateFlags(Mailbox<UUID> mailbox, Flags flags, boolean value, boolean replace, MessageRange set) throws MailboxException {
         ImmutableList.Builder<UpdatedFlags> result = ImmutableList.builder();
-        for (Row row : session.execute(buildQuery(mailbox, set))) {
+        for (Row row : session.execute(new CQLSelectBuilder(set, METADATA_FIELDS, mailbox).build() ) ) {
             updateMessage(mailbox, flags, value, replace, result, row);
         }
         return result.build().iterator();
@@ -377,7 +487,7 @@
 
     private void updateMessage(Mailbox<UUID> mailbox, Flags flags, boolean value, boolean replace, ImmutableList.Builder<UpdatedFlags> result, Row row) throws MailboxException {
         // Get the message and basic information about it
-        Message<UUID> message = message(row);
+        Message<UUID> message = message(row, FetchType.Metadata);
         long flagVersion = row.getLong(FLAG_VERSION);
         long uid = message.getUid();
         // update flags
@@ -389,7 +499,7 @@
         long modSeq = modSeqProvider.nextModSeq(mailboxSession, mailbox);
         message.setModSeq(modSeq);
         // Try a first update
-        if(!conditionalSave(mailbox, message, flagVersion)) {
+        if(!conditionalSave(message, flagVersion)) {
             int tries = 0;
             // It fails. Someone updated the flag before us.
             do {
@@ -401,7 +511,7 @@
                     // Skip it
                     break;
                 }
-                message = message(newRow);
+                message = message(newRow, FetchType.Metadata);
                 flagVersion = newRow.getLong(FLAG_VERSION);
                 // update flags
                 originFlags = message.createFlags();
@@ -415,16 +525,29 @@
                 }
                 message.setModSeq(modSeq);
                 // and retry
-            } while (!conditionalSave(mailbox, message, flagVersion) && tries < maxRetries);
+            } while (!conditionalSave(message, flagVersion) && tries < maxRetries);
             if(tries == maxRetries) {
                 throw new MailboxException("Max retries reached when asking an update of flags on message " + uid + " for mailbox " + mailbox.getMailboxId());
             }
         }
         result.add(new UpdatedFlags(message.getUid(), message.getModSeq(), originFlags, updatedFlags));
+        updateSeenCounterUponUpdateFlags(mailbox, originFlags, updatedFlags);
     }
 
+    private void updateSeenCounterUponUpdateFlags(Mailbox<UUID> mailbox, Flags originFlags, Flags updatedFlags) {
+        if(originFlags.contains(Flag.SEEN) && !updatedFlags.contains(Flag.SEEN) ) {
+            incrementUnseen(mailbox);
+        }
+        if(!originFlags.contains(Flag.SEEN) && updatedFlags.contains(Flag.SEEN)) {
+            decrementUnseen(mailbox);
+        }
+    }
+
+
     private Row findMessageByUid(Mailbox<UUID> mailbox, long uid) {
-        ResultSet resultSet = session.execute(selectMessage(mailbox, uid));
+        ResultSet resultSet = session.execute(
+                new CQLSelectBuilder(MessageRange.one(uid), METADATA_FIELDS, mailbox).build()
+        );
         if ( resultSet.isExhausted() ) {
             return null;
         }
@@ -449,6 +572,10 @@
     public MessageMetaData copy(Mailbox<UUID> mailbox, Message<UUID> original) throws MailboxException {
         original.setUid(uidProvider.nextUid(mailboxSession, mailbox));
         original.setModSeq(modSeqProvider.nextModSeq(mailboxSession, mailbox));
+        incrementCount(mailbox);
+        if(!original.isSeen()) {
+            incrementUnseen(mailbox);
+        }
         return save(mailbox, original);
     }
 
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java	2015-03-09 10:46:43.204296226 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java	2015-03-09 11:25:46.000000000 +0100
@@ -20,12 +20,14 @@
 package org.apache.james.mailbox.cassandra.mail;
 
 import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.incr;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
 import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.MAILBOX_ID;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.NEXT_MOD_SEQ;
-import static org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable.TABLE_NAME;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageModseqTable.NEXT_MODSEQ;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageModseqTable.TABLE_NAME;
+import static org.apache.james.mailbox.cassandra.table.CassandraMessageModseqTable.MAILBOX_ID;
 
 import java.util.UUID;
 
@@ -39,21 +41,48 @@
 
 public class CassandraModSeqProvider implements ModSeqProvider<UUID> {
 
+    private static final int DEFAULT_MAX_RETRY = 100000;
+
+
     private Session session;
+    private final int applied = 0;
+    private int maxRetry;
 
-    public CassandraModSeqProvider(Session session) {
+    public CassandraModSeqProvider(Session session, int maxRetry) {
         this.session = session;
+        this.maxRetry = maxRetry;
+    }
+
+    public CassandraModSeqProvider(Session session) {
+        this(session, DEFAULT_MAX_RETRY);
     }
 
     @Override
     public long nextModSeq(MailboxSession mailboxSession, Mailbox<UUID> mailbox) throws MailboxException {
-        session.execute(update(TABLE_NAME).with(incr(NEXT_MOD_SEQ)).where(eq(MAILBOX_ID, mailbox.getMailboxId())));
-        return highestModSeq(mailboxSession, mailbox);
+        long highestModSeq = highestModSeq(mailboxSession, mailbox);
+        if (highestModSeq == 0) {
+            ResultSet result = session.execute(insertInto(TABLE_NAME).value(NEXT_MODSEQ, highestModSeq+1).value(MAILBOX_ID, mailbox.getMailboxId()).ifNotExists());
+            if(result.one().getBool(applied)) {
+                return highestModSeq+1;
+            }
+        }
+        int tries = 0;
+        boolean isApplied;
+        do {
+            tries++;
+            highestModSeq = highestModSeq(mailboxSession, mailbox);
+            ResultSet result = session.execute(update(TABLE_NAME).onlyIf(eq(NEXT_MODSEQ, highestModSeq)).with(set(NEXT_MODSEQ, highestModSeq+1)).where(eq(MAILBOX_ID, mailbox.getMailboxId())));
+            isApplied = result.one().getBool(applied);
+        } while (! isApplied && tries < maxRetry);
+        if( ! isApplied ) {
+            throw new MailboxException("Can not obtain rights to manage UID");
+        }
+        return highestModSeq+1;
     }
 
     @Override
     public long highestModSeq(MailboxSession mailboxSession, Mailbox<UUID> mailbox) throws MailboxException {
-        ResultSet result = session.execute(select(NEXT_MOD_SEQ).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId())));
-        return result.isExhausted() ? 0 : result.one().getLong(NEXT_MOD_SEQ);
+        ResultSet result = session.execute(select(NEXT_MODSEQ).from(TABLE_NAME).where(eq(MAILBOX_ID, mailbox.getMailboxId())));
+        return result.isExhausted() ? 0 : result.one().getLong(NEXT_MODSEQ);
     }
 }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java	2015-03-09 10:46:43.256296483 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java	2015-03-09 11:25:46.000000000 +0100
@@ -19,11 +19,7 @@
 
 package org.apache.james.mailbox.cassandra.mail;
 
-import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.*;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageUidTable.MAILBOX_ID;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageUidTable.NEXT_UID;
 import static org.apache.james.mailbox.cassandra.table.CassandraMessageUidTable.TABLE_NAME;
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/CassandraMailbox.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/CassandraMailbox.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/CassandraMailbox.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/CassandraMailbox.java	2015-03-09 11:25:46.000000000 +0100
@@ -0,0 +1,179 @@
+/****************************************************************
+ * 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.model;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxTable;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.SimpleMailboxACL;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
+
+
+/**
+ * Mailbox implementation for Cassandra. It overrides SimpleMailbox class, adding ACL support
+ * ( stored in Cassandra ) and expose specific methods to allow check-and-set ACL management
+ * in  CassandraMessageManager
+ */
+public class CassandraMailbox<Id> extends SimpleMailbox<Id> {
+
+    private Session session;
+
+    private static final Logger LOG = LoggerFactory.getLogger(CassandraMailbox.class);
+
+    public CassandraMailbox(MailboxPath path, long uidValidity, Session session) {
+        super(path, uidValidity);
+        this.session = session;
+    }
+
+    @Override
+    public MailboxACL getACL() {
+        // Get the ACL string from Cassandra
+        ResultSet resultSet = session.execute(
+                select(CassandraMailboxTable.ACL)
+                        .from(CassandraMailboxTable.TABLE_NAME)
+                        .where(
+                                eq(CassandraMailboxTable.ID, getMailboxId())
+                        )
+        );
+        if(resultSet.isExhausted()) {
+            // No mailbox. Should not happen : return null
+            return null;
+        }
+        String aclJson = resultSet.one().getString(CassandraMailboxTable.ACL);
+        return convertIntoMailboxACL(aclJson);
+    }
+
+    private MailboxACL convertIntoMailboxACL(String aclJson) {
+        MailboxACL result = SimpleMailboxACL.EMPTY;
+        try {
+            result = SimpleMailboxACLJsonConverter.toACL(aclJson);
+        } catch (JsonParseException parseException) {
+            LOG.error(aclJson + " provided as ACL is malformed for mailbox " + this.getMailboxId());
+        } catch ( JsonMappingException mappingException) {
+            LOG.error(aclJson + " provided as ACL does not correspond to an ACL for mailbox " + this.getMailboxId());
+        } catch( IOException ioException) {
+            LOG.error("IOException while parsing JSON for mailbox " + this.getMailboxId());
+        }
+        return result;
+    }
+
+    public class ACLWithVersion {
+        public long version;
+        public MailboxACL acl;
+    }
+
+    /**
+     * Get the ACL and the version number.
+     *
+     * @return The ACL of this mailbox with its version number
+     */
+    public ACLWithVersion getACLWithVersion() {
+        ACLWithVersion result = new ACLWithVersion();
+        ResultSet resultSet = session.execute(
+                select(CassandraMailboxTable.FIELDS)
+                        .from(CassandraMailboxTable.TABLE_NAME)
+                        .where(
+                                eq(CassandraMailboxTable.ID, getMailboxId())
+                        )
+        );
+        if(resultSet.isExhausted()) {
+            result.version = 0;
+            result.acl = SimpleMailboxACL.EMPTY;
+            return result;
+        }
+        Row row = resultSet.one();
+        String aclJson = row.getString(CassandraMailboxTable.ACL);
+        result.version = row.getLong(CassandraMailboxTable.ACL_VERSION);
+        result.acl = convertIntoMailboxACL(aclJson);
+        return result;
+    }
+
+    /**
+     * Not concurrency safe AT ALL ! Do not use !
+     * @param acl The mailbox ACL to set
+     */
+    @Override
+    public void setACL(MailboxACL acl) {
+        String jsonACL;
+        try {
+            jsonACL = SimpleMailboxACLJsonConverter.toJson(acl);
+        } catch(JsonProcessingException processingException) {
+            LOG.error("Can not set acl for mailbox " + this.getMailboxId());
+            return;
+        }
+        session.execute(
+                update(CassandraMailboxTable.TABLE_NAME)
+                        .with(
+                                set(CassandraMailboxTable.ACL, jsonACL)
+                        )
+                        .where(eq(CassandraMailboxTable.ID, getMailboxId()))
+        );
+    }
+
+    /**
+     * Set ACL implementation for Cassandra using test and set CQL instruction
+     *
+     * @param acl Mailbox ACLs to set
+     * @return True if it succeeded, False if the version of the ACL is not the one specified in ACL version
+     */
+    public boolean setACLWithVersion(MailboxACL acl, long version ) {
+        long newVersionNumber = version + 1;
+        String jsonACL;
+        try {
+            jsonACL = SimpleMailboxACLJsonConverter.toJson(acl);
+        } catch(JsonProcessingException processingException) {
+            LOG.error("Can not set acl for mailbox " + this.getMailboxId());
+            return true;
+        }
+        ResultSet resultSet = session.execute(
+                update(CassandraMailboxTable.TABLE_NAME)
+                        .with(
+                                set(CassandraMailboxTable.ACL, jsonACL)
+                        )
+                        .and(
+                                set(CassandraMailboxTable.ACL_VERSION, newVersionNumber)
+                        )
+                        .where(
+                                eq(CassandraMailboxTable.ID, getMailboxId())
+                        )
+                        .onlyIf(
+                                eq(CassandraMailboxTable.ACL_VERSION, version)
+                        )
+
+        );
+        return resultSet.one().getBool("[applied]");
+    }
+
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/SimpleMailboxACLJsonConverter.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/SimpleMailboxACLJsonConverter.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/SimpleMailboxACLJsonConverter.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/model/SimpleMailboxACLJsonConverter.java	2015-03-09 11:25:46.000000000 +0100
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.model;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.james.mailbox.exception.UnsupportedRightException;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.SimpleMailboxACL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Convert the ACL in Json and convert Json back to ACL
+ */
+public class SimpleMailboxACLJsonConverter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SimpleMailboxACLJsonConverter.class);
+
+    /**
+     * Convert an ACL to JSON in order to store it in Cassandra
+     *
+     * @param acl ACL you want to Jsonify
+     * @return A Json representing your ACL
+     */
+    public static String toJson(MailboxACL acl) throws JsonProcessingException {
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> entries = acl.getEntries();
+        Set<Map.Entry<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>> set = entries.entrySet();
+        Map<String,Integer> convertedEntries = new HashMap<String, Integer>();
+        for(Map.Entry<MailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> entry : set) {
+            SimpleMailboxACL.Rfc4314Rights rights;
+            try{
+                rights = (SimpleMailboxACL.Rfc4314Rights) entry.getValue();
+            } catch(ClassCastException classCastException) {
+                LOG.warn("Cast Exception : you need to provide a Rfc4314Rights object when instantiating ACLs");
+                break;
+            }
+            convertedEntries.put(entry.getKey().serialize(), rights.getValue());
+        }
+        ObjectMapper mapper = new ObjectMapper();
+        return mapper.writeValueAsString(convertedEntries);
+    }
+
+    /**
+     * Get back your ACL from a JSON string
+     *
+     * A malformed Json is equivalent to an empty ACL
+     *
+     * @param jsonACLString Json string representing your ACL
+     * @return The ACL represented by your Json
+     */
+    public static MailboxACL toACL(String jsonACLString) throws JsonParseException, JsonMappingException, IOException {
+        ObjectMapper mapper = new ObjectMapper();
+        Map<String,Integer> convertedEntries = mapper.readValue(jsonACLString,
+                new TypeReference<Map<String, Integer>>() { } );
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> entries = new HashMap<MailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        Set<Map.Entry<String, Integer>> convertedEntrySet = convertedEntries.entrySet();
+        for(Map.Entry<String, Integer> entry : convertedEntrySet) {
+            SimpleMailboxACL.Rfc4314Rights rights;
+            try {
+                rights = new SimpleMailboxACL.Rfc4314Rights(entry.getValue());
+            } catch(UnsupportedRightException rightException) {
+                LOG.warn("Integer provided as right is out of range, causing an unsupported right exception");
+                break;
+            }
+            MailboxACL.MailboxACLEntryKey key = new SimpleMailboxACL.SimpleMailboxACLEntryKey(entry.getKey());
+            entries.put(key, rights);
+        }
+        return new SimpleMailboxACL(entries);
+    }
+
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraDefaultMaxQuotaTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraDefaultMaxQuotaTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraDefaultMaxQuotaTable.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraDefaultMaxQuotaTable.java	2015-03-09 11:25:46.000000000 +0100
@@ -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 CassandraDefaultMaxQuotaTable {
+    String DEFAULT_MAX_QUOTA_TABLE = "default_max_quota_table";
+    String ID = "id";
+    String DEFAULT_MAX_SIZE_QUOTA = "default_max_size_quota";
+    String DEFAULT_MAX_COUNT_QUOTA = "default_max_count_quota";
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraGroupMembershipTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraGroupMembershipTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraGroupMembershipTable.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraGroupMembershipTable.java	2015-03-09 11:25:46.000000000 +0100
@@ -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 CassandraGroupMembershipTable {
+    String TABLE = "group_membership";
+    String GROUP = "group";
+    String USER = "user";
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxCountersTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxCountersTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxCountersTable.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxCountersTable.java	2015-03-09 11:11:22.000000000 +0100
@@ -24,5 +24,4 @@
     String MAILBOX_ID = "mailboxId";
     String COUNT = "count";
     String UNSEEN = "unseen";
-    String NEXT_MOD_SEQ = "nextModSeq";
 }
\ Pas de fin de ligne à la fin du fichier
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxTable.java	2015-03-09 10:46:43.204296226 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxTable.java	2015-03-09 11:25:46.000000000 +0100
@@ -27,5 +27,7 @@
     String NAMESPACE = "namespace";
     String UIDVALIDITY = "uidvalidity";
     String NAME = "name";
-    String[] FIELDS = { ID, USER, NAMESPACE, UIDVALIDITY, NAME, PATH };
+    String ACL = "acl";
+    String ACL_VERSION = "acl_version";
+    String[] FIELDS = { ID, USER, NAMESPACE, UIDVALIDITY, NAME, PATH, ACL, ACL_VERSION };
 }
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMaxQuotaTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMaxQuotaTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMaxQuotaTable.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMaxQuotaTable.java	2015-03-09 11:25:46.000000000 +0100
@@ -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 CassandraMaxQuotaTable {
+    String TABLE_NAME = "quota_max";
+    String USER = "user";
+    String SIZE_QUOTA_MAX = "size_quota_max";
+    String COUNT_QUOTA_MAX = "count_quota_max";
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageModseqTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageModseqTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageModseqTable.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageModseqTable.java	2015-03-09 11:11:22.000000000 +0100
@@ -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 CassandraMessageModseqTable {
+    String TABLE_NAME = "modseq";
+    String MAILBOX_ID = "mailboxId";
+    String NEXT_MODSEQ = "nextModseq";
+}
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageTable.java	2015-03-09 10:46:43.256296483 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMessageTable.java	2015-03-13 15:28:48.000000000 +0100
@@ -29,7 +29,6 @@
     String IMAP_UID = "uid";
     String INTERNAL_DATE = "internalDate";
     String BODY_START_OCTET = "bodyStartOctet";
-    String CONTENT = "content";
     String MOD_SEQ = "modSeq";
     String MEDIA_TYPE = "mediaType";
     String SUB_TYPE = "subType";
@@ -39,8 +38,12 @@
     String BODY_CONTENT = "bodyContent";
     String HEADER_CONTENT = "headerContent";
     String FLAG_VERSION = "flagVersion";
-    String[] FIELDS = { MAILBOX_ID, IMAP_UID, INTERNAL_DATE, MOD_SEQ, BODY_START_OCTET, MEDIA_TYPE, SUB_TYPE, FULL_CONTENT_OCTETS, BODY_OCTECTS, Flag.ANSWERED, Flag.DELETED, Flag.DRAFT, Flag.FLAGGED, Flag.RECENT, Flag.SEEN, Flag.USER, BODY_CONTENT, HEADER_CONTENT, TEXTUAL_LINE_COUNT, FLAG_VERSION };
+    String[] FIELDS = { MAILBOX_ID, IMAP_UID, INTERNAL_DATE, MOD_SEQ, BODY_START_OCTET, MEDIA_TYPE, SUB_TYPE, FULL_CONTENT_OCTETS, BODY_OCTECTS, Flag.ANSWERED, Flag.DELETED, Flag.DRAFT, Flag.FLAGGED, Flag.RECENT, Flag.SEEN, Flag.USER, Flag.USER_FLAGS, BODY_CONTENT, HEADER_CONTENT, TEXTUAL_LINE_COUNT, FLAG_VERSION };
 
+    String[] METADATA_FIELDS = { MAILBOX_ID, IMAP_UID, INTERNAL_DATE, MOD_SEQ, BODY_START_OCTET, MEDIA_TYPE, SUB_TYPE, FULL_CONTENT_OCTETS, BODY_OCTECTS, Flag.ANSWERED, Flag.DELETED, Flag.DRAFT, Flag.FLAGGED, Flag.RECENT, Flag.SEEN, Flag.USER, Flag.USER_FLAGS, TEXTUAL_LINE_COUNT, FLAG_VERSION };
+
+    String[] UIDS_FIELD = { IMAP_UID };
+    
     interface Flag {
         String ANSWERED = "flagAnswered";
         String DELETED = "flagDeleted";
@@ -49,6 +52,7 @@
         String SEEN = "flagSeen";
         String FLAGGED = "flagFlagged";
         String USER = "flagUser";
+        String USER_FLAGS = "userFlags";
         String[] ALL = { ANSWERED, DELETED, DRAFT, RECENT, SEEN, FLAGGED, USER };
 
         ImmutableMap<String, javax.mail.Flags.Flag> JAVAX_MAIL_FLAG = new Builder<String, javax.mail.Flags.Flag>().put(ANSWERED, javax.mail.Flags.Flag.ANSWERED).put(DELETED, javax.mail.Flags.Flag.DELETED).put(DRAFT, javax.mail.Flags.Flag.DRAFT).put(RECENT, javax.mail.Flags.Flag.RECENT)
diff -uNr james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraQuotaTable.java james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraQuotaTable.java
--- james-mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraQuotaTable.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraQuotaTable.java	2015-03-09 11:25:46.000000000 +0100
@@ -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 CassandraQuotaTable {
+    String TABLE_NAME = "quota";
+    String USER = "user";
+    String SIZE_QUOTA = "size_quota";
+    String COUNT_QUOTA = "count_quota";
+}
diff -uNr james-mailbox/cassandra/src/main/resources/META-INF/spring/mailbox-cassandra.xml james-mailbox-new/cassandra/src/main/resources/META-INF/spring/mailbox-cassandra.xml
--- james-mailbox/cassandra/src/main/resources/META-INF/spring/mailbox-cassandra.xml	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/main/resources/META-INF/spring/mailbox-cassandra.xml	2015-03-13 15:28:48.000000000 +0100
@@ -23,6 +23,8 @@
        xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 
+    <import resource="classpath:META-INF/spring/mailbox-index-elasticsearch.xml"/>
+
     <!-- 
       Mailbox Cassandra
     -->
@@ -30,6 +32,22 @@
         <constructor-arg index="0" ref="cassandra-sessionMapperFactory"/>
         <constructor-arg index="1" ref="authenticator"/>
         <constructor-arg index="2" ref="cassandra-locker"/>
+
+        <!--
+            You may not want to use the limited default MessageSearchIndex. Instead we can use a fully featured ElasticSearch MessageSearch Index.
+            See elasticsearch/src/main/resources/mailbox-index-elasticsearch.xml for possible values.
+
+            Uncomment the following line if you have a valid ElasticSearch cluster
+        -->
+
+
+        <property name="messageSearchIndex" ref="elasticsearch-direct-index"/>
+        
+
+        <property name="copyBatchSize" value="20"/>
+        <property name="moveBatchSize" value="20"/>
+        <property name="fetchBatchSize" value="20"/>
+
     </bean>
 
     <bean id ="cassandra-subscriptionManager" class="org.apache.james.mailbox.cassandra.CassandraSubscriptionManager">
@@ -44,14 +62,16 @@
 
     <bean id="cassandra-uidProvider" class="org.apache.james.mailbox.cassandra.mail.CassandraUidProvider">
         <constructor-arg index="0" ref="cassandra-session"/>
+        <constructor-arg index="1" value="10000000"/>
     </bean>
 
     <bean id="cassandra-modSeqProvider" class="org.apache.james.mailbox.cassandra.mail.CassandraModSeqProvider">
         <constructor-arg index="0" ref="cassandra-session"/>
+        <constructor-arg index="1" value="10000000"/>
     </bean>
 
     <!--
-      The parameters are : the IP of a Cassendra cluster, the port, the keyspace and the replication factor
+      The parameters are : the IP of a Cassandra cluster, the port, the keyspace and the replication factor
       Default values are : localhost, 9042, apache_james and 1
     -->
     <bean id="cassandra-session" class="org.apache.james.mailbox.cassandra.CassandraSession">
@@ -61,6 +81,11 @@
         <constructor-arg index="3" value="1" type="int"/>
     </bean>
 
-    <alias name="jvm-locker" alias="cassandra-locker"/>
+    <bean id="cassandra-groupMembershipResolver" class="org.apache.james.mailbox.cassandra.CassandraGroupMembershipResolver">
+        <constructor-arg index="0" ref="cassandra-session"/>
+    </bean>
+
+
+    <alias name="no-locker" alias="cassandra-locker"/>
 
 </beans>
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraClusterSingleton.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraClusterSingleton.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraClusterSingleton.java	2015-03-09 10:46:43.204296226 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraClusterSingleton.java	2015-03-13 15:28:48.000000000 +0100
@@ -33,6 +33,8 @@
     private final static int CLUSTER_PORT_TEST = 9142;
     private final static String KEYSPACE_NAME = "apache_james";
     private final static int DEFAULT_REPLICATION_FACTOR = 1;
+    private static final long sleep_before_retry = 200;
+    private static final int max_retry = 200;
 
     private static final Logger LOG = LoggerFactory.getLogger(CassandraClusterSingleton.class);
     private static CassandraClusterSingleton cluster = null;
@@ -40,7 +42,7 @@
 
     /**
      * Builds a MiniCluster instance.
-     * 
+     *
      * @return the {@link CassandraClusterSingleton} instance
      * @throws RuntimeException
      */
@@ -53,20 +55,35 @@
     }
 
     private CassandraClusterSingleton() throws RuntimeException {
-       try {
+        boolean initialized = false;
+        int tries = 0;
+        try {
             EmbeddedCassandraServerHelper.startEmbeddedCassandra();
-            // Let Cassandra initialization before creating
-            // the session. Solve very fast computer tests run.
-            Thread.sleep(2000);
-            this.session = new CassandraSession(CLUSTER_IP, CLUSTER_PORT_TEST, KEYSPACE_NAME, DEFAULT_REPLICATION_FACTOR);
         } catch (Exception e) {
+            e.printStackTrace();
             throw new RuntimeException(e);
         }
+        while(!initialized && tries < max_retry) {
+            tries ++;
+            try {
+                this.session = new CassandraSession(CLUSTER_IP, CLUSTER_PORT_TEST, KEYSPACE_NAME, DEFAULT_REPLICATION_FACTOR);
+                initialized = true;
+            } catch (Exception e) {
+                if(tries == max_retry) {
+                    throw new RuntimeException(e);
+                }
+                try {
+                    Thread.sleep(sleep_before_retry);
+                } catch(InterruptedException interruptedException) {
+                    LOG.warn("Interrupted while waiting for Cassandra cluster to be available");
+                }
+            }
+        }
     }
 
     /**
      * Return a configuration for the runnning MiniCluster.
-     * 
+     *
      * @return
      */
     public Session getConf() {
@@ -75,25 +92,57 @@
 
     /**
      * Create a specific table.
-     * 
+     *
      * @param tableName
      *            the table name
      */
     public void ensureTable(String tableName) {
         if (tableName.equals("mailbox")) {
-            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".mailbox (" + "id uuid PRIMARY KEY," + "name text, namespace text," + "uidvalidity bigint," + "user text," + "path text" + ");");
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".mailbox (" + "id uuid PRIMARY KEY," + "name text, namespace text," + "uidvalidity bigint," + "user text," + "path text," + "acl text," + "acl_version bigint" + ");");
 
-            session.execute("CREATE INDEX IF NOT EXISTS ON " + session.getLoggedKeyspace() + ".mailbox(path);");
+            session.execute("CREATE INDEX IF NOT EXISTS path_index ON " + session.getLoggedKeyspace() + ".mailbox(path);");
+            session.execute("CREATE INDEX IF NOT EXISTS namespace_index ON " + session.getLoggedKeyspace() + ".mailbox(namespace);");
+            session.execute("CREATE INDEX IF NOT EXISTS user_index ON " + session.getLoggedKeyspace() + ".mailbox(user);");
+            session.execute("CREATE INDEX IF NOT EXISTS name_index ON " + session.getLoggedKeyspace() + ".mailbox(name);");
         } else if (tableName.equals("messageCounter")) {
             session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".messageCounter (" + "mailboxId UUID PRIMARY KEY," + "nextUid bigint," + ");");
         } else if (tableName.equals("mailboxCounters")) {
-            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".mailboxCounters (" + "mailboxId UUID PRIMARY KEY," + "count counter," + "unseen counter," + "nextModSeq counter" + ");");
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".mailboxCounters (" + "mailboxId UUID PRIMARY KEY," + "count counter," + "unseen counter," + ");");
         } else if (tableName.equals("message")) {
             session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".message (" + "mailboxId UUID," + "uid bigint," + "internalDate timestamp," + "bodyStartOctet int," + "content blob," + "modSeq bigint," + "mediaType text," + "subType text," + "fullContentOctets int,"
-                    + "bodyOctets int," + "textualLineCount bigint," + "bodyContent blob," + "headerContent blob," + "flagAnswered boolean," + "flagDeleted boolean," + "flagDraft boolean," + "flagRecent boolean," + "flagSeen boolean," + "flagFlagged boolean," + "flagUser boolean,"
+                    + "bodyOctets int," + "textualLineCount bigint," + "bodyContent blob," + "headerContent blob," + "flagAnswered boolean," + "flagDeleted boolean," + "flagDraft boolean," + "flagRecent boolean," + "flagSeen boolean," + "flagFlagged boolean," + "flagUser boolean," + "userFlags text,"
                     + "flagVersion bigint,"+ "PRIMARY KEY (mailboxId, uid)" + ");");
+            session.execute("CREATE INDEX IF NOT EXISTS ON " + session.getLoggedKeyspace() + ".message(flagRecent);");
+            session.execute("CREATE INDEX IF NOT EXISTS ON " + session.getLoggedKeyspace() + ".message(flagSeen);");
+            session.execute("CREATE INDEX IF NOT EXISTS ON " + session.getLoggedKeyspace() + ".message(flagDeleted);");
         } else if (tableName.equals("subscription")) {
             session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".subscription (" + "user text," + "mailbox text," + "PRIMARY KEY (mailbox, user)" + ");");
+        } else if (tableName.equals("quota")) {
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".quota ("
+                    + "user text PRIMARY KEY,"
+                    + "size_quota counter,"
+                    + "count_quota counter"
+                    + ");");
+        } else if (tableName.equals("quota_max")) {
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".quota_max ("
+                    + "user text PRIMARY KEY,"
+                    + "size_quota_max bigint,"
+                    + "count_quota_max bigint"
+                    + ");");
+        } else if(tableName.equals("default_max_quota_table")) {
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".default_max_quota_table ("
+                    + "id int PRIMARY KEY,"
+                    + "default_max_size_quota bigint,"
+                    + "default_max_count_quota bigint"
+                    + ");");
+        } else if(tableName.equals("group_membership")) {
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".group_membership("
+                    + "user text,"
+                    + "group text,"
+                    + "PRIMARY KEY (user,group)"
+                    + ");");
+        } else if(tableName.equals("modseq")) {
+            session.execute("CREATE TABLE IF NOT EXISTS " + session.getLoggedKeyspace() + ".modseq (" + "mailboxId uuid PRIMARY KEY," + "nextModseq bigint" + ");");
         } else {
             throw new NotImplementedException("We don't support the class " + tableName);
         }
@@ -107,11 +156,16 @@
         ensureTable("mailboxCounters");
         ensureTable("message");
         ensureTable("subscription");
+        ensureTable("quota");
+        ensureTable("quota_max");
+        ensureTable("default_max_quota_table");
+        ensureTable("group_membership");
+        ensureTable("modseq");
     }
 
     /**
      * Delete all rows from specified table.
-     * 
+     *
      * @param tableName
      */
     public void clearTable(String tableName) {
@@ -126,6 +180,11 @@
         clearTable("mailboxCounters");
         clearTable("message");
         clearTable("subscription");
+        clearTable("quota");
+        clearTable("quota_max");
+        clearTable("default_max_quota_table");
+        clearTable("group_membership");
+        clearTable("modseq");
     }
 
 }
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipTest.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipTest.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipTest.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraGroupMembershipTest.java	2015-03-09 11:25:46.000000000 +0100
@@ -0,0 +1,44 @@
+/****************************************************************
+ * 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;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Tests the CassandraGroupMembershipResolver class.
+ */
+public class CassandraGroupMembershipTest {
+    @Test
+    public void testGroupMembership() {
+        CassandraClusterSingleton CLUSTER = CassandraClusterSingleton.build();
+        CLUSTER.ensureAllTables();
+        CassandraGroupMembershipResolver groupMembershipResolver = new CassandraGroupMembershipResolver(CLUSTER.getConf());
+        assertFalse(groupMembershipResolver.isMember("benwa", "Linagora"));
+        groupMembershipResolver.addMembership("Linagora", "benwa");
+        assertFalse(groupMembershipResolver.isMember("benwa", "Renault"));
+        assertTrue(groupMembershipResolver.isMember("benwa", "Linagora"));
+        groupMembershipResolver.removeMembership("Linagora","benwa");
+        assertFalse(groupMembershipResolver.isMember("benwa","Linagora"));
+        CLUSTER.clearAllTables();
+    }
+}
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java	2015-03-09 10:46:43.260296503 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapperTest.java	2015-03-13 15:21:36.000000000 +0100
@@ -28,9 +28,11 @@
 import java.util.UUID;
 
 import org.apache.james.mailbox.cassandra.CassandraClusterSingleton;
+import org.apache.james.mailbox.cassandra.mail.model.CassandraMailbox;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.SimpleMailboxACL;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
 import org.junit.Before;
@@ -82,6 +84,9 @@
         testSave();
         testDelete();
         testHasChildren();
+        testAclInitialisation();
+        testSetACLMethod();
+        testSetAclWithVersion();
         // testDeleteAllMemberships(); // Ignore this test
     }
 
@@ -119,10 +124,11 @@
             if (i % 2 == 0) {
                 newPath.setUser(null);
             }
-            addMailbox(new SimpleMailbox<UUID>(newPath, 1234));
+            addMailbox(new CassandraMailbox<UUID>(newPath, 1234, CLUSTER.getConf()));
         }
-        result = mapper.findMailboxWithPathLike(path);
-        assertEquals(end - start + 1, result.size());
+        MailboxPath searchPath = new MailboxPath(path, "%"+path.getName()+"%");
+        result = mapper.findMailboxWithPathLike(searchPath);
+        assertEquals(  ( end - start ) / 2 + 1, result.size());
     }
 
     /**
@@ -209,6 +215,38 @@
         }
     }
 
+    private void testAclInitialisation() {
+        CassandraMailbox<UUID> mailbox = (CassandraMailbox<UUID>) mailboxList.get(0);
+        assertEquals(SimpleMailboxACL.EMPTY, mailbox.getACL());
+        CassandraMailbox.ACLWithVersion aclWithVersion = mailbox.getACLWithVersion();
+        assertEquals(SimpleMailboxACL.EMPTY, aclWithVersion.acl);
+        assertEquals(aclWithVersion.version, 0);
+    }
+
+    private void testSetAclWithVersion() {
+        CassandraMailbox<UUID> mailbox = (CassandraMailbox<UUID>) mailboxList.get(0);
+        mailbox.setACL(SimpleMailboxACL.EMPTY);
+        assertEquals(SimpleMailboxACL.EMPTY, mailbox.getACL());
+        mailbox.setACLWithVersion(SimpleMailboxACL.OWNER_FULL_ACL, 0);
+        CassandraMailbox.ACLWithVersion aclWithVersion = mailbox.getACLWithVersion();
+        assertEquals(SimpleMailboxACL.OWNER_FULL_ACL, aclWithVersion.acl);
+        assertEquals(1, aclWithVersion.version);
+    }
+
+    /**
+     * Test if a mailbox can store and retrieve its ACLs
+     */
+    private void testSetACLMethod() {
+        // Find a mailbox
+        CassandraMailbox<UUID> mailbox = (CassandraMailbox<UUID>) mailboxList.get(0);
+        // Test a setACL
+        mailbox.setACL(SimpleMailboxACL.OWNER_FULL_ACL);
+        assertEquals(SimpleMailboxACL.OWNER_FULL_ACL, mailbox.getACL());
+        CassandraMailbox.ACLWithVersion aclWithVersion = mailbox.getACLWithVersion();
+        assertEquals(SimpleMailboxACL.OWNER_FULL_ACL, aclWithVersion.acl);
+        assertEquals(0, aclWithVersion.version);
+    }
+
     private static void fillMailboxList() {
         mailboxList = new ArrayList<SimpleMailbox<UUID>>();
         pathsList = new ArrayList<MailboxPath>();
@@ -224,14 +262,14 @@
                     }
                     path = new MailboxPath("namespace" + i, "user" + j, name);
                     pathsList.add(path);
-                    mailboxList.add(new SimpleMailbox<UUID>(path, 13));
+                    mailboxList.add(new CassandraMailbox<UUID>(path, 13, CLUSTER.getConf()));
                 }
             }
         }
         LOG.info("Created test case with {} mailboxes and {} paths", mailboxList.size(), pathsList.size());
     }
 
-    private void addMailbox(SimpleMailbox<UUID> mailbox) throws MailboxException {
+    private void addMailbox(CassandraMailbox<UUID> mailbox) throws MailboxException {
         mailboxList.add(mailbox);
         pathsList.add(new MailboxPath(mailbox.getNamespace(), mailbox.getUser(), mailbox.getName()));
         mapper.save(mailbox);
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java	2015-03-13 15:28:48.000000000 +0100
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
@@ -144,6 +145,10 @@
         testGetHighestModSeq();
         testMessageUpdateReplace();
         testMessageUpdateAddition();
+        testSeenFlagSet();
+        testCopySeenFlagSet();
+        testSeenFlagUnSet();
+        testCopySeenFlagUnSet();
     }
 
     /**
@@ -174,6 +179,8 @@
         LOG.info("message update : flag addition");
         Flags flags = new Flags();
         flags.add(Flags.Flag.FLAGGED);
+        flags.add("personal");
+        flags.add("work");
         messageMapper.updateFlags(MBOXES.get(1), flags, true, false, MessageRange.all());
         Iterator<Message<UUID>> messageIterator = messageMapper.findInMailbox(MBOXES.get(1), MessageRange.all(), MessageMapper.FetchType.Full, 100);
         while(messageIterator.hasNext()) {
@@ -184,27 +191,89 @@
             assertFalse(message.isRecent());
             assertFalse(message.isSeen());
             assertTrue(message.isFlagged());
+            String[] userFlags = message.createFlags().getUserFlags();
+            assertEquals(2, userFlags.length);
+            assertEquals("work", userFlags[0]);
+            assertEquals("personal", userFlags[1]);
         }
     }
 
     /**
-     * Test message flag removal
+     * Test that messages are all marked read when flag SEEN is set
      */
-    private void testMessageUpdateRemove() throws MailboxException {
-        LOG.info("message update : flag removal");
+    private void testSeenFlagSet() throws MailboxException {
+        LOG.info("message update : flag addition");
         Flags flags = new Flags();
-        flags.add(Flags.Flag.ANSWERED);
+        flags.add(Flags.Flag.SEEN);
+        messageMapper.updateFlags(MBOXES.get(1), flags, true, false, MessageRange.all());
+        Iterator<Message<UUID>> messageIterator = messageMapper.findInMailbox(MBOXES.get(1), MessageRange.all(), MessageMapper.FetchType.Full, 100);
+        while(messageIterator.hasNext()) {
+            Message<UUID> message = messageIterator.next();
+            assertTrue(message.isAnswered());
+            assertTrue(message.isDraft());
+            assertFalse(message.isDeleted());
+            assertFalse(message.isRecent());
+            assertTrue(message.isSeen());
+            assertTrue(message.isFlagged());
+        }
+        assertEquals(0, messageMapper.countUnseenMessagesInMailbox(MBOXES.get(1)));
+    }
+
+    /**
+     * Test that messages are all marked read when flag SEEN is set
+     */
+    private void testCopySeenFlagSet() throws MailboxException {
+        LOG.info("message copy : seen flag set");
+        Mailbox<UUID> destinationMailbox = MBOXES.get(0);
+        Mailbox<UUID> originMailbox = MBOXES.get(1);
+        long destinationMessageCount = messageMapper.countMessagesInMailbox(destinationMailbox);
+        long destinationUnseenMessaageCount = messageMapper.countUnseenMessagesInMailbox(destinationMailbox);
+        Message<UUID> message =  messageMapper.findInMailbox(originMailbox, MessageRange.one(1), MessageMapper.FetchType.Full, -1).next();
+        messageMapper.copy(destinationMailbox, message);
+        assertTrue(message.isSeen());
+        assertEquals(destinationMessageCount + 1, messageMapper.countMessagesInMailbox(destinationMailbox));
+        assertEquals(destinationUnseenMessaageCount, messageMapper.countUnseenMessagesInMailbox(destinationMailbox));
+    }
+
+    /**
+     * Test that messages are all marked read when flag SEEN is set
+     */
+    private void testCopySeenFlagUnSet() throws MailboxException {
+        LOG.info("message copy : seen flag set");
+        Mailbox<UUID> destinationMailbox = MBOXES.get(0);
+        Mailbox<UUID> originMailbox = MBOXES.get(1);
+        long destinationMessageCount = messageMapper.countMessagesInMailbox(destinationMailbox);
+        long destinationUnseenMessaageCount = messageMapper.countUnseenMessagesInMailbox(destinationMailbox);
+        Message<UUID> message =  messageMapper.findInMailbox(originMailbox, MessageRange.one(1), MessageMapper.FetchType.Full, -1).next();
+        messageMapper.copy(destinationMailbox, message);
+        assertFalse(message.isSeen());
+        assertEquals(destinationMessageCount + 1, messageMapper.countMessagesInMailbox(destinationMailbox));
+        assertEquals(destinationUnseenMessaageCount +1, messageMapper.countUnseenMessagesInMailbox(destinationMailbox));
+    }
+
+    /**
+     * Test that messages are all marked read when flag SEEN is set
+     */
+    private void testSeenFlagUnSet() throws MailboxException {
+        LOG.info("message update : flag addition");
+        Flags flags = new Flags();
+        flags.add(Flags.Flag.SEEN);
+        flags.add("personal");
+        flags.add("work");
         messageMapper.updateFlags(MBOXES.get(1), flags, false, false, MessageRange.all());
         Iterator<Message<UUID>> messageIterator = messageMapper.findInMailbox(MBOXES.get(1), MessageRange.all(), MessageMapper.FetchType.Full, 100);
         while(messageIterator.hasNext()) {
             Message<UUID> message = messageIterator.next();
-            assertFalse(message.isAnswered());
+            assertTrue(message.isAnswered());
             assertTrue(message.isDraft());
             assertFalse(message.isDeleted());
             assertFalse(message.isRecent());
             assertFalse(message.isSeen());
             assertTrue(message.isFlagged());
+            String[] userFlags = message.createFlags().getUserFlags();
+            assertEquals(0, userFlags.length);
         }
+        assertEquals(2*COUNT, messageMapper.countUnseenMessagesInMailbox(MBOXES.get(1)));
     }
 
     /**
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidAndModSeqProviderTest.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidAndModSeqProviderTest.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidAndModSeqProviderTest.java	2015-03-09 10:46:43.208296245 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraUidAndModSeqProviderTest.java	2015-03-09 11:25:46.000000000 +0100
@@ -25,6 +25,7 @@
 import java.util.UUID;
 
 import org.apache.james.mailbox.cassandra.CassandraClusterSingleton;
+import org.apache.james.mailbox.cassandra.mail.model.CassandraMailbox;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
 import org.junit.Before;
@@ -82,7 +83,7 @@
                     }
                     path = new MailboxPath("namespace" + i, "user" + j, name);
                     pathsList.add(path);
-                    mailboxList.add(new SimpleMailbox<UUID>(path, 13));
+                    mailboxList.add(new CassandraMailbox<UUID>(path, 13, CLUSTER.getConf()));
                 }
             }
         }
@@ -97,7 +98,7 @@
     public void testLastUid() throws Exception {
         LOG.info("lastUid");
         final MailboxPath path = new MailboxPath("gsoc", "ieugen", "Trash");
-        final SimpleMailbox<UUID> newBox = new SimpleMailbox<UUID>(path, 1234);
+        final SimpleMailbox<UUID> newBox = new CassandraMailbox<UUID>(path, 1234, CLUSTER.getConf());
         mapper.save(newBox);
         mailboxList.add(newBox);
         pathsList.add(path);
@@ -133,7 +134,7 @@
         LOG.info("highestModSeq");
         LOG.info("lastUid");
         MailboxPath path = new MailboxPath("gsoc", "ieugen", "Trash");
-        SimpleMailbox<UUID> newBox = new SimpleMailbox<UUID>(path, 1234);
+        SimpleMailbox<UUID> newBox = new CassandraMailbox<UUID>(path, 1234, CLUSTER.getConf());
         mapper.save(newBox);
         mailboxList.add(newBox);
         pathsList.add(path);
diff -uNr james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/SimpleMailboxACLJsonConverterTest.java james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/SimpleMailboxACLJsonConverterTest.java
--- james-mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/SimpleMailboxACLJsonConverterTest.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/SimpleMailboxACLJsonConverterTest.java	2015-03-13 15:28:48.000000000 +0100
@@ -0,0 +1,119 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import org.apache.james.mailbox.cassandra.mail.model.SimpleMailboxACLJsonConverter;
+import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.SimpleMailboxACL;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test the converter of a simple ACL into Json
+ */
+public class SimpleMailboxACLJsonConverterTest {
+
+    @Test
+    public void testEmptyACLToJson() throws Exception {
+        MailboxACL emptyACL = SimpleMailboxACL.EMPTY;
+        assertEquals("{}", SimpleMailboxACLJsonConverter.toJson(emptyACL));
+    }
+
+    @Test
+    public void testSingleUserEntryToJson() throws Exception {
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleUserEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals("{\"-user\":2040}", SimpleMailboxACLJsonConverter.toJson(acl));
+    }
+
+    private void addSingleUserEntryToMap(Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map) {
+        SimpleMailboxACL.Rfc4314Rights rights = new SimpleMailboxACL.Rfc4314Rights(false, true, true, true, false, true, false, true, true, true, true);
+        SimpleMailboxACL.MailboxACLEntryKey key = new SimpleMailboxACL.SimpleMailboxACLEntryKey("user", MailboxACL.NameType.user, true);
+        map.put(key, rights);
+    }
+
+    @Test
+    public void testSingleGroupEntryToJson() throws Exception {
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleGroupEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals("{\"-$group\":2032}", SimpleMailboxACLJsonConverter.toJson(acl));
+    }
+
+    private void addSingleGroupEntryToMap(Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map) {
+        SimpleMailboxACL.Rfc4314Rights rights = new SimpleMailboxACL.Rfc4314Rights(false, false, true, true, false, true, false, true, true, true, true);
+        SimpleMailboxACL.MailboxACLEntryKey key = new SimpleMailboxACL.SimpleMailboxACLEntryKey("group", MailboxACL.NameType.group, true);
+        map.put(key, rights);
+    }
+
+    @Test
+    public void testSingleEntryToJson() throws Exception {
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleSpecialEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals("{\"-special\":1968}", SimpleMailboxACLJsonConverter.toJson(acl));
+    }
+
+    private void addSingleSpecialEntryToMap(Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map) {
+        SimpleMailboxACL.Rfc4314Rights rights = new SimpleMailboxACL.Rfc4314Rights(false, false, true, true, false, true, false, true, false, true, true);
+        SimpleMailboxACL.MailboxACLEntryKey key = new SimpleMailboxACL.SimpleMailboxACLEntryKey("special", MailboxACL.NameType.special, true);
+        map.put(key, rights);
+    }
+
+    @Test
+    public void testEmptyACLFromJson() throws Exception {
+        String aclString = "{}";
+        assertEquals(SimpleMailboxACL.EMPTY, SimpleMailboxACLJsonConverter.toACL(aclString));
+    }
+
+    @Test
+    public void testSingleUserEntryFromJson() throws Exception {
+        String aclString = "{\"-user\":2040}";
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleUserEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals(acl, SimpleMailboxACLJsonConverter.toACL(aclString));
+    }
+
+    @Test
+    public void testSingleGroupEntryFromJson() throws Exception {
+        String aclString = "{\"-$group\":2032}";
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleGroupEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals(acl, SimpleMailboxACLJsonConverter.toACL(aclString));
+    }
+
+    @Test
+    public void testMultipleEntriesFromJson() throws Exception {
+        String aclString = "{\"-$group\":2032,\"-user\":2040}";
+        Map<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights> map = new HashMap<SimpleMailboxACL.MailboxACLEntryKey, MailboxACL.MailboxACLRights>();
+        addSingleUserEntryToMap(map);
+        addSingleGroupEntryToMap(map);
+        MailboxACL acl = new SimpleMailboxACL(map);
+        assertEquals(acl, SimpleMailboxACLJsonConverter.toACL(aclString));
+    }
+}
diff -uNr james-mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java
--- james-mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java	2015-03-09 10:46:43.052295472 +0100
+++ james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMessage.java	2015-03-13 15:28:48.000000000 +0100
@@ -47,6 +47,7 @@
     private boolean flagged;
     private boolean recent;
     private boolean seen;
+    private String[] userFlags;
     private Date internalDate;
     private final String subType;
     private List<Property> properties;
@@ -142,6 +143,12 @@
         flagged = flags.contains(Flags.Flag.FLAGGED);
         recent = flags.contains(Flags.Flag.RECENT);
         seen = flags.contains(Flags.Flag.SEEN);
+        userFlags = flags.getUserFlags();
+    }
+
+    @Override
+    protected String[] createUserFlags() {
+        return userFlags;
     }
 
     public InputStream getBodyContent() throws IOException {
diff -uNr james-mailbox/store/src/main/java/org/apache/james/mailbox/store/NoMailboxPathLocker.java james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/NoMailboxPathLocker.java
--- james-mailbox/store/src/main/java/org/apache/james/mailbox/store/NoMailboxPathLocker.java	1970-01-01 01:00:00.000000000 +0100
+++ james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/NoMailboxPathLocker.java	2015-03-13 15:28:48.000000000 +0100
@@ -0,0 +1,44 @@
+/****************************************************************
+ * 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.store;
+
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxPath;
+
+/**
+ * Some distributed mailboxes do not require any locking ( eg : Cassandra )
+ *
+ * This PathLocker avoid useless locking when these mailboxes are used
+ */
+public class NoMailboxPathLocker extends AbstractMailboxPathLocker {
+
+    @Override
+    protected void lock(MailboxSession session, MailboxPath path, boolean writeLock) throws MailboxException {
+
+    }
+
+
+    @Override
+    protected void unlock(MailboxSession session, MailboxPath path, boolean writeLock) throws MailboxException {
+
+    }
+
+}
diff -uNr james-mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
--- james-mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java	2015-03-09 10:46:43.020295313 +0100
+++ james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java	2015-03-11 17:57:07.000000000 +0100
@@ -480,7 +480,7 @@
             List<MessageRange> movedRanges = new ArrayList<MessageRange>();
             Iterator<MessageRange> ranges = set.split(moveBatchSize).iterator();
             while (ranges.hasNext()) {
-                movedRanges.addAll(fromMailbox.moveTo(ranges.next(), toMailbox, session));
+                movedRanges.addAll(fromMailbox.copyTo(ranges.next(), toMailbox, session));
             }
             return movedRanges;
         } else {
diff -uNr james-mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java
--- james-mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java	2015-03-09 10:46:43.048295452 +0100
+++ james-mailbox-new/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java	2015-03-11 17:57:07.000000000 +0100
@@ -25,6 +25,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -126,6 +127,10 @@
 
     private int fetchBatchSize;
 
+    private int updateBatchSize;
+
+    private int deleteBatchSize;
+
     public StoreMessageManager(final MessageMapperFactory<Id> mapperFactory, final MessageSearchIndex<Id> index, final MailboxEventDispatcher<Id> dispatcher, final MailboxPathLocker locker, final Mailbox<Id> mailbox, final MailboxACLResolver aclResolver,
             final GroupMembershipResolver groupMembershipResolver) throws MailboxException {
         this.mailbox = mailbox;
@@ -141,6 +146,14 @@
         this.fetchBatchSize = fetchBatchSize;
     }
 
+    public void setUpdateBatchSize(int updateBatchSize) {
+        this.updateBatchSize = updateBatchSize;
+    }
+
+    public void setDeleteBatchSize(int deleteBatchSize) {
+        this.deleteBatchSize = deleteBatchSize;
+    }
+
     /**
      * Return the {@link MailboxPathLocker}
      * 
@@ -530,23 +543,25 @@
 
         final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(mailboxSession);
 
-        Iterator<UpdatedFlags> it = messageMapper.execute(new Mapper.Transaction<Iterator<UpdatedFlags>>() {
+        List<MessageRange> updateRanges = splitRange(set, updateBatchSize);
+        for(final MessageRange range : updateRanges) {
+            Iterator<UpdatedFlags> it = messageMapper.execute(new Mapper.Transaction<Iterator<UpdatedFlags>>() {
 
-            public Iterator<UpdatedFlags> run() throws MailboxException {
-                return messageMapper.updateFlags(getMailboxEntity(), flags, value, replace, set);
-            }
-        });
-
-        final SortedMap<Long, UpdatedFlags> uFlags = new TreeMap<Long, UpdatedFlags>();
+                public Iterator<UpdatedFlags> run() throws MailboxException {
+                    return messageMapper.updateFlags(getMailboxEntity(), flags, value, replace, range);
+                }
+            });
 
-        while (it.hasNext()) {
-            UpdatedFlags flag = it.next();
-            newFlagsByUid.put(flag.getUid(), flag.getNewFlags());
-            uFlags.put(flag.getUid(), flag);
-        }
+            final SortedMap<Long, UpdatedFlags> uFlags = new TreeMap<Long, UpdatedFlags>();
 
-        dispatcher.flagsUpdated(mailboxSession, new ArrayList<Long>(uFlags.keySet()), getMailboxEntity(), new ArrayList<UpdatedFlags>(uFlags.values()));
+            while (it.hasNext()) {
+                UpdatedFlags flag = it.next();
+                newFlagsByUid.put(flag.getUid(), flag.getNewFlags());
+                uFlags.put(flag.getUid(), flag);
+            }
 
+            dispatcher.flagsUpdated(mailboxSession, new ArrayList<Long>(uFlags.keySet()), getMailboxEntity(), new ArrayList<UpdatedFlags>(uFlags.values()));
+        }
         return newFlagsByUid;
     }
 
@@ -658,7 +673,10 @@
                 for (MessageRange range : ranges) {
                     if (reset) {
                         // only call save if we need to
-                        messageMapper.updateFlags(getMailboxEntity(), new Flags(Flag.RECENT), false, false, range);
+                        List<MessageRange> updateRanges = splitRange(range, updateBatchSize);
+                        for(MessageRange workingRange : updateRanges) {
+                            messageMapper.updateFlags(getMailboxEntity(), new Flags(Flag.RECENT), false, false, workingRange);
+                        }
                     }
                 }
                 return members;
@@ -672,13 +690,20 @@
 
         final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
 
-        return messageMapper.execute(new Mapper.Transaction<Map<Long, MessageMetaData>>() {
+        Map<Long, MessageMetaData> result = new HashMap<Long, MessageMetaData>();
+        Map<Long, MessageMetaData> partialResult;
+        List<MessageRange> deleteRanges = splitRange(range, deleteBatchSize);
+        for(final MessageRange workingRanges : deleteRanges) {
+            partialResult = messageMapper.execute(new Mapper.Transaction<Map<Long, MessageMetaData>>() {
 
-            public Map<Long, MessageMetaData> run() throws MailboxException {
-                return messageMapper.expungeMarkedForDeletionInMailbox(getMailboxEntity(), range);
-            }
+                public Map<Long, MessageMetaData> run() throws MailboxException {
+                    return messageMapper.expungeMarkedForDeletionInMailbox(getMailboxEntity(), workingRanges);
+                }
 
-        });
+            });
+            result.putAll(partialResult);
+        }
+        return result;
     }
 
     /**
@@ -867,4 +892,25 @@
         return ns == null || (!ns.equals(session.getPersonalSpace()) && !ns.equals(session.getOtherUsersSpace()));
     }
 
+    /**
+     * This method split the provided MessageRange into a collection of smaller MessageRanges,
+     * with a maximum Message count of batchSize
+     *
+     * As split method from MessageRange is only implemented for
+     *
+     * @param set The MessageRange we want to split
+     * @param batchSize The maximum size allowed for Range based MessageRange
+     * @return The list of the resulted MessageRanges
+     */
+    private List<MessageRange> splitRange(MessageRange set, int batchSize) {
+        List<MessageRange> updateRanges;
+        if (batchSize > 0) {
+            updateRanges = set.split(batchSize);
+        } else {
+            updateRanges = new ArrayList<MessageRange>();
+            updateRanges.add(set);
+        }
+        return updateRanges;
+    }
+
 }
