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]