You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by no...@apache.org on 2010/04/07 18:04:32 UTC

svn commit: r931605 - in /james/imap/trunk: memory/src/main/java/org/apache/james/imap/inmemory/ store/src/main/java/org/apache/james/imap/store/ store/src/main/java/org/apache/james/imap/store/mail/model/ store/src/test/java/org/apache/james/imap/stor...

Author: norman
Date: Wed Apr  7 16:04:31 2010
New Revision: 931605

URL: http://svn.apache.org/viewvc?rev=931605&view=rev
Log:
First part of work for exposing message content via InputStream. The performance is not optimal atm and it breaks the build but I think its better to commit the work in progress (IMAP-128)

Added:
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/AbstractFullContent.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullByteContent.java
      - copied, changed from r931438, james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullContent.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamContent.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamFullContent.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/LazySkippingInputStream.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/RewindableInputStream.java
    james/imap/trunk/store/src/test/java/org/apache/james/imap/store/RewindableInputStreamTest.java
Removed:
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullContent.java
Modified:
    james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/SimpleMailboxMembership.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/PartContentBuilder.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/ResultUtils.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/AbstractDocument.java
    james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/Document.java
    james/imap/trunk/store/src/test/java/org/apache/james/imap/store/SimpleMessage.java
    james/imap/trunk/torque/src/main/java/org/apache/james/mailboxmanager/torque/MessageRowUtils.java

Modified: james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/SimpleMailboxMembership.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/SimpleMailboxMembership.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/SimpleMailboxMembership.java (original)
+++ james/imap/trunk/memory/src/main/java/org/apache/james/imap/inmemory/SimpleMailboxMembership.java Wed Apr  7 16:04:31 2010
@@ -19,12 +19,16 @@
 
 package org.apache.james.imap.inmemory;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Date;
 import java.util.List;
 
 import javax.mail.Flags;
 
+import org.apache.james.imap.store.LazySkippingInputStream;
+import org.apache.james.imap.store.RewindableInputStream;
 import org.apache.james.imap.store.mail.model.AbstractMailboxMembership;
 import org.apache.james.imap.store.mail.model.Document;
 import org.apache.james.imap.store.mail.model.Header;
@@ -147,18 +151,17 @@ public class SimpleMailboxMembership ext
         recent = false;
     }
 
-    public ByteBuffer getBodyContent() {
-        final ByteBuffer contentBuffer = getFullContent();
-        contentBuffer.position(bodyStartOctet);
-        return contentBuffer.slice();
+    public RewindableInputStream getBodyContent() throws IOException {
+        return new RewindableInputStream(new LazySkippingInputStream(new ByteArrayInputStream(document),bodyStartOctet));
+       
     }
 
     public long getBodyOctets() {
         return getFullContentOctets() - bodyStartOctet;
     }
 
-    public ByteBuffer getFullContent() {
-        return ByteBuffer.wrap(document).asReadOnlyBuffer();
+    public RewindableInputStream getFullContent() throws IOException {
+        return new RewindableInputStream(new ByteArrayInputStream(document));
     }
 
     public long getFullContentOctets() {

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/AbstractFullContent.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/AbstractFullContent.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/AbstractFullContent.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/AbstractFullContent.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,110 @@
+/****************************************************************
+ * 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.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.james.imap.mailbox.Content;
+import org.apache.james.imap.mailbox.MessageResult;
+import org.apache.james.imap.mailbox.MessageResult.Header;
+
+/**
+ * Abstract base class for {@link Content} implementations which hold the headers and 
+ * the body a email
+ *
+ */
+public abstract class AbstractFullContent implements Content{
+
+
+    private List<Header> headers;
+    
+    public AbstractFullContent(final List<MessageResult.Header> headers) throws IOException {
+        this.headers = headers;
+    }
+    
+    protected long caculateSize() throws IOException{
+        long result = getBodySize();
+        result += 2;
+        for (final Iterator<MessageResult.Header> it = headers.iterator(); it.hasNext();) {
+            final MessageResult.Header header = it.next();
+            if (header != null) {
+                result += header.size();
+                result += 2;
+            }
+        }
+        return result;
+    }
+    
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Content#writeTo(java.nio.channels.WritableByteChannel)
+     */
+    public final void writeTo(WritableByteChannel channel) throws IOException {
+        ByteBuffer newLine = ByteBuffer.wrap(ResultUtils.BYTES_NEW_LINE);
+        for (final Iterator<MessageResult.Header> it = headers.iterator(); it.hasNext();) {
+            final MessageResult.Header header = it.next();
+            if (header != null) {
+                header.writeTo(channel);
+            }
+            newLine.rewind();
+            writeAll(channel, newLine);
+        }
+        newLine.rewind();
+        writeAll(channel, newLine);
+        bodyWriteTo(channel);
+    }
+
+    
+    /**
+     * Write all 
+     * 
+     * @param channel
+     * @param buffer
+     * @throws IOException
+     */
+    protected void writeAll(WritableByteChannel channel, ByteBuffer buffer)
+            throws IOException {
+        while (channel.write(buffer) > 0) {
+            // write more
+        }
+    }
+    
+    /**
+     * Return the size of the body
+     * 
+     * @return size
+     * @throws IOException
+     */
+    protected abstract long getBodySize() throws IOException;
+    
+    /**
+     * Write the body to the channel
+     * 
+     * @param channel
+     * @throws IOException
+     */
+    protected abstract void bodyWriteTo(WritableByteChannel channel) throws IOException;
+
+    
+}

Copied: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullByteContent.java (from r931438, james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullContent.java)
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullByteContent.java?p2=james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullByteContent.java&p1=james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullContent.java&r1=931438&r2=931605&rev=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullContent.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/FullByteContent.java Wed Apr  7 16:04:31 2010
@@ -25,42 +25,24 @@ package org.apache.james.imap.store;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.WritableByteChannel;
-import java.util.Iterator;
 import java.util.List;
 
-import org.apache.james.imap.mailbox.Content;
 import org.apache.james.imap.mailbox.MessageResult;
 
 /**
  * Content which holds the full content, including {@link Header} objets
  *
  */
-public final class FullContent implements Content {
+public final class FullByteContent extends  AbstractFullContent {
     private final ByteBuffer contents;
-
-    private final List<MessageResult.Header> headers;
-
     private final long size;
 
-    public FullContent(final ByteBuffer contents, final List<MessageResult.Header> headers) {
+    public FullByteContent(final ByteBuffer contents, final List<MessageResult.Header> headers) throws IOException {
+        super(headers);
         this.contents = contents;
-        this.headers = headers;
         this.size = caculateSize();
     }
 
-    private long caculateSize() {
-        long result = contents.limit();
-        result += 2;
-        for (final Iterator<MessageResult.Header> it = headers.iterator(); it.hasNext();) {
-            final MessageResult.Header header = it.next();
-            if (header != null) {
-                result += header.size();
-                result += 2;
-            }
-        }
-        return result;
-    }
-
     /*
      * (non-Javadoc)
      * @see org.apache.james.imap.mailbox.Content#size()
@@ -69,37 +51,15 @@ public final class FullContent implement
         return size;
     }
 
-    /*
-     * (non-Javadoc)
-     * @see org.apache.james.imap.mailbox.Content#writeTo(java.nio.channels.WritableByteChannel)
-     */
-    public void writeTo(WritableByteChannel channel) throws IOException {
-        ByteBuffer newLine = ByteBuffer.wrap(ResultUtils.BYTES_NEW_LINE);
-        for (final Iterator<MessageResult.Header> it = headers.iterator(); it.hasNext();) {
-            final MessageResult.Header header = it.next();
-            if (header != null) {
-                header.writeTo(channel);
-            }
-            newLine.rewind();
-            writeAll(channel, newLine);
-        }
-        newLine.rewind();
-        writeAll(channel, newLine);
+    @Override
+    protected void bodyWriteTo(WritableByteChannel channel) throws IOException {
         contents.rewind();
-        writeAll(channel, contents);
+        writeAll(channel, contents);        
     }
 
-    /**
-     * Write all 
-     * 
-     * @param channel
-     * @param buffer
-     * @throws IOException
-     */
-    private void writeAll(WritableByteChannel channel, ByteBuffer buffer)
-            throws IOException {
-        while (channel.write(buffer) > 0) {
-            // write more
-        }
+    @Override
+    protected long getBodySize() throws IOException {
+        return contents.limit();
     }
+
 }
\ No newline at end of file

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamContent.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamContent.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamContent.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamContent.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+import org.apache.james.imap.mailbox.Content;
+
+/**
+ * {@link Content} which is stored in a {@link InputStream}
+ *
+ */
+public final class InputStreamContent implements Content{
+
+    private RewindableInputStream in;
+    private long size;
+
+    public InputStreamContent(RewindableInputStream in) throws IOException{
+        this.in = in;
+        this.size = in.available();
+    }
+    
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Content#size()
+     */
+    public long size() {
+        return size;
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Content#writeTo(java.nio.channels.WritableByteChannel)
+     */
+    @SuppressWarnings("unused")
+    public void writeTo(WritableByteChannel channel) throws IOException {
+        
+        // rewind the stream before write it to the channel
+        in.rewind();
+        
+        // read all the content of the underlying InputStream in 8096 byte chunks, wrap them
+        // in a ByteBuffer and finally write the Buffer to the channel
+        byte[] buf = new byte[1];
+        int i = 0;
+        while ((i = in.read(buf)) != -1) {
+            channel.write(ByteBuffer.wrap(buf));
+        }  
+    }
+
+}

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamFullContent.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamFullContent.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamFullContent.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/InputStreamFullContent.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,80 @@
+/****************************************************************
+ * 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.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.List;
+
+import org.apache.james.imap.mailbox.MessageResult;
+
+
+/**
+ * {@link AbstractFullContent} implementations which uses an {@link InputStream} as source for the 
+ * body content
+ *
+ */
+public class InputStreamFullContent extends AbstractFullContent{
+
+    private final RewindableInputStream in;
+    private long size;
+
+
+    public InputStreamFullContent(final RewindableInputStream contents, final List<MessageResult.Header> headers) throws IOException{
+        super(headers);
+        this.in = contents;
+        this.size = caculateSize();
+    }
+
+    
+
+
+    /*
+     * (non-Javadoc)
+     * @see org.apache.james.imap.mailbox.Content#size()
+     */
+    public final long size() {
+        return size;
+    }
+
+    @SuppressWarnings("unused")
+    @Override
+    protected void bodyWriteTo(WritableByteChannel channel) throws IOException {
+        // rewind the stream before write it to the channel
+        in.rewind();
+        
+        // read all the content of the underlying InputStream in 8096 byte chunks, wrap them
+        // in a ByteBuffer and finally write the Buffer to the channel
+        byte[] buf = new byte[1];
+        int i = 0;
+        while ((i = in.read(buf)) != -1) {
+            channel.write(ByteBuffer.wrap(buf));
+
+        }  
+    }
+
+
+    @Override
+    protected long getBodySize() throws IOException{
+        return in.available();
+    }
+
+}

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/LazySkippingInputStream.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/LazySkippingInputStream.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/LazySkippingInputStream.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/LazySkippingInputStream.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,90 @@
+/****************************************************************
+ * 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.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * {@link FilterInputStream} implementation which skip the given bytes as late as possible.
+ * 
+ *
+ */
+public class LazySkippingInputStream extends FilterInputStream{
+
+    private long skipBytes;
+    private boolean skipped = false;
+
+    public LazySkippingInputStream(InputStream in, long skipBytes) {
+        super(in);
+        this.skipBytes = skipBytes;
+    }
+
+    @Override
+    public int read() throws IOException {
+        skipIfNeeded();
+
+        return super.read();
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        skipIfNeeded();
+        return super.read(b, off, len);
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        skipIfNeeded();
+        return super.read(b);
+    }
+    
+    @Override
+    public int available() throws IOException {
+        skipIfNeeded();
+
+        return super.available();
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+        // not supported
+    }
+
+    @Override
+    public boolean markSupported() {
+        return false;
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        skipIfNeeded();
+        return super.skip(n);
+    }
+
+    private void skipIfNeeded() throws IOException {
+        if (skipped == false) {
+            super.skip(skipBytes);
+            skipped = true;
+        }
+    }
+
+}

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/PartContentBuilder.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/PartContentBuilder.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/PartContentBuilder.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/PartContentBuilder.java Wed Apr  7 16:04:31 2010
@@ -121,7 +121,7 @@ public class PartContentBuilder {
     public Content getFullContent() throws IOException, UnexpectedEOFException, MimeException {
         final List<Header> headers = getMimeHeaders();
         final byte[] content = mimeBodyContent();
-        return new FullContent(ByteBuffer.wrap(content), headers);
+        return new FullByteContent(ByteBuffer.wrap(content), headers);
     }
 
     public Content getMessageBodyContent() throws IOException, MimeException {

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/ResultUtils.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/ResultUtils.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/ResultUtils.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/ResultUtils.java Wed Apr  7 16:04:31 2010
@@ -21,7 +21,6 @@ package org.apache.james.imap.store;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -81,10 +80,11 @@ public class ResultUtils {
      * 
      * @param membership
      * @return bodyContent
+     * @throws IOException 
      */
-    public static Content createBodyContent(MailboxMembership<?> membership) {
-        final ByteBuffer bytes = membership.getDocument().getBodyContent();
-        final ByteContent result = new ByteContent(bytes);
+    public static Content createBodyContent(MailboxMembership<?> membership) throws IOException {
+        final RewindableInputStream stream = membership.getDocument().getBodyContent();
+        final InputStreamContent result = new InputStreamContent(stream);
         return result;
     }
 
@@ -93,10 +93,11 @@ public class ResultUtils {
      * 
      * @param membership
      * @return content
+     * @throws IOException 
      */
-    public static Content createFullContent(final MailboxMembership<?> membership) {
-        final ByteBuffer bytes = membership.getDocument().getFullContent();
-        final ByteContent results = new ByteContent(bytes);
+    public static Content createFullContent(final MailboxMembership<?> membership) throws IOException {
+        final RewindableInputStream stream = membership.getDocument().getFullContent();
+        final InputStreamContent results = new InputStreamContent(stream);
         return results;
     }
 
@@ -118,20 +119,21 @@ public class ResultUtils {
             messageResult.setFlags(message.createFlags());
             messageResult.setSize(message.getSize());
             messageResult.setInternalDate(message.getInternalDate());
-            
-            if ((content & FetchGroup.HEADERS) > 0) {
-                addHeaders(message, messageResult);
-                content -= FetchGroup.HEADERS;
-            }
-            if ((content & FetchGroup.BODY_CONTENT) > 0) {
-                addBody(message, messageResult);
-                content -= FetchGroup.BODY_CONTENT;
-            }
-            if ((content & FetchGroup.FULL_CONTENT) > 0) {
-                addFullContent(message, messageResult);
-                content -= FetchGroup.FULL_CONTENT;
-            }
+
             try {
+
+                if ((content & FetchGroup.HEADERS) > 0) {
+                    addHeaders(message, messageResult);
+                    content -= FetchGroup.HEADERS;
+                }
+                if ((content & FetchGroup.BODY_CONTENT) > 0) {
+                    addBody(message, messageResult);
+                    content -= FetchGroup.BODY_CONTENT;
+                }
+                if ((content & FetchGroup.FULL_CONTENT) > 0) {
+                    addFullContent(message, messageResult);
+                    content -= FetchGroup.FULL_CONTENT;
+                }
                 if ((content & FetchGroup.MIME_DESCRIPTOR) > 0) {
                     addMimeDescriptor(message, messageResult);
                     content -= FetchGroup.MIME_DESCRIPTOR;
@@ -139,7 +141,7 @@ public class ResultUtils {
                 if (content != 0) {
                     throw new UnsupportedOperationException("Unsupported result: " + content);
                 }
-            
+
                 addPartContent(fetchGroup, message, messageResult);
             } catch (IOException e) {
                 throw new MailboxException(HumanReadableText.FAILURE_MAIL_PARSE, e);
@@ -155,16 +157,16 @@ public class ResultUtils {
             messageResult.setMimeDescriptor(descriptor);
     }
 
-    private static void addFullContent(final MailboxMembership<?> messageRow, MessageResultImpl messageResult) 
-            throws MailboxException {
-        final Content content = createFullContent(messageRow);
+    private static void addFullContent(final MailboxMembership<?> messageRow, MessageResultImpl messageResult) throws IOException {
+        Content content = createFullContent(messageRow);
         messageResult.setFullContent(content);
+
     }
 
-    private static void addBody(final MailboxMembership<?> message,
-            MessageResultImpl messageResult) {
+    private static void addBody(final MailboxMembership<?> message, MessageResultImpl messageResult)throws IOException {
         final Content content = createBodyContent(message);
         messageResult.setBody(content);
+
     }
 
     private static void addHeaders(final MailboxMembership<?> message,
@@ -231,19 +233,23 @@ public class ResultUtils {
      * 
      * @param membership
      * @return stream
+     * @throws IOException 
      */
-    public static InputStream toInput(final MailboxMembership<?> membership) {
+
+    public static InputStream toInput(final MailboxMembership<?> membership) throws IOException {
         final org.apache.james.imap.store.mail.model.Document document = membership.getDocument();
         return toInput(document);
     }
+   
 
     /**
      * Return an {@link InputStream} which holds the content of the given {@link org.apache.james.imap.store.mail.model.Document}
      * 
      * @param document
      * @return stream
+     * @throws IOException 
      */
-    public static InputStream toInput(final org.apache.james.imap.store.mail.model.Document document) {
+    public static InputStream toInput(final org.apache.james.imap.store.mail.model.Document document) throws IOException {
         final List<Header> headers = getSortedHeaders(document);
         final StringBuffer headersToString = new StringBuffer(headers.size() * 50);
         for (Header header: headers) {
@@ -253,40 +259,83 @@ public class ResultUtils {
             headersToString.append("\r\n");
         }
         headersToString.append("\r\n");
-        final ByteBuffer bodyContent = document.getBodyContent();
+        final RewindableInputStream bodyContent = document.getBodyContent();
         final MessageInputStream stream = new MessageInputStream(headersToString, bodyContent);
         return stream;
     }
 
-    private static final class MessageInputStream extends InputStream {
-        private final StringBuffer headers;
-
-        private final ByteBuffer bodyContent;
 
+    private static final class MessageInputStream extends RewindableInputStream {
+        private final StringBuffer headers;
         private int headerPosition = 0;
 
         public MessageInputStream(final StringBuffer headers,
-                final ByteBuffer bodyContent) {
-            super();
+                final RewindableInputStream bodyContent) throws IOException{
+            super(bodyContent);
+            
             this.headers = headers;
             bodyContent.rewind();
-            this.bodyContent = bodyContent;
         }
 
         public int read() throws IOException {
+            rewindIfNeeded();
             final int result;
             if (headerPosition < headers.length()) {
                 result = headers.charAt(headerPosition++);
-            } else if (bodyContent.hasRemaining()) {
-                result = bodyContent.get();
-            } else {
-                result = -1;
+            } else  { 
+                result = in.read();
             }
             return result;
         }
 
-    }
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
 
+            rewindIfNeeded();
+            if (headerPosition < headers.length()) {
+                int headersLeft = headers.length() - headerPosition;
+                if (len > headersLeft) {
+                    int i;
+                    for (i = 0; i < headersLeft; i++) {
+                        int a =  read();
+                        if (a == -1) {
+                            return i;
+                        } 
+                        b[off +i] = (byte) a;
+                    }
+                    int bytesLeft = len - headersLeft;
+                    return i + in.read(b, off +i, bytesLeft);
+                } else {
+
+                    for (int i = 0 ; i < len; i++) {
+                        int a =  read();
+                        if (a == -1) {
+                            return -1;
+                        } 
+                        b[off +i] = (byte) a;
+                    }
+                    return len;
+                }
+            }
+            return in.read(b, off, len);
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            return read(b, 0, b.length);
+        }
+
+        @Override
+        protected void rewindIfNeeded() throws IOException {
+            if (needsRewind()) {
+                headerPosition = 0;
+                ((RewindableInputStream)in).rewindIfNeeded();
+            }
+        }
+        
+        
+    }
+  
     private static final int[] path(MimePath mimePath) {
         final int[] result;
         if (mimePath == null) {

Added: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/RewindableInputStream.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/RewindableInputStream.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/RewindableInputStream.java (added)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/RewindableInputStream.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,162 @@
+/****************************************************************
+ * 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.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * {@link FilterInputStream} which support the get rewinded. This is done by copy over every byte
+ * to a File after it was read. The rewinding will get queued as long as possible. So if you call
+ * rewind, it will only get performed when needed
+ * 
+ *
+ */
+public class RewindableInputStream extends FilterInputStream{
+
+    private File f;
+    private OutputStream fOut;
+    private InputStream fIn;
+    protected boolean end = false;
+    protected boolean rewind;
+
+    public RewindableInputStream(InputStream in) throws IOException {
+        super(in);
+        f = File.createTempFile("rewindable", ".tmp");
+        fOut = new FileOutputStream(f);
+        fIn = new FileInputStream(f);
+    }
+
+    @Override
+    public void close() throws IOException {
+        try {
+            in.close();
+            fOut.close();
+            fIn.close();
+        } finally {
+            f.delete();
+        }
+    }
+
+    @Override
+    public int read() throws IOException {        
+        // rewind if we need to
+        rewindIfNeeded();
+        
+        int i;
+
+        if (end == false) {
+            i = in.read();
+            if (i == -1) {
+                end = true;
+            } else {
+                fOut.write(i);
+            }
+        } else {
+            i = fIn.read();
+        }
+        return i;
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (len == 0) {
+            return 0;
+        }
+        
+        // rewind if we need to
+        rewindIfNeeded();
+        
+        int i;
+        if (end == false) {
+            i = in.read(b, off, len);
+            if (i == -1) {
+                end = true;
+            }
+            fOut.write(b, off, len);
+        } else {
+            i = fIn.read(b,off,len);
+        }
+        return i;
+    }
+
+    @Override
+    public int read(byte[] b) throws IOException {
+        return read(b,0,b.length);
+    }
+    
+    public void rewind() {
+        rewind = true;
+    }
+
+    protected boolean needsRewind() {
+        return rewind;
+    }
+    
+    protected void rewindIfNeeded() throws IOException {
+        if (needsRewind()) {
+            rewind = false;
+
+            if (end == false) {
+                while ( read() != -1);
+            }
+            fIn = new FileInputStream(f);
+        }
+    }
+    
+    @Override
+    public int available() throws IOException {
+        if (end == false) {
+            return in.available();
+        } else {
+            return fIn.available();
+        }
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+        // do nothing
+    }
+
+    @Override
+    public boolean markSupported() {
+        return false;
+    }
+
+    @Override
+    public synchronized void reset() throws IOException {
+        // do nothing
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        for (int i = 0; i < n; i++) {
+            if (read() == -1) {
+                return n -i;
+            }
+            if (end) break;
+        }
+        return 0;
+    }
+}

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/AbstractDocument.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/AbstractDocument.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/AbstractDocument.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/AbstractDocument.java Wed Apr  7 16:04:31 2010
@@ -18,39 +18,13 @@
  ****************************************************************/
 package org.apache.james.imap.store.mail.model;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
 
 /**
  * Abstract base class for {@link Document}
  *
  */
 public abstract class AbstractDocument implements Document{
-
-    protected ByteBuffer getContentAsByteBuffer(InputStream content) {
-        ByteArrayOutputStream full = new ByteArrayOutputStream();
-        try {
-            byte[] buf = new byte[1024];
-            int i = 0;
-            while ((i = content.read(buf)) != -1) {
-                full.write(buf, 0, i);
-            }
-        } catch (IOException e) {
-            // what todo here ?
-        }
-        return ByteBuffer.wrap(full.toByteArray()).asReadOnlyBuffer();
-        
-    }
-    /**
-     * @see org.apache.james.imap.store.mail.model.Document#getBodyContent()
-     */    
-    public ByteBuffer getBodyContent() {
-        final ByteBuffer contentBuffer = getFullContent();
-        contentBuffer.position(getBodyStartOctet());
-        return contentBuffer.slice();
-    }
+    
     
     /**
      * The number of octets contained in the body of this part.
@@ -69,23 +43,4 @@ public abstract class AbstractDocument i
     protected abstract int getBodyStartOctet();
     
 
-
-    public final class ByteBufferInputStream extends InputStream {
-        private ByteBuffer buf;
-
-        public ByteBufferInputStream(ByteBuffer buf)  {
-            this.buf = buf;
-        }
-        
-        public synchronized int read() throws IOException {             
-            return buf.hasRemaining() ? buf.get() : -1;
-        }
-
-        public synchronized int read(byte[] bytes, int off, int len) throws IOException {           
-            int rv = Math.min(len, buf.remaining());                
-            buf.get(bytes, off, rv);
-            return rv == 0 ? -1 : rv;
-        }
-    }
-
 }

Modified: james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/Document.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/Document.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/Document.java (original)
+++ james/imap/trunk/store/src/main/java/org/apache/james/imap/store/mail/model/Document.java Wed Apr  7 16:04:31 2010
@@ -18,9 +18,11 @@
  ****************************************************************/
 package org.apache.james.imap.store.mail.model;
 
-import java.nio.ByteBuffer;
+import java.io.IOException;
 import java.util.List;
 
+import org.apache.james.imap.store.RewindableInputStream;
+
 /**
  * A MIME documents, consisting of meta-data (including MIME headers)
  * plus body content. In the case of multipart documents, this body content
@@ -31,16 +33,16 @@ public interface Document {
 
     /**
      * Gets the full content (including headers) of the document.
-     * @return read only buffer, not null
+     * @return fullContent, not null
      */
-    public abstract ByteBuffer getFullContent();
+    public abstract RewindableInputStream getFullContent() throws IOException;
     
     /**
      * Gets the body content of the document.
      * Headers are excluded.
-     * @return read only buffer, not null
+     * @return body, not null
      */
-    public abstract ByteBuffer getBodyContent();
+    public abstract RewindableInputStream getBodyContent() throws IOException;
 
     /**
      * Gets the top level MIME content media type.

Added: james/imap/trunk/store/src/test/java/org/apache/james/imap/store/RewindableInputStreamTest.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/test/java/org/apache/james/imap/store/RewindableInputStreamTest.java?rev=931605&view=auto
==============================================================================
--- james/imap/trunk/store/src/test/java/org/apache/james/imap/store/RewindableInputStreamTest.java (added)
+++ james/imap/trunk/store/src/test/java/org/apache/james/imap/store/RewindableInputStreamTest.java Wed Apr  7 16:04:31 2010
@@ -0,0 +1,86 @@
+/****************************************************************
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class RewindableInputStreamTest {
+
+    private RewindableInputStream in;
+    private final static String CONTENT = "test\nblah!\n";
+
+    @Before
+    public void setUp() throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        out.write(CONTENT.getBytes());
+
+        in = new RewindableInputStream(new ByteArrayInputStream(out.toByteArray()));
+
+    }
+
+    public void tearDown() throws IOException {
+        in.close();
+    }
+
+    @Test
+    public void testRewindAfterCompleteReading() throws IOException {
+
+        consume();
+        in.rewind();
+        consume();
+    }
+
+    @Test
+    public void testRewindAfterPartReading() throws IOException {
+
+        consume(3);
+        in.rewind();
+        consume();
+    }
+
+    private void consume() throws IOException {
+        int i = -1;
+        int a = 0;
+        while ((i = in.read()) != -1) {
+            assertEquals(CONTENT.charAt(a), (char) i);
+            a++;
+        }
+
+        // everything really read
+        assertEquals(a, CONTENT.length());
+    }
+
+    private void consume(int n) throws IOException {
+        int i = -1;
+        int a = 0;
+        while ((i = in.read()) != -1) {
+            assertEquals(CONTENT.charAt(a), (char) i);
+            a++;
+            if (i == n) {
+                break;
+            }
+        }
+    }
+}

Modified: james/imap/trunk/store/src/test/java/org/apache/james/imap/store/SimpleMessage.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/store/src/test/java/org/apache/james/imap/store/SimpleMessage.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/store/src/test/java/org/apache/james/imap/store/SimpleMessage.java (original)
+++ james/imap/trunk/store/src/test/java/org/apache/james/imap/store/SimpleMessage.java Wed Apr  7 16:04:31 2010
@@ -18,10 +18,11 @@
  ****************************************************************/
 package org.apache.james.imap.store;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -83,18 +84,20 @@ public class SimpleMessage implements Do
     }
 
     /**
+     * @throws IOException 
      * @see org.apache.james.imap.jpa.mail.model.Document#getBodyContent()
      */
-    public ByteBuffer getBodyContent() {
-        return ByteBuffer.wrap(body).asReadOnlyBuffer();
+    public RewindableInputStream getBodyContent() throws IOException {
+        return new RewindableInputStream(new ByteArrayInputStream(body));
     }
 
     /**
      * Gets the full content (including headers) of the document.
      * @return read only buffer, not null
+     * @throws IOException 
      */
-    public ByteBuffer getFullContent() {
-        return ByteBuffer.wrap(fullContent).asReadOnlyBuffer();
+    public RewindableInputStream getFullContent() throws IOException {
+        return new RewindableInputStream(new ByteArrayInputStream(fullContent));
     }
     
     /**

Modified: james/imap/trunk/torque/src/main/java/org/apache/james/mailboxmanager/torque/MessageRowUtils.java
URL: http://svn.apache.org/viewvc/james/imap/trunk/torque/src/main/java/org/apache/james/mailboxmanager/torque/MessageRowUtils.java?rev=931605&r1=931604&r2=931605&view=diff
==============================================================================
--- james/imap/trunk/torque/src/main/java/org/apache/james/mailboxmanager/torque/MessageRowUtils.java (original)
+++ james/imap/trunk/torque/src/main/java/org/apache/james/mailboxmanager/torque/MessageRowUtils.java Wed Apr  7 16:04:31 2010
@@ -40,7 +40,7 @@ import org.apache.james.imap.mailbox.Mes
 import org.apache.james.imap.mailbox.MessageResult.MimePath;
 import org.apache.james.imap.mailbox.util.MessageResultImpl;
 import org.apache.james.imap.store.ByteContent;
-import org.apache.james.imap.store.FullContent;
+import org.apache.james.imap.store.FullByteContent;
 import org.apache.james.imap.store.MimeDescriptorImpl;
 import org.apache.james.imap.store.PartContentBuilder;
 import org.apache.james.imap.store.ResultHeader;
@@ -123,8 +123,12 @@ public class MessageRowUtils {
         final MessageBody body = (MessageBody) messageRow.getMessageBodys()
                 .get(0);
         final byte[] bytes = body.getBody();
-        final FullContent results = new FullContent(ByteBuffer.wrap(bytes), headers);
-        return results;
+        FullByteContent results;
+        try {
+            return new FullByteContent(ByteBuffer.wrap(bytes), headers);
+        } catch (IOException e) {
+            throw new TorqueException("Unable to parse message",e);
+        }
     }
 
     public static MessageResult loadMessageResult(final MessageRow messageRow,



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