Author: norman
Date: Wed Nov 17 17:26:17 2010
New Revision: 1036124

URL: http://svn.apache.org/viewvc?rev=1036124&view=rev
Log:
Only parse Message if really needed in Maildir, the same is true for opening 
inputstreams. See IMAP-227

Added:
    
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/AbstractMaildirMessage.java
    
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/LazyLoadingMaildirMessage.java
Modified:
    
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
    
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMessage.java

Modified: 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java?rev=1036124&r1=1036123&r2=1036124&view=diff
==============================================================================
--- 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
 (original)
+++ 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/MaildirMessageMapper.java
 Wed Nov 17 17:26:17 2010
@@ -19,47 +19,36 @@
 package org.apache.james.mailbox.maildir.mail;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PushbackInputStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.SortedMap;
 import java.util.Map.Entry;
-
-import javax.mail.util.SharedFileInputStream;
+import java.util.SortedMap;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.james.mailbox.MailboxException;
 import org.apache.james.mailbox.MessageRange;
-import org.apache.james.mailbox.SearchQuery;
 import org.apache.james.mailbox.MessageRange.Type;
+import org.apache.james.mailbox.SearchQuery;
 import org.apache.james.mailbox.SearchQuery.Criterion;
 import org.apache.james.mailbox.SearchQuery.NumericRange;
 import org.apache.james.mailbox.maildir.MaildirFolder;
 import org.apache.james.mailbox.maildir.MaildirMessageName;
 import org.apache.james.mailbox.maildir.MaildirStore;
 import org.apache.james.mailbox.maildir.UidConstraint;
-import org.apache.james.mailbox.maildir.mail.model.MaildirHeader;
+import org.apache.james.mailbox.maildir.mail.model.AbstractMaildirMessage;
+import org.apache.james.mailbox.maildir.mail.model.LazyLoadingMaildirMessage;
 import org.apache.james.mailbox.maildir.mail.model.MaildirMessage;
 import org.apache.james.mailbox.store.SearchQueryIterator;
 import org.apache.james.mailbox.store.mail.MessageMapper;
-import org.apache.james.mailbox.store.mail.model.Header;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxMembership;
-import org.apache.james.mailbox.store.mail.model.PropertyBuilder;
-import org.apache.james.mailbox.store.streaming.ConfigurableMimeTokenStream;
-import org.apache.james.mailbox.store.streaming.CountingInputStream;
 import org.apache.james.mailbox.store.transaction.NonTransactionalMapper;
-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;
 
 public class MaildirMessageMapper extends NonTransactionalMapper implements 
MessageMapper<Integer> {
 
@@ -77,7 +66,7 @@ public class MaildirMessageMapper extend
      */
     public long copy(Mailbox<Integer> mailbox, MailboxMembership<Integer> 
original)
     throws MailboxException {
-        MaildirMessage theCopy = new MaildirMessage(mailbox, (MaildirMessage) 
original);
+        MaildirMessage theCopy = new MaildirMessage(mailbox, 
(AbstractMaildirMessage) original);
 
         return save(mailbox, theCopy);
     }
@@ -168,8 +157,7 @@ public class MaildirMessageMapper extend
         }
         ArrayList<MailboxMembership<Integer>> messages = new 
ArrayList<MailboxMembership<Integer>>();
         if (messageName != null) {
-            MaildirMessage message = loadMessage(mailbox, messageName, uid);
-            messages.add(message);
+            messages.add(new LazyLoadingMaildirMessage(mailbox, uid, 
messageName));
         }
         return messages;
     }
@@ -188,7 +176,7 @@ public class MaildirMessageMapper extend
         }
         ArrayList<MailboxMembership<Integer>> messages = new 
ArrayList<MailboxMembership<Integer>>();
         for (Entry<Long, MaildirMessageName> entry : uidMap.entrySet()) {
-            messages.add(loadMessage(mailbox, entry.getValue(), 
entry.getKey()));
+            messages.add(new LazyLoadingMaildirMessage(mailbox, 
entry.getKey(), entry.getValue()));
         }
         return messages;
     }
@@ -231,7 +219,7 @@ public class MaildirMessageMapper extend
         }
         ArrayList<MailboxMembership<Integer>> filtered = new 
ArrayList<MailboxMembership<Integer>>(uidMap.size());
         for (Entry<Long, MaildirMessageName> entry : uidMap.entrySet())
-            filtered.add(loadMessage(mailbox, entry.getValue(), 
entry.getKey()));
+            filtered.add(new LazyLoadingMaildirMessage(mailbox, 
entry.getKey(), entry.getValue()));
         return filtered;
     }
 
@@ -246,8 +234,7 @@ public class MaildirMessageMapper extend
         }
         ArrayList<MailboxMembership<Integer>> messages = new 
ArrayList<MailboxMembership<Integer>>();
         if (MaildirMessageName.FILTER_DELETED_MESSAGES.accept(null, 
messageName.getFullName())) {
-            MaildirMessage message = loadMessage(mailbox, messageName, uid);
-            messages.add(message);
+            messages.add(new LazyLoadingMaildirMessage(mailbox, uid, 
messageName));
         }
         return messages;
     }
@@ -267,7 +254,7 @@ public class MaildirMessageMapper extend
         }
         List<MailboxMembership<Integer>> recentMessages = new 
ArrayList<MailboxMembership<Integer>>(recentMessageNames.size());
         for (Entry<Long, MaildirMessageName> entry : 
recentMessageNames.entrySet())
-            recentMessages.add(loadMessage(mailbox, entry.getValue(), 
entry.getKey()));
+            recentMessages.add(new LazyLoadingMaildirMessage(mailbox, 
entry.getKey(), entry.getValue()));
         return recentMessages;
     }
 
@@ -292,7 +279,7 @@ public class MaildirMessageMapper extend
      */
     public long save(Mailbox<Integer> mailbox, MailboxMembership<Integer> 
message)
     throws MailboxException {
-        MaildirMessage maildirMessage = (MaildirMessage) message;
+        AbstractMaildirMessage maildirMessage = (AbstractMaildirMessage) 
message;
         MaildirFolder folder = maildirStore.createMaildirFolder(mailbox);
         long uid = 0;
         // a new message
@@ -422,7 +409,7 @@ public class MaildirMessageMapper extend
             //System.out.println("check " + entry.getKey());
             if (constraint.isAllowed(entry.getKey())) {
                 //System.out.println("allow " + entry.getKey());
-                messages.add(loadMessage(mailbox, entry.getValue(), 
entry.getKey()));
+                messages.add(new LazyLoadingMaildirMessage(mailbox, 
entry.getKey(), entry.getValue()));
                 // Check if we only need to fetch 1 message, if so we can set 
a limit to speed up things
                 if (rangeLength == 1 && range == false) break;
             }
@@ -439,199 +426,5 @@ public class MaildirMessageMapper extend
         
     }
     
-    /**
-     * Creates a {...@link MaildirMessage} object with data loaded from the 
repository.
-     * @param mailbox The mailbox which the message resides in
-     * @param messageName The name of the message
-     * @return the {...@link MaildirMessage} filled with data from the 
respective file
-     * @throws MailboxException if there was an error parsing the message or 
the message could not be found
-     */
-    private MaildirMessage loadMessage(Mailbox<Integer> mailbox, 
MaildirMessageName messageName, Long uid)
-    throws MailboxException {
-        MaildirMessage message = null;
-        try {
-            File messageFile = messageName.getFile();
-            message = parseMessage(messageFile, mailbox);
-        } catch (IOException e) {
-            throw new MailboxException("Parsing of message failed in Mailbox " 
+ mailbox, e );
-        }
-        message.setFlags(messageName.getFlags());
-        message.setInternalDate(messageName.getInternalDate());
-        message.setUid(uid);
-        return message;
-    }
-    
-    
/******************************************************************************
-     * Until there is a database for properties, mail needs to be parse 
exactlty as
-     * it is done when appending. Hence, this part below is basically a copy of
-     * some parts of {...@link StoreMessageManager}.
-     * TODO: This code below needs to be extracted to some common method(s)!
-     */
-
-    /**
-     * The initial size of the headers list
-     */
-    private static final int INITIAL_SIZE_HEADERS = 32;
-    
-    /**
-     * Read a message file and extract the meta data:
-     *   - size
-     *   - headers
-     *   - media type
-     *   - content properties
-     *   - charset
-     *   - boundary
-     *   - line count
-     * It is missing the flags and internalDate
-     * @param messageFile
-     * @param mailbox
-     * @return
-     * @throws IOException
-     */
-    private MaildirMessage parseMessage(File messageFile, Mailbox<Integer> 
mailbox) throws IOException {
-        SharedFileInputStream tmpMsgIn = null;
-        try {
-            tmpMsgIn = new SharedFileInputStream(messageFile);
-
-            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);
-                    }
-                    headers.add(new MaildirHeader(lineNumber, 
parser.getField().getName(), fieldValue));
-                }
-                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);
-            }
-            FileInputStream documentIn = new FileInputStream(messageFile);
-            final List<MaildirHeader> maildirHeaders = new 
ArrayList<MaildirHeader>(headers.size());
-            for (Header header: headers) {
-                maildirHeaders.add((MaildirHeader) header);
-            }
-            return new MaildirMessage(mailbox, size, documentIn, 
bodyStartOctet, maildirHeaders, propertyBuilder);
-        }
-        catch (MimeException e) {
-            // has successfully been parsen when appending, shouldn't give any 
problems
-        }
-        finally {
-            if (tmpMsgIn != null) {
-                try {
-                    tmpMsgIn.close();
-                } catch (IOException e) {
-                    // ignore on close
-                }
-            }
-        }
-        return null;
-    }
-    
-    /**
-     * 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;
-    }
-
+  
 }

Added: 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/AbstractMaildirMessage.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/AbstractMaildirMessage.java?rev=1036124&view=auto
==============================================================================
--- 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/AbstractMaildirMessage.java
 (added)
+++ 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/AbstractMaildirMessage.java
 Wed Nov 17 17:26:17 2010
@@ -0,0 +1,210 @@
+/****************************************************************
+ * 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.maildir.mail.model;
+
+import javax.mail.Flags;
+
+import org.apache.james.mailbox.maildir.MaildirMessageName;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+import org.apache.james.mailbox.store.mail.model.MailboxMembership;
+import org.apache.james.mailbox.store.mail.model.Message;
+
+public abstract class AbstractMaildirMessage implements Message, 
MailboxMembership<Integer>{
+
+    
+
+    protected boolean answered;
+    protected boolean deleted;
+    protected boolean draft;
+    protected boolean flagged;
+    protected boolean recent;
+    protected boolean seen;
+    private Mailbox<Integer> mailbox;
+    private long uid;
+    protected boolean newMessage;
+
+    public AbstractMaildirMessage(Mailbox<Integer> mailbox) {
+        this.mailbox = mailbox;
+    }
+    
+    public Integer getMailboxId() {
+        return mailbox.getMailboxId();
+    }
+
+    public long getUid() {
+        return uid;
+    }
+
+    public void setUid(long uid) {
+        this.uid = uid;
+    }
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#setFlags(
+     * javax.mail.Flags)
+     */
+    public void setFlags(Flags flags) {
+        if (flags != null) {
+            answered = flags.contains(Flags.Flag.ANSWERED);
+            deleted = flags.contains(Flags.Flag.DELETED);
+            draft = flags.contains(Flags.Flag.DRAFT);
+            flagged = flags.contains(Flags.Flag.FLAGGED);
+            recent = flags.contains(Flags.Flag.RECENT);
+            seen = flags.contains(Flags.Flag.SEEN);
+        }
+    }
+    
+
+    public Message getMessage() {
+        return this;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isAnswered()
+     */
+    public boolean isAnswered() {
+        return answered;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isDeleted()
+     */
+    public boolean isDeleted() {
+        return deleted;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isDraft()
+     */
+    public boolean isDraft() {
+        return draft;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isFlagged()
+     */
+    public boolean isFlagged() {
+        return flagged;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.james.mailbox.store.mail.model.MailboxMembership#isRecent()
+     */
+    public boolean isRecent() {
+        return recent;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isSeen()
+     */
+    public boolean isSeen() {
+        return seen;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * 
org.apache.james.mailbox.store.mail.model.MailboxMembership#unsetRecent()
+     */
+    public void unsetRecent() {
+        recent = false;
+    }
+
+    
+    /* 
+     * (non-Javadoc)
+     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#createFlags()
+     */
+    public Flags createFlags() {
+        final Flags flags = new Flags();
+
+        if (isAnswered()) {
+            flags.add(Flags.Flag.ANSWERED);
+        }
+        if (isDeleted()) {
+            flags.add(Flags.Flag.DELETED);
+        }
+        if (isDraft()) {
+            flags.add(Flags.Flag.DRAFT);
+        }
+        if (isFlagged()) {
+            flags.add(Flags.Flag.FLAGGED);
+        }
+        if (isRecent()) {
+            flags.add(Flags.Flag.RECENT);
+        }
+        if (isSeen()) {
+            flags.add(Flags.Flag.SEEN);
+        }
+        return flags;
+    }
+    
+    
+    /**
+     * Indicates whether this MaildirMessage reflects a new message or one 
that already
+     * exists in the file system.
+     * @return true if it is new, false if it already exists
+     */
+    public boolean isNew() {
+        return newMessage;
+    }
+    
+    
+    @Override
+    public String toString() {
+        StringBuffer theString = new StringBuffer("MaildirMessage ");
+        theString.append(getUid());
+        theString.append(" {");
+        Flags flags = createFlags();
+        if (flags.contains(Flags.Flag.DRAFT))
+            theString.append(MaildirMessageName.FLAG_DRAFT);
+        if (flags.contains(Flags.Flag.FLAGGED))
+            theString.append(MaildirMessageName.FLAG_FLAGGED);
+        if (flags.contains(Flags.Flag.ANSWERED))
+            theString.append(MaildirMessageName.FLAG_ANSWERD);
+        if (flags.contains(Flags.Flag.SEEN))
+            theString.append(MaildirMessageName.FLAG_SEEN);
+        if (flags.contains(Flags.Flag.DELETED))
+            theString.append(MaildirMessageName.FLAG_DELETED);
+        theString.append("} ");
+        theString.append(getInternalDate());
+        return theString.toString();
+    }
+
+}

Added: 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/LazyLoadingMaildirMessage.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/LazyLoadingMaildirMessage.java?rev=1036124&view=auto
==============================================================================
--- 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/LazyLoadingMaildirMessage.java
 (added)
+++ 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/LazyLoadingMaildirMessage.java
 Wed Nov 17 17:26:17 2010
@@ -0,0 +1,305 @@
+/****************************************************************
+ * 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.maildir.mail.model;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.mail.util.SharedFileInputStream;
+
+import org.apache.james.mailbox.maildir.MaildirMessageName;
+import org.apache.james.mailbox.store.mail.model.Header;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+import org.apache.james.mailbox.store.mail.model.Property;
+import org.apache.james.mailbox.store.mail.model.PropertyBuilder;
+import org.apache.james.mailbox.store.streaming.ConfigurableMimeTokenStream;
+import org.apache.james.mailbox.store.streaming.CountingInputStream;
+import org.apache.james.mailbox.store.streaming.RewindableInputStream;
+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;
+
+public class LazyLoadingMaildirMessage extends AbstractMaildirMessage {
+
+    private MaildirMessageName messageName;
+    private int bodyStartOctet;
+    private final PropertyBuilder propertyBuilder = new PropertyBuilder();
+    private final List<Header> headers = new ArrayList<Header>();
+
+    private boolean parsed;
+
+    public LazyLoadingMaildirMessage(Mailbox<Integer> mailbox, long uid, 
MaildirMessageName messageName) {
+        super(mailbox);
+        setUid(uid);
+        setFlags(messageName.getFlags());
+        this.messageName = messageName;
+    }
+
+    /**
+     * Parse message if needed
+     */
+    private synchronized void parseMessage() {
+        if (parsed)
+            return;
+        SharedFileInputStream tmpMsgIn = null;
+        try {
+            tmpMsgIn = new SharedFileInputStream(messageName.getFile());
+
+            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));
+
+            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);
+                    }
+                    headers.add(new MaildirHeader(lineNumber, 
parser.getField().getName(), fieldValue));
+                }
+                next = parser.next();
+            }
+            final MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) 
parser.getBodyDescriptor();
+            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);
+            }
+        } catch (IOException e) {
+            // has successfully been parsen when appending, shouldn't give any
+            // problems
+        } catch (MimeException e) {
+            // has successfully been parsen when appending, shouldn't give any
+            // problems
+        } finally {
+            if (tmpMsgIn != null) {
+                try {
+                    tmpMsgIn.close();
+                } catch (IOException e) {
+                    // ignore on close
+                }
+            }
+            parsed = true;
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getMediaType()
+     */
+    public String getMediaType() {
+        parseMessage();
+        return propertyBuilder.getMediaType();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getSubType()
+     */
+    public String getSubType() {
+        parseMessage();
+        return propertyBuilder.getSubType();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see 
org.apache.james.mailbox.store.mail.model.Message#getFullContentOctets()
+     */
+    public long getFullContentOctets() {
+        return messageName.getSize();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see 
org.apache.james.mailbox.store.mail.model.Message#getTextualLineCount()
+     */
+    public Long getTextualLineCount() {
+        parseMessage();
+        return propertyBuilder.getTextualLineCount();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getHeaders()
+     */
+    public List<Header> getHeaders() {
+        parseMessage();
+
+        return headers;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getProperties()
+     */
+    public List<Property> getProperties() {
+        parseMessage();
+        return propertyBuilder.toProperties();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#getInternalDate()
+     */
+    public Date getInternalDate() {
+        return messageName.getInternalDate();
+    }
+  
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getBodyOctets()
+     */
+    public long getBodyOctets() {
+        parseMessage();
+        return getFullContentOctets() - bodyStartOctet;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getFullContent()
+     */
+    public RewindableInputStream getFullContent() throws IOException {
+        return new MyRewindableInputStream(new 
SharedFileInputStream(messageName.getFile()));
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.mailbox.store.mail.model.Message#getBodyContent()
+     */
+    public RewindableInputStream getBodyContent() throws IOException {
+        parseMessage();
+
+        SharedFileInputStream in = new 
SharedFileInputStream(messageName.getFile());
+        return new MyRewindableInputStream(in.newStream(bodyStartOctet, -1));
+    }
+    
+    private final class MyRewindableInputStream extends RewindableInputStream {
+
+        protected MyRewindableInputStream(InputStream in) {
+            super(in);
+            
+        }
+
+        @Override
+        protected void rewindIfNeeded() throws IOException {
+            in.reset();
+        }
+        
+    }
+}

Modified: 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMessage.java
URL: 
http://svn.apache.org/viewvc/james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMessage.java?rev=1036124&r1=1036123&r2=1036124&view=diff
==============================================================================
--- 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMessage.java
 (original)
+++ 
james/imap/trunk/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMessage.java
 Wed Nov 17 17:26:17 2010
@@ -29,17 +29,16 @@ import javax.mail.Flags;
 
 import org.apache.commons.lang.NotImplementedException;
 import org.apache.james.mailbox.MailboxException;
-import org.apache.james.mailbox.maildir.MaildirMessageName;
-import org.apache.james.mailbox.store.mail.model.AbstractMessage;
 import org.apache.james.mailbox.store.mail.model.Header;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
-import org.apache.james.mailbox.store.mail.model.MailboxMembership;
 import org.apache.james.mailbox.store.mail.model.Message;
 import org.apache.james.mailbox.store.mail.model.Property;
 import org.apache.james.mailbox.store.mail.model.PropertyBuilder;
+import org.apache.james.mailbox.store.streaming.LazySkippingInputStream;
+import org.apache.james.mailbox.store.streaming.RewindableInputStream;
 import org.apache.james.mailbox.store.streaming.StreamUtils;
 
-public class MaildirMessage extends AbstractMessage implements 
MailboxMembership<Integer> {
+public class MaildirMessage extends AbstractMaildirMessage {
 
     // Document
     private int bodyStartOctet;
@@ -53,15 +52,7 @@ public class MaildirMessage extends Abst
     // MailboxMembership
     private Date internalDate;
     private long size;
-    private long uid;
-    private boolean answered;
-    private boolean deleted;
-    private boolean draft;
-    private boolean flagged;
-    private boolean recent;
-    private boolean seen;
     
-    private boolean newMessage;
     private boolean modified = false;
     
     /**
@@ -78,7 +69,7 @@ public class MaildirMessage extends Abst
     public MaildirMessage(Mailbox<Integer> mailbox, Date internalDate,
             int size, Flags flags, InputStream documentIn, int bodyStartOctet,
             List<MaildirHeader> maildirHeaders, PropertyBuilder 
propertyBuilder) {
-        super();
+        super(mailbox);
         // Document
         this.rawFullContent = documentIn;
         this.bodyStartOctet = bodyStartOctet;
@@ -111,7 +102,7 @@ public class MaildirMessage extends Abst
      */
     public MaildirMessage(Mailbox<Integer> mailbox, int size, InputStream 
documentIn, int bodyStartOctet,
             List<MaildirHeader> maildirHeaders, PropertyBuilder 
propertyBuilder) {
-        super();
+        super(mailbox);
         // Document
         this.rawFullContent = documentIn;
         this.bodyStartOctet = bodyStartOctet;
@@ -138,7 +129,8 @@ public class MaildirMessage extends Abst
      * @param message The message to copy
      * @throws IOException 
      */
-    public MaildirMessage(Mailbox<Integer> mailbox, MaildirMessage message) 
throws MailboxException {
+    public MaildirMessage(Mailbox<Integer> mailbox, AbstractMaildirMessage 
message) throws MailboxException {
+        super(mailbox);
         this.internalDate = message.getInternalDate();
         this.size = message.getMessage().getFullContentOctets();
         this.answered = message.isAnswered();
@@ -176,23 +168,7 @@ public class MaildirMessage extends Abst
         newMessage = true;
     }
 
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.AbstractDocument#getBodyStartOctet()
-     */
-    @Override
-    protected int getBodyStartOctet() {
-        return bodyStartOctet;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.AbstractDocument#getRawFullContent()
-     */
-    @Override
-    protected InputStream getRawFullContent() {
-        return rawFullContent;
-    }
+  
 
     /* 
      * (non-Javadoc)
@@ -302,13 +278,6 @@ public class MaildirMessage extends Abst
         throw new NotImplementedException();
     }
 
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#getUid()
-     */
-    public long getUid() {
-        return uid;
-    }
     
     /**
      * Set the Uid
@@ -316,56 +285,10 @@ public class MaildirMessage extends Abst
      */
     public void setUid(long uid) {
         modified = true;
-        this.uid = uid;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isAnswered()
-     */
-    public boolean isAnswered() {
-        return answered;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isDeleted()
-     */
-    public boolean isDeleted() {
-        return deleted;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isDraft()
-     */
-    public boolean isDraft() {
-        return draft;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isFlagged()
-     */
-    public boolean isFlagged() {
-        return flagged;
+        setUid(uid);
     }
 
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isRecent()
-     */
-    public boolean isRecent() {
-        return recent;
-    }
-
-    /* 
-     * (non-Javadoc)
-     * @see 
org.apache.james.mailbox.store.mail.model.MailboxMembership#isSeen()
-     */
-    public boolean isSeen() {
-        return seen;
-    }
+  
 
     /* 
      * (non-Javadoc)
@@ -374,13 +297,9 @@ public class MaildirMessage extends Abst
     public void setFlags(Flags flags) {
         if (flags != null) {
             modified = true;
-            answered = flags.contains(Flags.Flag.ANSWERED);
-            deleted = flags.contains(Flags.Flag.DELETED);
-            draft = flags.contains(Flags.Flag.DRAFT);
-            flagged = flags.contains(Flags.Flag.FLAGGED);
-            recent = flags.contains(Flags.Flag.RECENT);
-            seen = flags.contains(Flags.Flag.SEEN);
+       
         }
+        super.setFlags(flags);
     }
 
     /* 
@@ -389,16 +308,7 @@ public class MaildirMessage extends Abst
      */
     public void unsetRecent() {
         modified = true;
-        recent = false;
-    }
-    
-    /**
-     * Indicates whether this MaildirMessage reflects a new message or one 
that already
-     * exists in the file system.
-     * @return true if it is new, false if it already exists
-     */
-    public boolean isNew() {
-        return newMessage;
+        super.unsetRecent();
     }
     
     /**
@@ -408,26 +318,31 @@ public class MaildirMessage extends Abst
     public boolean isModified() {
         return modified;
     }
+
+    public RewindableInputStream getFullContent() throws IOException {
+        // TODO Auto-generated method stub
+        return new MyRewindableInputStream(rawFullContent);
+    }
+
+    public RewindableInputStream getBodyContent() throws IOException {
+        return new MyRewindableInputStream(new 
LazySkippingInputStream(rawFullContent, bodyStartOctet));
+    }
+
+    public long getBodyOctets() {
+        return getFullContentOctets() - bodyStartOctet;
+    }
     
-    @Override
-    public String toString() {
-        StringBuffer theString = new StringBuffer("MaildirMessage ");
-        theString.append(uid);
-        theString.append(" {");
-        Flags flags = createFlags();
-        if (flags.contains(Flags.Flag.DRAFT))
-            theString.append(MaildirMessageName.FLAG_DRAFT);
-        if (flags.contains(Flags.Flag.FLAGGED))
-            theString.append(MaildirMessageName.FLAG_FLAGGED);
-        if (flags.contains(Flags.Flag.ANSWERED))
-            theString.append(MaildirMessageName.FLAG_ANSWERD);
-        if (flags.contains(Flags.Flag.SEEN))
-            theString.append(MaildirMessageName.FLAG_SEEN);
-        if (flags.contains(Flags.Flag.DELETED))
-            theString.append(MaildirMessageName.FLAG_DELETED);
-        theString.append("} ");
-        theString.append(getInternalDate());
-        return theString.toString();
+    private class MyRewindableInputStream extends RewindableInputStream {
+
+        protected MyRewindableInputStream(InputStream in) {
+            super(in);
+        }
+
+        @Override
+        protected void rewindIfNeeded() throws IOException {
+            // do nothing.. 
+        }
+        
     }
 
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to