### Eclipse Workspace Patch 1.0
#P imap-trunk
Index: jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMessageManager.java
===================================================================
--- jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMessageManager.java	(revision 984470)
+++ jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMessageManager.java	(working copy)
@@ -31,7 +31,6 @@
 import org.apache.james.imap.jpa.mail.model.JPAHeader;
 import org.apache.james.imap.jpa.mail.model.openjpa.JPAStreamingMailboxMembership;
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.store.mail.model.Header;
 import org.apache.james.imap.store.mail.model.Mailbox;
@@ -47,13 +46,13 @@
     private final boolean useStreaming;
 
     public OpenJPAMessageManager(JPAMailboxSessionMapperFactory mapperFactory,
-            MailboxEventDispatcher dispatcher, Mailbox<Long> mailbox, MailboxSession session) throws MailboxException {
-        this(mapperFactory, dispatcher, mailbox, session, false);
+            MailboxEventDispatcher dispatcher, Mailbox<Long> mailbox) throws MailboxException {
+        this(mapperFactory, dispatcher, mailbox, false);
     }
 
     public OpenJPAMessageManager(JPAMailboxSessionMapperFactory mapperFactory,
-            MailboxEventDispatcher dispatcher, Mailbox<Long> mailbox, MailboxSession session, final boolean useStreaming) throws MailboxException {
-        super(mapperFactory, dispatcher, mailbox, session);
+            MailboxEventDispatcher dispatcher, Mailbox<Long> mailbox, final boolean useStreaming) throws MailboxException {
+        super(mapperFactory, dispatcher, mailbox);
         this.useStreaming = useStreaming;
     }
 
@@ -64,7 +63,7 @@
             for (Header header: headers) {
                 jpaHeaders.add((JPAHeader) header);
             }
-            return new JPAStreamingMailboxMembership(mailbox.getMailboxId(), internalDate, size, flags, document, bodyStartOctet, jpaHeaders, propertyBuilder);
+            return new JPAStreamingMailboxMembership(getMailboxEntity().getMailboxId(), internalDate, size, flags, document, bodyStartOctet, jpaHeaders, propertyBuilder);
         } else {
             return super.createMessage(internalDate, size, bodyStartOctet, document, flags, headers, propertyBuilder);
         }
Index: store/src/main/java/org/apache/james/imap/store/AbstractStoreMessageManager.java
===================================================================
--- store/src/main/java/org/apache/james/imap/store/AbstractStoreMessageManager.java	(revision 0)
+++ store/src/main/java/org/apache/james/imap/store/AbstractStoreMessageManager.java	(revision 0)
@@ -0,0 +1,491 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.imap.store;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.mail.Flags;
+import javax.mail.MessagingException;
+import javax.mail.util.SharedFileInputStream;
+
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.mailbox.MailboxException;
+import org.apache.james.imap.mailbox.MailboxListener;
+import org.apache.james.imap.mailbox.MailboxNotFoundException;
+import org.apache.james.imap.mailbox.MailboxSession;
+import org.apache.james.imap.mailbox.MessageRange;
+import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
+import org.apache.james.imap.store.mail.MessageMapper;
+import org.apache.james.imap.store.mail.model.Header;
+import org.apache.james.imap.store.mail.model.Mailbox;
+import org.apache.james.imap.store.mail.model.MailboxMembership;
+import org.apache.james.imap.store.mail.model.PropertyBuilder;
+import org.apache.james.imap.store.streaming.ConfigurableMimeTokenStream;
+import org.apache.james.imap.store.streaming.CountingInputStream;
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.descriptor.MaximalBodyDescriptor;
+import org.apache.james.mime4j.parser.MimeEntityConfig;
+import org.apache.james.mime4j.parser.MimeTokenStream;
+
+/**
+ * Abstract base class for {@link org.apache.james.imap.mailbox.Mailbox} implementations. This abstract
+ * class take care of dispatching events to the registered {@link MailboxListener} and so help
+ * with handling concurrent {@link MailboxSession}'s. So this is a perfect starting point when writing your 
+ * own implementation and don't want to depend on {@link MessageMapper}.
+ *
+ */
+public abstract class AbstractStoreMessageManager<Id> implements org.apache.james.imap.mailbox.Mailbox{
+
+
+    private final Mailbox<Id> mailbox;
+    
+    private final MailboxEventDispatcher dispatcher;    
+    
+    public AbstractStoreMessageManager(final MailboxEventDispatcher dispatcher, final Mailbox<Id> mailbox) throws MailboxException {
+        this.mailbox = mailbox;
+        this.dispatcher = dispatcher;
+    }
+    
+    /**
+     * Return the {@link MailboxEventDispatcher} for this Mailbox
+     * 
+     * @return dispatcher
+     */
+    protected MailboxEventDispatcher getDispatcher() {
+        return dispatcher;
+    }
+    
+    /**
+     * Return the underlying {@link Mailbox}
+     * 
+     * @param session
+     * @return mailbox
+     * @throws MailboxException
+     */
+    
+    public Mailbox<Id> getMailboxEntity() throws MailboxException {
+        return mailbox;
+    }
+    
+
+    
+    private Flags getPermanentFlags() {
+        Flags permanentFlags = new Flags();
+        permanentFlags.add(Flags.Flag.ANSWERED);
+        permanentFlags.add(Flags.Flag.DELETED);
+        permanentFlags.add(Flags.Flag.DRAFT);
+        permanentFlags.add(Flags.Flag.FLAGGED);
+        permanentFlags.add(Flags.Flag.SEEN);
+        return permanentFlags;
+    }
+
+    /**
+     * Return a List which holds all uids of recent messages and optional reset the recent flag on the messages for the uids
+     * 
+     * @param reset
+     * @param mailboxSession
+     * @return list
+     * @throws MailboxException
+     */
+    protected abstract List<Long> recent(final boolean reset, MailboxSession mailboxSession) throws MailboxException;
+
+
+    protected abstract Iterator<Long> deleteMarkedInMailbox(MessageRange range, MailboxSession session) throws MailboxException;
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Mailbox#expunge(org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.mailbox.MailboxSession)
+     */
+    public Iterator<Long> expunge(final MessageRange set, MailboxSession mailboxSession) throws MailboxException {
+        List<Long> uids = new ArrayList<Long>();
+        Iterator<Long> uidIt = deleteMarkedInMailbox(set, mailboxSession);
+        while(uidIt.hasNext()) {
+            long uid = uidIt.next();
+            dispatcher.expunged(uid, mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()));
+            uids.add(uid);
+        }
+        return uids.iterator();    
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Mailbox#appendMessage(byte[], java.util.Date, org.apache.james.imap.mailbox.MailboxSession, boolean, javax.mail.Flags)
+     */
+    public long appendMessage(final InputStream msgIn, final Date internalDate,
+            final MailboxSession mailboxSession,final boolean isRecent, final Flags flagsToBeSet)
+    throws MailboxException {
+        File file = null;
+        SharedFileInputStream tmpMsgIn = null;
+        try {
+            // Create a temporary file and copy the message to it. We will work with the file as
+            // source for the InputStream
+            file = File.createTempFile("imap", ".msg");
+            FileOutputStream out = new FileOutputStream(file);
+            
+            byte[] buf = new byte[1024];
+            int i = 0;
+            while ((i = msgIn.read(buf)) != -1) {
+                out.write(buf, 0, i);
+            }
+            out.flush();
+            out.close();
+            
+            tmpMsgIn = new SharedFileInputStream(file);
+           
+            final int size = tmpMsgIn.available();
+            final int bodyStartOctet = bodyStartOctet(tmpMsgIn);
+
+            // Disable line length... This should be handled by the smtp server component and not the parser itself
+            // https://issues.apache.org/jira/browse/IMAP-122
+            MimeEntityConfig config = new MimeEntityConfig();
+            config.setMaximalBodyDescriptor(true);
+            config.setMaxLineLen(-1);
+            final ConfigurableMimeTokenStream parser = new ConfigurableMimeTokenStream(config);
+           
+            parser.setRecursionMode(MimeTokenStream.M_NO_RECURSE);
+            parser.parse(tmpMsgIn.newStream(0, -1));
+            final List<Header> headers = new ArrayList<Header>();
+            
+            int lineNumber = 0;
+            int next = parser.next();
+            while (next != MimeTokenStream.T_BODY
+                    && next != MimeTokenStream.T_END_OF_STREAM
+                    && next != MimeTokenStream.T_START_MULTIPART) {
+                if (next == MimeTokenStream.T_FIELD) {
+                    String fieldValue = parser.getField().getBody();
+                    if (fieldValue.endsWith("\r\f")) {
+                        fieldValue = fieldValue.substring(0,fieldValue.length() - 2);
+                    }
+                    if (fieldValue.startsWith(" ")) {
+                        fieldValue = fieldValue.substring(1);
+                    }
+                    final Header header 
+                        = createHeader(++lineNumber, parser.getField().getName(), 
+                            fieldValue);
+                    headers.add(header);
+                }
+                next = parser.next();
+            }
+            final MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) parser.getBodyDescriptor();
+            final PropertyBuilder propertyBuilder = new PropertyBuilder();
+            final String mediaType;
+            final String mediaTypeFromHeader = descriptor.getMediaType();
+            final String subType;
+            if (mediaTypeFromHeader == null) {
+                mediaType = "text";
+                subType = "plain";
+            } else {
+                mediaType = mediaTypeFromHeader;
+                subType = descriptor.getSubType();
+            }
+            propertyBuilder.setMediaType(mediaType);
+            propertyBuilder.setSubType(subType);
+            propertyBuilder.setContentID(descriptor.getContentId());
+            propertyBuilder.setContentDescription(descriptor.getContentDescription());
+            propertyBuilder.setContentLocation(descriptor.getContentLocation());
+            propertyBuilder.setContentMD5(descriptor.getContentMD5Raw());
+            propertyBuilder.setContentTransferEncoding(descriptor.getTransferEncoding());
+            propertyBuilder.setContentLanguage(descriptor.getContentLanguage());
+            propertyBuilder.setContentDispositionType(descriptor.getContentDispositionType());
+            propertyBuilder.setContentDispositionParameters(descriptor.getContentDispositionParameters());
+            propertyBuilder.setContentTypeParameters(descriptor.getContentTypeParameters());
+            // Add missing types
+            final String codeset = descriptor.getCharset();
+            if (codeset == null) {
+                if ("TEXT".equalsIgnoreCase(mediaType)) {
+                    propertyBuilder.setCharset("us-ascii");
+                }
+            } else {
+                propertyBuilder.setCharset(codeset);
+            }
+            
+            final String boundary = descriptor.getBoundary();
+            if (boundary != null) {
+                propertyBuilder.setBoundary(boundary);
+            }   
+            if ("text".equalsIgnoreCase(mediaType)) {
+                final CountingInputStream bodyStream = new CountingInputStream(parser.getInputStream());
+                bodyStream.readAll();
+                long lines = bodyStream.getLineCount();
+                
+                next = parser.next();
+                if (next == MimeTokenStream.T_EPILOGUE)  {
+                    final CountingInputStream epilogueStream = new CountingInputStream(parser.getInputStream());
+                    epilogueStream.readAll();
+                    lines+=epilogueStream.getLineCount();
+                }
+                propertyBuilder.setTextualLineCount(lines);
+            }
+            
+            final Flags flags;
+            if (flagsToBeSet == null) {
+                flags = new Flags();
+            } else {
+                flags = flagsToBeSet;
+            }
+            if (isRecent) {
+                flags.add(Flags.Flag.RECENT);
+            }
+            final MailboxMembership<Id> message = createMessage(internalDate, size, bodyStartOctet, tmpMsgIn.newStream(0, -1), flags, headers, propertyBuilder);
+            long uid = appendMessageToStore(message, mailboxSession);
+            
+            dispatcher.added(uid, mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()));
+            return uid;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
+        } catch (MessagingException e) {
+            e.printStackTrace();
+            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
+        } catch (MimeException e) {
+            e.printStackTrace();
+            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
+        } finally {
+            if (tmpMsgIn != null) {
+                try {
+                    tmpMsgIn.close();
+                } catch (IOException e) {
+                    // ignore on close
+                }
+            }
+            // delete the temporary file if one was specified
+            if (file != null) {
+                file.delete();
+            }
+        }
+
+    }
+    
+    protected abstract long appendMessageToStore(MailboxMembership<Id> message, MailboxSession session) throws MailboxException;
+
+    /**
+     * Return the position in the given {@link InputStream} at which the Body of the 
+     * Message starts
+     * 
+     * @param msgIn
+     * @return bodyStartOctet
+     * @throws IOException
+     */
+    private int bodyStartOctet(InputStream msgIn) throws IOException{
+        // we need to pushback maximal 3 bytes
+        PushbackInputStream in = new PushbackInputStream(msgIn,3);
+        
+        int bodyStartOctet = in.available();
+        int i = -1;
+        int count = 0;
+        while ((i = in.read()) != -1 && in.available() > 4) {
+            if (i == 0x0D) {
+                int a = in.read();
+                if (a == 0x0A) {
+                    int b = in.read();
+
+                    if (b == 0x0D) {
+                        int c = in.read();
+
+                        if (c == 0x0A) {
+                            bodyStartOctet = count+4;
+                            break;
+                        }
+                        in.unread(c);
+                    }
+                    in.unread(b);
+                }
+                in.unread(a);
+            }
+            count++;
+        }
+        
+        return bodyStartOctet;
+    }
+
+    /**
+     * Create a new {@link MailboxMembership} for the given data
+     * 
+     * @param internalDate
+     * @param size
+     * @param bodyStartOctet
+     * @param documentIn
+     * @param flags
+     * @param headers
+     * @param propertyBuilder
+     * @return membership
+     * @throws MailboxException 
+     */
+    protected abstract MailboxMembership<Id> createMessage(Date internalDate, final int size, int bodyStartOctet, 
+            final InputStream documentIn, final Flags flags, final List<Header> headers, PropertyBuilder propertyBuilder) throws MailboxException;
+    
+    /**
+     * Create a new {@link Header} for the given data
+     * 
+     * @param lineNumber
+     * @param name
+     * @param value
+     * @return header
+     */
+    protected abstract Header createHeader(int lineNumber, String name, String value);
+    
+    public void addListener(MailboxListener listener) throws MailboxException {
+        dispatcher.addMailboxListener(listener);
+    }
+
+    private long getUidNext(MailboxSession mailboxSession) throws MailboxException {
+        Mailbox<Id> mailbox = getMailboxEntity();
+        if (mailbox == null) {
+            throw new MailboxNotFoundException("Mailbox has been deleted");
+        } else {
+            final long lastUid = mailbox.getLastUid();
+            return lastUid + 1;
+        }
+    }
+    
+
+    /**
+     * This mailbox is writable
+     */
+    public boolean isWriteable(MailboxSession session) {
+        return true;
+    }
+    
+    
+    /**
+     * @see {@link Mailbox#getMetaData(boolean, MailboxSession, FetchGroup)}
+     */
+    public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, 
+            org.apache.james.imap.mailbox.Mailbox.MetaData.FetchGroup fetchGroup) throws MailboxException {
+        final List<Long> recent = recent(resetRecent, mailboxSession);
+        final Flags permanentFlags = getPermanentFlags();
+        final long uidValidity = getMailboxEntity().getUidValidity();
+        final long uidNext = getUidNext(mailboxSession);
+        final long messageCount = getMessageCount(mailboxSession);
+        final long unseenCount;
+        final Long firstUnseen;
+        switch (fetchGroup) {
+            case UNSEEN_COUNT:
+                unseenCount = countUnseenMessagesInMailbox(mailboxSession);
+                firstUnseen = null;
+                break;
+            case FIRST_UNSEEN:
+                firstUnseen = findFirstUnseenMessageUid(mailboxSession);
+                unseenCount = 0;
+                break;
+            default:
+                firstUnseen = null;
+                unseenCount = 0;
+                break;
+        }
+        return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession));
+    }
+
+    /**
+     * Return the uid of the first unseen message or null of none is found
+     * 
+     * @param mailbox
+     * @param session
+     * @return uid
+     * @throws MailboxException
+     */
+    protected abstract Long findFirstUnseenMessageUid(MailboxSession session) throws MailboxException;
+
+    /**
+     * Return the count of unseen messages
+     * 
+     * @param mailbox
+     * @param session
+     * @return
+     * @throws MailboxException
+     */
+    protected abstract long countUnseenMessagesInMailbox(MailboxSession session) throws MailboxException;
+    
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Mailbox#setFlags(javax.mail.Flags, boolean, boolean, org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.mailbox.MailboxSession)
+     */
+    public Map<Long, Flags> setFlags(final Flags flags, final boolean value, final boolean replace,
+            final MessageRange set, MailboxSession mailboxSession) throws MailboxException {
+       
+        final SortedMap<Long, Flags> newFlagsByUid = new TreeMap<Long, Flags>();
+
+        Iterator<UpdatedFlag> it = updateFlags(flags, value, replace, set, mailboxSession);
+        while (it.hasNext()) {
+            UpdatedFlag flag = it.next();
+            dispatcher.flagsUpdated(flag.getUid(), mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()), flag.getOldFlags(), flag.getNewFlags());
+            newFlagsByUid.put(flag.getUid(), flag.getNewFlags());
+        }
+        return newFlagsByUid;
+    }
+
+    /**
+     * Update the Flags for the given {@link MessageRange} 
+     * 
+     * @param flags
+     * @param value
+     * @param replace
+     * @param set
+     * @param mailboxSession
+     * @return
+     * @throws MailboxException
+     */
+    protected abstract Iterator<UpdatedFlag> updateFlags(final Flags flags, final boolean value, final boolean replace,
+            final MessageRange set, MailboxSession mailboxSession) throws MailboxException;
+
+
+
+    /**
+     * Copy the {@link MessageSet} to the {@link StoreMessageManager}
+     * 
+     * @param set
+     * @param toMailbox
+     * @param session
+     * @throws MailboxException
+     */
+    public void copyTo(MessageRange set, AbstractStoreMessageManager<Id> toMailbox, MailboxSession session) throws MailboxException {
+        try {
+            Iterator<Long> copiedUids = copy(set, toMailbox, session);
+            while(copiedUids.hasNext()) {
+                dispatcher.added(copiedUids.next(), session.getSessionId(), new StoreMailboxPath<Id>(toMailbox.getMailboxEntity()));
+            }
+        } catch (MessagingException e) {
+            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
+        }
+    }
+    
+    
+    /**
+     * Copy the messages in the given {@link MessageRange} to the given {@link AbstractStoreMessageManager} and return a {@link Iterator} which
+     * holds the uids of the copied messages
+     * 
+     * @param range
+     * @param toMailbox
+     * @param session
+     * @return uids
+     * @throws MailboxException
+     */
+    protected abstract Iterator<Long> copy(MessageRange range, AbstractStoreMessageManager<Id> toMailbox, MailboxSession session) throws MailboxException;
+}
Index: store/src/main/java/org/apache/james/imap/store/ResultIterator.java
===================================================================
--- store/src/main/java/org/apache/james/imap/store/ResultIterator.java	(revision 984773)
+++ store/src/main/java/org/apache/james/imap/store/ResultIterator.java	(working copy)
@@ -93,7 +93,7 @@
 
         private final Date internalDate;
 
-        private final int size;
+        private final long size;
 
         private final long uid;
 
@@ -101,7 +101,7 @@
                 final MailboxException exception) {
             super();
             internalDate = message.getInternalDate();
-            size = (int)message.getDocument().getFullContentOctets();
+            size = message.getDocument().getFullContentOctets();
             uid = message.getUid();
             this.exception = exception;
         }
Index: jcr/src/main/java/org/apache/james/imap/jcr/JCRMailboxManager.java
===================================================================
--- jcr/src/main/java/org/apache/james/imap/jcr/JCRMailboxManager.java	(revision 986733)
+++ jcr/src/main/java/org/apache/james/imap/jcr/JCRMailboxManager.java	(working copy)
@@ -54,7 +54,7 @@
     
     @Override
     protected StoreMessageManager<String> createMessageManager(MailboxEventDispatcher dispatcher, Mailbox<String> mailboxEntity, MailboxSession session) throws MailboxException{
-        return new JCRMessageManager(mapperFactory, dispatcher, (JCRMailbox) mailboxEntity, logger, getDelimiter(), session);
+        return new JCRMessageManager(mapperFactory, dispatcher, (JCRMailbox) mailboxEntity, logger, getDelimiter());
     }
 
     @Override
Index: store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java
===================================================================
--- store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java	(revision 987140)
+++ store/src/main/java/org/apache/james/imap/store/StoreMessageManager.java	(working copy)
@@ -19,31 +19,17 @@
 
 package org.apache.james.imap.store;
 
-import java.io.File;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PushbackInputStream;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
 import java.util.TreeSet;
 
 import javax.mail.Flags;
 import javax.mail.MessagingException;
-import javax.mail.util.SharedFileInputStream;
 
 import org.apache.james.imap.api.display.HumanReadableText;
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxListener;
-import org.apache.james.imap.mailbox.MailboxNotFoundException;
 import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.MessageRange;
 import org.apache.james.imap.mailbox.MessageResult;
@@ -51,17 +37,9 @@
 import org.apache.james.imap.mailbox.MessageResult.FetchGroup;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.store.mail.MessageMapper;
-import org.apache.james.imap.store.mail.model.Header;
 import org.apache.james.imap.store.mail.model.Mailbox;
 import org.apache.james.imap.store.mail.model.MailboxMembership;
-import org.apache.james.imap.store.mail.model.PropertyBuilder;
-import org.apache.james.imap.store.streaming.ConfigurableMimeTokenStream;
-import org.apache.james.imap.store.streaming.CountingInputStream;
 import org.apache.james.imap.store.transaction.TransactionalMapper;
-import org.apache.james.mime4j.MimeException;
-import org.apache.james.mime4j.descriptor.MaximalBodyDescriptor;
-import org.apache.james.mime4j.parser.MimeEntityConfig;
-import org.apache.james.mime4j.parser.MimeTokenStream;
 
 /**
  * Abstract base class for {@link org.apache.james.imap.mailbox.Mailbox} implementations.
@@ -70,278 +48,41 @@
  * 
  *
  */
-public abstract class StoreMessageManager<Id> implements org.apache.james.imap.mailbox.Mailbox {
+public abstract class StoreMessageManager<Id> extends AbstractStoreMessageManager<Id> {
 
-    private static final int INITIAL_SIZE_FLAGS = 32;
+   
+    private MailboxSessionMapperFactory<Id> mapperFactory;
 
-    private static final int INITIAL_SIZE_HEADERS = 32;
     
-    private MessageMapper<Id> messageMapper;
+    public StoreMessageManager(MailboxSessionMapperFactory<Id> mapperFactory, final MailboxEventDispatcher dispatcher, final Mailbox<Id> mailbox) throws MailboxException {
+        super(dispatcher, mailbox);
+        this.mapperFactory = mapperFactory;
+    }
 
-    protected final Mailbox<Id> mailbox;
-    
-    private MailboxEventDispatcher dispatcher;
 
-    
-    
-    public StoreMessageManager(MailboxSessionMapperFactory<Id> mapperFactory, final MailboxEventDispatcher dispatcher, final Mailbox<Id> mailbox, MailboxSession session) throws MailboxException {
-        this.mailbox = mailbox;
-        this.dispatcher = dispatcher;
-        this.messageMapper = mapperFactory.getMessageMapper(session);
-    }
+    @Override
+    protected long appendMessageToStore(final MailboxMembership<Id> message, MailboxSession session) throws MailboxException {
+        final MessageMapper<Id> mapper = mapperFactory.getMessageMapper(session);
+        return mapperFactory.getMessageMapper(session).execute(new TransactionalMapper.Transaction<Long>() {
 
-    protected MessageMapper<Id> getMessageMapper() {
-        return messageMapper;
-    }
-    
-    /**
-     * Return the {@link MailboxEventDispatcher} for this Mailbox
-     * 
-     * @return dispatcher
-     */
-    protected MailboxEventDispatcher getDispatcher() {
-        return dispatcher;
-    }
-    
-    /**
-     * Return the underlying {@link Mailbox}
-     * 
-     * @param session
-     * @return mailbox
-     * @throws MailboxException
-     */
-    
-    public Mailbox<Id> getMailboxEntity() throws MailboxException {
-        return mailbox;
+            public Long run() throws MailboxException {
+                return mapper.save(getMailboxEntity(), message);
+            }
+            
+        });
     }
 
+
     /*
      * (non-Javadoc)
      * @see org.apache.james.imap.mailbox.Mailbox#getMessageCount(org.apache.james.imap.mailbox.MailboxSession)
      */
     public long getMessageCount(MailboxSession mailboxSession) throws MailboxException {
-        return messageMapper.countMessagesInMailbox(mailbox);
+        return mapperFactory.getMessageMapper(mailboxSession).countMessagesInMailbox(getMailboxEntity());
     }
 
-    /*
-     * (non-Javadoc)
-     * @see org.apache.james.imap.mailbox.Mailbox#appendMessage(byte[], java.util.Date, org.apache.james.imap.mailbox.MailboxSession, boolean, javax.mail.Flags)
-     */
-    public long appendMessage(final InputStream msgIn, final Date internalDate,
-            final MailboxSession mailboxSession,final boolean isRecent, final Flags flagsToBeSet)
-    throws MailboxException {
-        File file = null;
-        SharedFileInputStream tmpMsgIn = null;
-        try {
-            // Create a temporary file and copy the message to it. We will work with the file as
-            // source for the InputStream
-            file = File.createTempFile("imap", ".msg");
-            FileOutputStream out = new FileOutputStream(file);
-            
-            byte[] buf = new byte[1024];
-            int i = 0;
-            while ((i = msgIn.read(buf)) != -1) {
-                out.write(buf, 0, i);
-            }
-            out.flush();
-            out.close();
-            
-            tmpMsgIn = new SharedFileInputStream(file);
-           
-            final int size = tmpMsgIn.available();
-            final int bodyStartOctet = bodyStartOctet(tmpMsgIn);
-
-            // Disable line length... This should be handled by the smtp server component and not the parser itself
-            // https://issues.apache.org/jira/browse/IMAP-122
-            MimeEntityConfig config = new MimeEntityConfig();
-            config.setMaximalBodyDescriptor(true);
-            config.setMaxLineLen(-1);
-            final ConfigurableMimeTokenStream parser = new ConfigurableMimeTokenStream(config);
-           
-            parser.setRecursionMode(MimeTokenStream.M_NO_RECURSE);
-            parser.parse(tmpMsgIn.newStream(0, -1));
-            final List<Header> headers = new ArrayList<Header>(INITIAL_SIZE_HEADERS);
-            
-            int lineNumber = 0;
-            int next = parser.next();
-            while (next != MimeTokenStream.T_BODY
-                    && next != MimeTokenStream.T_END_OF_STREAM
-                    && next != MimeTokenStream.T_START_MULTIPART) {
-                if (next == MimeTokenStream.T_FIELD) {
-                    String fieldValue = parser.getField().getBody();
-                    if (fieldValue.endsWith("\r\f")) {
-                        fieldValue = fieldValue.substring(0,fieldValue.length() - 2);
-                    }
-                    if (fieldValue.startsWith(" ")) {
-                        fieldValue = fieldValue.substring(1);
-                    }
-                    final Header header 
-                        = createHeader(++lineNumber, parser.getField().getName(), 
-                            fieldValue);
-                    headers.add(header);
-                }
-                next = parser.next();
-            }
-            final MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) parser.getBodyDescriptor();
-            final PropertyBuilder propertyBuilder = new PropertyBuilder();
-            final String mediaType;
-            final String mediaTypeFromHeader = descriptor.getMediaType();
-            final String subType;
-            if (mediaTypeFromHeader == null) {
-                mediaType = "text";
-                subType = "plain";
-            } else {
-                mediaType = mediaTypeFromHeader;
-                subType = descriptor.getSubType();
-            }
-            propertyBuilder.setMediaType(mediaType);
-            propertyBuilder.setSubType(subType);
-            propertyBuilder.setContentID(descriptor.getContentId());
-            propertyBuilder.setContentDescription(descriptor.getContentDescription());
-            propertyBuilder.setContentLocation(descriptor.getContentLocation());
-            propertyBuilder.setContentMD5(descriptor.getContentMD5Raw());
-            propertyBuilder.setContentTransferEncoding(descriptor.getTransferEncoding());
-            propertyBuilder.setContentLanguage(descriptor.getContentLanguage());
-            propertyBuilder.setContentDispositionType(descriptor.getContentDispositionType());
-            propertyBuilder.setContentDispositionParameters(descriptor.getContentDispositionParameters());
-            propertyBuilder.setContentTypeParameters(descriptor.getContentTypeParameters());
-            // Add missing types
-            final String codeset = descriptor.getCharset();
-            if (codeset == null) {
-                if ("TEXT".equalsIgnoreCase(mediaType)) {
-                    propertyBuilder.setCharset("us-ascii");
-                }
-            } else {
-                propertyBuilder.setCharset(codeset);
-            }
-            
-            final String boundary = descriptor.getBoundary();
-            if (boundary != null) {
-                propertyBuilder.setBoundary(boundary);
-            }   
-            if ("text".equalsIgnoreCase(mediaType)) {
-                final CountingInputStream bodyStream = new CountingInputStream(parser.getInputStream());
-                bodyStream.readAll();
-                long lines = bodyStream.getLineCount();
-                
-                next = parser.next();
-                if (next == MimeTokenStream.T_EPILOGUE)  {
-                    final CountingInputStream epilogueStream = new CountingInputStream(parser.getInputStream());
-                    epilogueStream.readAll();
-                    lines+=epilogueStream.getLineCount();
-                }
-                propertyBuilder.setTextualLineCount(lines);
-            }
-            
-            final Flags flags;
-            if (flagsToBeSet == null) {
-                flags = new Flags();
-            } else {
-                flags = flagsToBeSet;
-            }
-            if (isRecent) {
-                flags.add(Flags.Flag.RECENT);
-            }
-            final MailboxMembership<Id> message = createMessage(internalDate, size, bodyStartOctet, tmpMsgIn.newStream(0, -1), flags, headers, propertyBuilder);
-            long uid = messageMapper.execute(new TransactionalMapper.Transaction<Long>() {
-                
-                public Long run() throws MailboxException {
-                    return messageMapper.save(mailbox, message);
-                }
-            });
-            dispatcher.added(uid, mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()));
-            return uid;
-        } catch (IOException e) {
-            e.printStackTrace();
-            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
-        } catch (MessagingException e) {
-            e.printStackTrace();
-            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
-        } catch (MimeException e) {
-            e.printStackTrace();
-            throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
-        } finally {
-            if (tmpMsgIn != null) {
-                try {
-                    tmpMsgIn.close();
-                } catch (IOException e) {
-                    // ignore on close
-                }
-            }
-            // delete the temporary file if one was specified
-            if (file != null) {
-                file.delete();
-            }
-        }
-
-    }
 
-    /**
-     * Return the position in the given {@link InputStream} at which the Body of the 
-     * Message starts
-     * 
-     * @param msgIn
-     * @return bodyStartOctet
-     * @throws IOException
-     */
-    private int bodyStartOctet(InputStream msgIn) throws IOException{
-        // we need to pushback maximal 3 bytes
-        PushbackInputStream in = new PushbackInputStream(msgIn,3);
-        
-        int bodyStartOctet = in.available();
-        int i = -1;
-        int count = 0;
-        while ((i = in.read()) != -1 && in.available() > 4) {
-            if (i == 0x0D) {
-                int a = in.read();
-                if (a == 0x0A) {
-                    int b = in.read();
 
-                    if (b == 0x0D) {
-                        int c = in.read();
-
-                        if (c == 0x0A) {
-                            bodyStartOctet = count+4;
-                            break;
-                        }
-                        in.unread(c);
-                    }
-                    in.unread(b);
-                }
-                in.unread(a);
-            }
-            count++;
-        }
-        
-        return bodyStartOctet;
-    }
-
-    /**
-     * Create a new {@link MailboxMembership} for the given data
-     * 
-     * @param internalDate
-     * @param size
-     * @param bodyStartOctet
-     * @param documentIn
-     * @param flags
-     * @param headers
-     * @param propertyBuilder
-     * @return membership
-     * @throws MailboxException 
-     */
-    protected abstract MailboxMembership<Id> createMessage(Date internalDate, final int size, int bodyStartOctet, 
-            final InputStream documentIn, final Flags flags, final List<Header> headers, PropertyBuilder propertyBuilder) throws MailboxException;
-    
-    /**
-     * Create a new {@link Header} for the given data
-     * 
-     * @param lineNumber
-     * @param name
-     * @param value
-     * @return header
-     */
-    protected abstract Header createHeader(int lineNumber, String name, String value);
-
 
     /*
      * (non-Javadoc)
@@ -349,21 +90,10 @@
      */
     public Iterator<MessageResult> getMessages(final MessageRange set, FetchGroup fetchGroup,
             MailboxSession mailboxSession) throws MailboxException {
-        final List<MailboxMembership<Id>> rows = messageMapper.findInMailbox(mailbox, set);
+        final List<MailboxMembership<Id>> rows = mapperFactory.getMessageMapper(mailboxSession).findInMailbox(getMailboxEntity(), set);
         return new ResultIterator<Id>(rows.iterator(), fetchGroup);
     }
 
- 
-    private Flags getPermanentFlags() {
-        Flags permanentFlags = new Flags();
-        permanentFlags.add(Flags.Flag.ANSWERED);
-        permanentFlags.add(Flags.Flag.DELETED);
-        permanentFlags.add(Flags.Flag.DRAFT);
-        permanentFlags.add(Flags.Flag.FLAGGED);
-        permanentFlags.add(Flags.Flag.SEEN);
-        return permanentFlags;
-    }
-
     /**
      * Return a List which holds all uids of recent messages and optional reset the recent flag on the messages for the uids
      * 
@@ -373,11 +103,12 @@
      * @throws MailboxException
      */
     protected List<Long> recent(final boolean reset, MailboxSession mailboxSession) throws MailboxException {
-
+        final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(mailboxSession);
+        
         return messageMapper.execute(new TransactionalMapper.Transaction<List<Long>>() {
 
             public List<Long> run() throws MailboxException {
-                final List<MailboxMembership<Id>> members = messageMapper.findRecentMessagesInMailbox(mailbox, -1);
+                final List<MailboxMembership<Id>> members = messageMapper.findRecentMessagesInMailbox(getMailboxEntity(), -1);
                 final List<Long> results = new ArrayList<Long>();
 
                 for (MailboxMembership<Id> member:members) {
@@ -386,7 +117,7 @@
                         member.unsetRecent();
                         
                         // only call save if we need to
-                        messageMapper.save(mailbox, member);
+                        messageMapper.save(getMailboxEntity(), member);
                     }
                 }
                 return results;
@@ -396,50 +127,45 @@
         
     }
 
+    @Override
+    protected Iterator<Long> deleteMarkedInMailbox(final MessageRange range, final MailboxSession session) throws MailboxException {
+        final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
 
-    /*
-     * (non-Javadoc)
-     * @see org.apache.james.imap.mailbox.Mailbox#expunge(org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.mailbox.MailboxSession)
-     */
-    public Iterator<Long> expunge(final MessageRange set, MailboxSession mailboxSession) throws MailboxException {
-        
-        Collection<Long> uids = messageMapper.execute(new TransactionalMapper.Transaction<Collection<Long>>() {
+        return messageMapper.execute(new TransactionalMapper.Transaction<Iterator<Long>>() {
 
-            public Collection<Long> run() throws MailboxException {
+            public Iterator<Long> run() throws MailboxException {
                 final Collection<Long> uids = new TreeSet<Long>();
 
-                final List<MailboxMembership<Id>> members = messageMapper.findMarkedForDeletionInMailbox(mailbox, set);
+                final List<MailboxMembership<Id>> members = messageMapper.findMarkedForDeletionInMailbox(getMailboxEntity(), range);
                 for (MailboxMembership<Id> message:members) {
                     uids.add(message.getUid());
-                    messageMapper.delete(mailbox, message);
+                    messageMapper.delete(getMailboxEntity(), message);
                     
                 }  
-                return uids;
+                return uids.iterator();
             }
             
-        });
-        
-        Iterator<Long> uidIt = uids.iterator();
-        while(uidIt.hasNext()) {
-            dispatcher.expunged(uidIt.next(), mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()));
-        }
-        return uids.iterator();    
+        });       
     }
 
+
+    
     /*
      * (non-Javadoc)
-     * @see org.apache.james.imap.mailbox.Mailbox#setFlags(javax.mail.Flags, boolean, boolean, org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.mailbox.MailboxSession)
+     * @see org.apache.james.imap.store.AbstractStoreMessageManager#updateFlags(javax.mail.Flags, boolean, boolean, org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.mailbox.MailboxSession)
      */
-    public Map<Long, Flags> setFlags(final Flags flags, final boolean value, final boolean replace,
+    public Iterator<UpdatedFlag> updateFlags(final Flags flags, final boolean value, final boolean replace,
             final MessageRange set, MailboxSession mailboxSession) throws MailboxException {
-    	final SortedMap<Long, Flags> newFlagsByUid = new TreeMap<Long, Flags>();
-        final Map<Long, Flags> originalFlagsByUid = new HashMap<Long, Flags>(INITIAL_SIZE_FLAGS);
-        messageMapper.execute(new TransactionalMapper.VoidTransaction(){
+        final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(mailboxSession);
+
+        return messageMapper.execute(new TransactionalMapper.Transaction<Iterator<UpdatedFlag>>(){
+
+            public Iterator<UpdatedFlag> run() throws MailboxException {
+                final List<UpdatedFlag> updatedFlags = new ArrayList<UpdatedFlag>();
 
-            public void runVoid() throws MailboxException {
-                final List<MailboxMembership<Id>> members = messageMapper.findInMailbox(mailbox, set);
+                final List<MailboxMembership<Id>> members = messageMapper.findInMailbox(getMailboxEntity(), set);
                 for (final MailboxMembership<Id> member:members) {
-                    originalFlagsByUid.put(member.getUid(), member.createFlags());
+                    Flags originalFlags = member.createFlags();
                     if (replace) {
                         member.setFlags(flags);
                     } else {
@@ -451,119 +177,88 @@
                         }
                         member.setFlags(current);
                     }
-                    newFlagsByUid.put(member.getUid(), member.createFlags());
-                    messageMapper.save(mailbox, member);
+                    Flags newFlags = member.createFlags();
+                    messageMapper.save(getMailboxEntity(), member);
+                    updatedFlags.add(new UpdatedFlag(member.getUid(),originalFlags, newFlags));
                 }
+                
+                return updatedFlags.iterator();
             }
             
         });
        
-        Iterator<Long> it = newFlagsByUid.keySet().iterator();
-        while (it.hasNext()) {
-            Long uid = it.next();
-            dispatcher.flagsUpdated(uid, mailboxSession.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()), originalFlagsByUid.get(uid), newFlagsByUid.get(uid));
-
-        }
-        return newFlagsByUid;
     }
 
 
-    public void addListener(MailboxListener listener) throws MailboxException {
-        dispatcher.addMailboxListener(listener);
-    }
 
-    private long getUidNext(MailboxSession mailboxSession) throws MailboxException {
-        Mailbox<Id> mailbox = getMailboxEntity();
-        if (mailbox == null) {
-            throw new MailboxNotFoundException("Mailbox has been deleted");
-        } else {
-            final long lastUid = mailbox.getLastUid();
-            return lastUid + 1;
-        }
-    }
 
     /*
      * (non-Javadoc)
      * @see org.apache.james.imap.mailbox.Mailbox#search(org.apache.james.imap.mailbox.SearchQuery, org.apache.james.imap.mailbox.MailboxSession)
      */
     public Iterator<Long> search(SearchQuery query, MailboxSession mailboxSession) throws MailboxException {
-        return messageMapper.searchMailbox(mailbox, query);    
+        return mapperFactory.getMessageMapper(mailboxSession).searchMailbox(getMailboxEntity(), query);    
     }
 
-    /**
-     * This mailbox is writable
-     */
-    public boolean isWriteable(MailboxSession session) {
-        return true;
-    }
 
-    private void copy(final List<MailboxMembership<Id>> originalRows, final MailboxSession session) throws MailboxException {
+    private Iterator<Long> copy(final List<MailboxMembership<Id>> originalRows, final MailboxSession session) throws MailboxException {
         try {
             final List<Long> copiedRows = new ArrayList<Long>();
+            final MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
+
             for (final MailboxMembership<Id> originalMessage:originalRows) {
                copiedRows.add(messageMapper.execute(new TransactionalMapper.Transaction<Long>() {
 
                     public Long run() throws MailboxException {
 
-                        return messageMapper.copy(mailbox, originalMessage);
+                        return messageMapper.copy(getMailboxEntity(), originalMessage);
                         
                     }
                     
                 }));
             }
-            // Wait until commit before issuing events
-            for (Long newMember:copiedRows) {
-                dispatcher.added(newMember, session.getSessionId(), new StoreMailboxPath<Id>(getMailboxEntity()));
-            }
+            return copiedRows.iterator();
         } catch (MessagingException e) {
             throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
         }
     }
-
-    /**
-     * Copy the {@link MessageSet} to the {@link StoreMessageManager}
-     * 
-     * @param set
-     * @param toMailbox
-     * @param session
-     * @throws MailboxException
+    
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.store.AbstractStoreMessageManager#copy(org.apache.james.imap.mailbox.MessageRange, org.apache.james.imap.store.AbstractStoreMessageManager, org.apache.james.imap.mailbox.MailboxSession)
      */
-    public void copyTo(MessageRange set, StoreMessageManager<Id> toMailbox, MailboxSession session) throws MailboxException {
+    protected Iterator<Long> copy(MessageRange set, AbstractStoreMessageManager<Id> toMailbox, MailboxSession session) throws MailboxException {
+        StoreMessageManager<Id> to = (StoreMessageManager<Id>) toMailbox;
         try {
-            final List<MailboxMembership<Id>> originalRows = messageMapper.findInMailbox(mailbox, set);
-            toMailbox.copy(originalRows, session);
+            MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
+            final List<MailboxMembership<Id>> originalRows = messageMapper.findInMailbox(getMailboxEntity(), set);
+            return to.copy(originalRows, session);
 
         } catch (MessagingException e) {
             throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
         }
     }
-    
-    /**
-     * @see {@link Mailbox#getMetaData(boolean, MailboxSession, FetchGroup)}
+
+
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.store.AbstractStoreMessageManager#countUnseenMessagesInMailbox(org.apache.james.imap.mailbox.MailboxSession)
      */
-    public MetaData getMetaData(boolean resetRecent, MailboxSession mailboxSession, 
-            org.apache.james.imap.mailbox.Mailbox.MetaData.FetchGroup fetchGroup) throws MailboxException {
-        final List<Long> recent = recent(resetRecent, mailboxSession);
-        final Flags permanentFlags = getPermanentFlags();
-        final long uidValidity = getMailboxEntity().getUidValidity();
-        final long uidNext = getUidNext(mailboxSession);
-        final long messageCount = getMessageCount(mailboxSession);
-        final long unseenCount;
-        final Long firstUnseen;
-        switch (fetchGroup) {
-            case UNSEEN_COUNT:
-                unseenCount = getMessageMapper().countUnseenMessagesInMailbox(mailbox);
-                firstUnseen = null;
-                break;
-            case FIRST_UNSEEN:
-                firstUnseen = getMessageMapper().findFirstUnseenMessageUid(mailbox);
-                unseenCount = 0;
-                break;
-            default:
-                firstUnseen = null;
-                unseenCount = 0;
-                break;
-        }
-        return new MailboxMetaData(recent, permanentFlags, uidValidity, uidNext, messageCount, unseenCount, firstUnseen, isWriteable(mailboxSession));
+    protected long countUnseenMessagesInMailbox(MailboxSession session) throws MailboxException {
+        MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
+        return messageMapper.countUnseenMessagesInMailbox(getMailboxEntity());
     }
+
+
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.store.AbstractStoreMessageManager#findFirstUnseenMessageUid(org.apache.james.imap.mailbox.MailboxSession)
+     */
+    protected Long findFirstUnseenMessageUid(MailboxSession session) throws MailboxException{
+        MessageMapper<Id> messageMapper = mapperFactory.getMessageMapper(session);
+        return messageMapper.findFirstUnseenMessageUid(getMailboxEntity());
+    }
+
 }
\ No newline at end of file
Index: maildir/src/main/java/org/apache/james/imap/maildir/MaildirMailboxManager.java
===================================================================
--- maildir/src/main/java/org/apache/james/imap/maildir/MaildirMailboxManager.java	(revision 986087)
+++ maildir/src/main/java/org/apache/james/imap/maildir/MaildirMailboxManager.java	(working copy)
@@ -42,7 +42,7 @@
     @Override
     protected StoreMessageManager<Integer> createMessageManager(MailboxEventDispatcher dispatcher,
             Mailbox<Integer> mailboxEntiy, MailboxSession session) throws MailboxException {
-        return new MaildirMessageManager(mailboxSessionMapperFactory, dispatcher, mailboxEntiy, session);
+        return new MaildirMessageManager(mailboxSessionMapperFactory, dispatcher, mailboxEntiy);
     }
 
     @Override
Index: store/src/main/java/org/apache/james/imap/store/UpdatedFlag.java
===================================================================
--- store/src/main/java/org/apache/james/imap/store/UpdatedFlag.java	(revision 0)
+++ store/src/main/java/org/apache/james/imap/store/UpdatedFlag.java	(revision 0)
@@ -0,0 +1,46 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.imap.store;
+
+import javax.mail.Flags;
+
+public class UpdatedFlag {
+
+    private final long uid;
+    private final Flags oldFlags;
+    private final Flags newFlags;
+
+    public UpdatedFlag(long uid, Flags oldFlags, Flags newFlags) {
+       this.uid = uid;
+       this.oldFlags = oldFlags;
+       this.newFlags = newFlags;
+    }
+    
+    public Flags getOldFlags() {
+        return oldFlags;
+    }
+    
+    public Flags getNewFlags() {
+        return newFlags;
+    }
+    
+    public long getUid() {
+        return uid;
+    }
+}
Index: maildir/src/main/java/org/apache/james/imap/maildir/MaildirMessageManager.java
===================================================================
--- maildir/src/main/java/org/apache/james/imap/maildir/MaildirMessageManager.java	(revision 986087)
+++ maildir/src/main/java/org/apache/james/imap/maildir/MaildirMessageManager.java	(working copy)
@@ -26,7 +26,6 @@
 import javax.mail.Flags;
 
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.maildir.mail.model.MaildirHeader;
 import org.apache.james.imap.maildir.mail.model.MaildirMessage;
@@ -40,9 +39,9 @@
 public class MaildirMessageManager extends StoreMessageManager<Integer> {
 
     public MaildirMessageManager(MailboxSessionMapperFactory<Integer> mapperFactory,
-            MailboxEventDispatcher dispatcher, Mailbox<Integer> mailboxEntiy, MailboxSession session)
+            MailboxEventDispatcher dispatcher, Mailbox<Integer> mailboxEntiy)
     throws MailboxException {
-        super(mapperFactory, dispatcher, mailboxEntiy, session);
+        super(mapperFactory, dispatcher, mailboxEntiy);
     }
 
     @Override
@@ -59,7 +58,7 @@
         for (Header header: headers) {
             maildirHeaders.add((MaildirHeader) header);
         }
-        final MailboxMembership<Integer> message = new MaildirMessage(mailbox, internalDate, 
+        final MailboxMembership<Integer> message = new MaildirMessage(getMailboxEntity(), internalDate, 
                 size, flags, documentIn, bodyStartOctet, maildirHeaders, propertyBuilder);
         return message;
     }
Index: memory/src/main/java/org/apache/james/imap/inmemory/InMemoryStoreMessageManager.java
===================================================================
--- memory/src/main/java/org/apache/james/imap/inmemory/InMemoryStoreMessageManager.java	(revision 984470)
+++ memory/src/main/java/org/apache/james/imap/inmemory/InMemoryStoreMessageManager.java	(working copy)
@@ -30,7 +30,6 @@
 import org.apache.james.imap.inmemory.mail.model.SimpleHeader;
 import org.apache.james.imap.inmemory.mail.model.SimpleMailboxMembership;
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.store.MailboxSessionMapperFactory;
 import org.apache.james.imap.store.StoreMessageManager;
@@ -41,9 +40,8 @@
 public class InMemoryStoreMessageManager extends StoreMessageManager<Long> {
 
     public InMemoryStoreMessageManager(MailboxSessionMapperFactory<Long> mapperFactory,
-            MailboxEventDispatcher dispatcher, InMemoryMailbox mailbox,
-            MailboxSession session) throws MailboxException {
-        super(mapperFactory, dispatcher,mailbox, session);
+            MailboxEventDispatcher dispatcher, InMemoryMailbox mailbox) throws MailboxException {
+        super(mapperFactory, dispatcher,mailbox);
     }
     
     @Override
@@ -53,7 +51,7 @@
 
     @Override
     protected MailboxMembership<Long> createMessage(Date internalDate, int size, int bodyStartOctet, 
-            InputStream  document, Flags flags, List<Header> headers, PropertyBuilder propertyBuilder) {
+            InputStream  document, Flags flags, List<Header> headers, PropertyBuilder propertyBuilder) throws MailboxException {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         byte[] byteContent;
         try {
@@ -70,6 +68,7 @@
             e.printStackTrace();
             byteContent = new byte[0];
         }
+        InMemoryMailbox mailbox = (InMemoryMailbox) getMailboxEntity();
         ((InMemoryMailbox) mailbox).consumeUid();
         return new SimpleMailboxMembership(internalDate, mailbox.getLastUid(), size, bodyStartOctet, byteContent, flags, headers, propertyBuilder, mailbox.getMailboxId());
     }
Index: memory/src/main/java/org/apache/james/imap/inmemory/InMemoryMailboxManager.java
===================================================================
--- memory/src/main/java/org/apache/james/imap/inmemory/InMemoryMailboxManager.java	(revision 984470)
+++ memory/src/main/java/org/apache/james/imap/inmemory/InMemoryMailboxManager.java	(working copy)
@@ -40,7 +40,7 @@
 
     @Override
     protected StoreMessageManager<Long> createMessageManager(MailboxEventDispatcher dispatcher, Mailbox<Long> mailboxRow, MailboxSession session) throws MailboxException {
-        return new InMemoryStoreMessageManager(mailboxSessionMapperFactory, dispatcher, (InMemoryMailbox)mailboxRow, session);
+        return new InMemoryStoreMessageManager(mailboxSessionMapperFactory, dispatcher, (InMemoryMailbox)mailboxRow);
     }
 
     @Override
Index: jcr/src/main/java/org/apache/james/imap/jcr/JCRMessageManager.java
===================================================================
--- jcr/src/main/java/org/apache/james/imap/jcr/JCRMessageManager.java	(revision 984470)
+++ jcr/src/main/java/org/apache/james/imap/jcr/JCRMessageManager.java	(working copy)
@@ -30,7 +30,6 @@
 import org.apache.james.imap.jcr.mail.model.JCRMailbox;
 import org.apache.james.imap.jcr.mail.model.JCRMessage;
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.store.StoreMessageManager;
 import org.apache.james.imap.store.mail.model.Header;
@@ -46,8 +45,8 @@
     private final Log log;
 
     public JCRMessageManager(JCRMailboxSessionMapperFactory mapperFactory,
-            final MailboxEventDispatcher dispatcher, final JCRMailbox mailbox, final Log log, final char delimiter, MailboxSession session) throws MailboxException {
-        super(mapperFactory, dispatcher, mailbox, session);
+            final MailboxEventDispatcher dispatcher, final JCRMailbox mailbox, final Log log, final char delimiter) throws MailboxException {
+        super(mapperFactory, dispatcher, mailbox);
         this.log = log;
     }
 
@@ -57,12 +56,12 @@
     }
 
     @Override
-    protected MailboxMembership<String> createMessage(Date internalDate, int size, int bodyStartOctet, InputStream document, Flags flags, List<Header> headers, PropertyBuilder propertyBuilder) {
+    protected MailboxMembership<String> createMessage(Date internalDate, int size, int bodyStartOctet, InputStream document, Flags flags, List<Header> headers, PropertyBuilder propertyBuilder) throws MailboxException{
         final List<JCRHeader> jcrHeaders = new ArrayList<JCRHeader>(headers.size());
         for (Header header: headers) {
             jcrHeaders.add((JCRHeader) header);
         }
-        final MailboxMembership<String> message = new JCRMessage(mailbox.getMailboxId(), internalDate, 
+        final MailboxMembership<String> message = new JCRMessage(getMailboxEntity().getMailboxId(), internalDate, 
                 size, flags, document, bodyStartOctet, jcrHeaders, propertyBuilder, log);
         return message;
     }
Index: jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMailboxManager.java
===================================================================
--- jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMailboxManager.java	(revision 984470)
+++ jpa/src/main/java/org/apache/james/imap/jpa/openjpa/OpenJPAMailboxManager.java	(working copy)
@@ -49,7 +49,7 @@
 
     @Override
     protected StoreMessageManager<Long> createMessageManager(MailboxEventDispatcher dispatcher, Mailbox<Long> mailboxRow, MailboxSession session) throws MailboxException {
-        StoreMessageManager<Long> result =  new OpenJPAMessageManager((JPAMailboxSessionMapperFactory) mailboxSessionMapperFactory, dispatcher, mailboxRow, session, useStreaming);
+        StoreMessageManager<Long> result =  new OpenJPAMessageManager((JPAMailboxSessionMapperFactory) mailboxSessionMapperFactory, dispatcher, mailboxRow, useStreaming);
         return result;
     }
 }
Index: jpa/src/main/java/org/apache/james/imap/jpa/JPAMessageManager.java
===================================================================
--- jpa/src/main/java/org/apache/james/imap/jpa/JPAMessageManager.java	(revision 984470)
+++ jpa/src/main/java/org/apache/james/imap/jpa/JPAMessageManager.java	(working copy)
@@ -28,7 +28,6 @@
 import org.apache.james.imap.jpa.mail.model.JPAHeader;
 import org.apache.james.imap.jpa.mail.model.openjpa.JPAMailboxMembership;
 import org.apache.james.imap.mailbox.MailboxException;
-import org.apache.james.imap.mailbox.MailboxSession;
 import org.apache.james.imap.mailbox.util.MailboxEventDispatcher;
 import org.apache.james.imap.store.StoreMessageManager;
 import org.apache.james.imap.store.mail.model.Header;
@@ -42,8 +41,8 @@
 public class JPAMessageManager extends StoreMessageManager<Long> {
     
     public JPAMessageManager(JPAMailboxSessionMapperFactory mapperFactory,
-            final MailboxEventDispatcher dispatcher,final Mailbox<Long> mailbox, MailboxSession session) throws MailboxException {
-        super(mapperFactory, dispatcher, mailbox, session);     
+            final MailboxEventDispatcher dispatcher,final Mailbox<Long> mailbox) throws MailboxException {
+        super(mapperFactory, dispatcher, mailbox);     
     }
     
     @Override
@@ -53,7 +52,7 @@
         for (Header header: headers) {
             jpaHeaders.add((JPAHeader) header);
         }
-        final MailboxMembership<Long> message = new JPAMailboxMembership(mailbox.getMailboxId(), internalDate, size, flags, document, bodyStartOctet, jpaHeaders, propertyBuilder);
+        final MailboxMembership<Long> message = new JPAMailboxMembership(getMailboxEntity().getMailboxId(), internalDate, size, flags, document, bodyStartOctet, jpaHeaders, propertyBuilder);
         return message;
     }
 
