james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rdon...@apache.org
Subject svn commit: r591578 - in /james/server/trunk: core-library/src/main/java/org/apache/james/mailboxmanager/ core-library/src/main/java/org/apache/james/mailboxmanager/impl/ experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/im...
Date Sat, 03 Nov 2007 10:47:22 GMT
Author: rdonkin
Date: Sat Nov  3 03:47:19 2007
New Revision: 591578

URL: http://svn.apache.org/viewvc?rev=591578&view=rev
Log:
IAMP FETCH optimisation. Introduced Content interface. Still some design issues to be sorted out but this is a step in the right direction and no more wrong than the current implmentation. Contains reworking of JAMES-808 (https://issues.apache.org/jira/browse/JAMES-808 JAMES-808). Thanks to Zsombor Gegesy.

Added:
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalFetchRunner.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalHostSystem.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchBodyNoSection.test
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchText.test
    james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalFetchTest.java
    james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/MessageUtils.java
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsCountUnnormalLinesTest.java
    james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsNormalisedWriteTo.java
Modified:
    james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResult.java
    james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/impl/MessageResultImpl.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractProtocolTest.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/HostSystem.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/ProtocolSession.java
    james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/SelectedStateSetup.test
    james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java
    james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java
    james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalSelectedStateTest.java
    james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/TorqueMailbox.java

Modified: james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResult.java
URL: http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResult.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResult.java (original)
+++ james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/MessageResult.java Sat Nov  3 03:47:19 2007
@@ -93,6 +93,10 @@
 
     public static final int HEADERS = 0x100;
     
+    public static final int FULL_CONTENT = 0x200;
+    
+    public static final int BODY_CONTENT = 0x400;
+    
     int getIncludedResults();
 
     boolean contains(int result);
@@ -173,5 +177,43 @@
          * @throws MessagingException
          */
         List getOtherLines(String[] names) throws MessagingException;
+    }
+    
+    /**
+     * Gets the full message including headers and body.
+     * The message data should have normalised line endings (CRLF).
+     * @return <code>Content</code>, 
+     * or or null if {@link #FULL_CONTENT} has not been included in the 
+     * results
+     */
+    Content getFullMessage();
+
+    /**
+     * Gets the body of the message excluding headers.
+     * The message data should have normalised line endings (CRLF).
+     * @return <code>Content</code>,
+     * or or null if {@link #FULL_CONTENT} has not been included in the 
+     * results 
+     */
+    Content getMessageBody();
+
+    /**
+     * IMAP needs to know the size of the content before it starts to write it out.
+     * This interface allows direct writing whilst exposing total size.
+     */
+    public interface Content {
+        /**
+         * Writes content into the given buffer.
+         * @param buffer <code>StringBuffer</code>, not null
+         * @throws MessagingException
+         */
+        public void writeTo(StringBuffer buffer) throws MessagingException;
+        
+        /**
+         * Size (in octets) of the content.
+         * @return number of octets to be written
+         * @throws MessagingException
+         */
+        public long size() throws MessagingException;
     }
 }

Modified: james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/impl/MessageResultImpl.java
URL: http://svn.apache.org/viewvc/james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/impl/MessageResultImpl.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/impl/MessageResultImpl.java (original)
+++ james/server/trunk/core-library/src/main/java/org/apache/james/mailboxmanager/impl/MessageResultImpl.java Sat Nov  3 03:47:19 2007
@@ -38,6 +38,8 @@
     private Date internalDate;
     private String key;
     private Headers headers;
+    private Content messageBody;
+    private Content fullMessage;
     
 
     public MessageResultImpl(long uid) {
@@ -191,5 +193,21 @@
 
     public void setHeaders(Headers headers) {
         this.headers = headers;
+    }
+
+    public final Content getFullMessage() {
+        return fullMessage;
+    }
+
+    public final void setFullMessage(Content fullMessage) {
+        this.fullMessage = fullMessage;
+    }
+
+    public final Content getMessageBody() {
+        return messageBody;
+    }
+
+    public final void setMessageBody(Content messageBody) {
+        this.messageBody = messageBody;
     }
 }

Modified: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractProtocolTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractProtocolTest.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractProtocolTest.java (original)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractProtocolTest.java Sat Nov  3 03:47:19 2007
@@ -42,13 +42,6 @@
     /** The Protocol session which is run after the testElements. */
     protected ProtocolSession postElements = new ProtocolSession();
 
-    /** The host name to connect to for socket-based testing. */
-    protected String host = HOST;
-    /** The host port to connect to for socket-based testing. */
-    protected int port = PORT;
-    /** The timeout to set on the socket for socket-based testing. */
-    protected int timeout = TIMEOUT;
-
     private final HostSystem hostSystem;
     
     public AbstractProtocolTest( HostSystem hostSystem )
@@ -60,6 +53,12 @@
     {
         super.setUp();
         setUpEnvironment();
+    }
+    
+    protected void continueAfterFailure() {
+        preElements.setContinueAfterFailure(true);
+        testElements.setContinueAfterFailure(true);
+        postElements.setContinueAfterFailure(true);
     }
 
 

Added: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/AbstractTestFetch.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.test.functional.imap;
+
+public abstract class AbstractTestFetch extends BaseTestSelectedState {
+
+    public AbstractTestFetch(HostSystem system) {
+        super(system);
+    }
+
+    public void testFetchText() throws Exception {
+        scriptTest("FetchText");
+    }
+
+    public void testFetchBodyNoSection() throws Exception {
+        scriptTest("FetchBodyNoSection");
+    }
+}

Modified: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/HostSystem.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/HostSystem.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/HostSystem.java (original)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/HostSystem.java Sat Nov  3 03:47:19 2007
@@ -54,7 +54,16 @@
     {
         public String readLine() throws Exception;
         public void writeLine(String line) throws Exception;
+        /**
+         * Opens the session.
+         * @throws Exception
+         */
         public void start() throws Exception;
+        
+        /**
+         * Closes the session.
+         * @throws Exception
+         */
         public void stop() throws Exception;
     }
     

Modified: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/ProtocolSession.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/ProtocolSession.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/ProtocolSession.java (original)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/ProtocolSession.java Sat Nov  3 03:47:19 2007
@@ -46,6 +46,17 @@
     private Iterator elementsIterator;
     private HostSystem.Session[] sessions;
     private ProtocolElement nextTest;
+    private boolean continueAfterFailure = false;
+    
+    
+    
+    public final boolean isContinueAfterFailure() {
+        return continueAfterFailure;
+    }
+
+    public final void setContinueAfterFailure(boolean continueAfterFailure) {
+        this.continueAfterFailure = continueAfterFailure;
+    }
 
     /**
      * Returns the number of sessions required to run this ProtocolSession.
@@ -72,7 +83,7 @@
             Object obj = elementsIterator.next();
             if ( obj instanceof ProtocolElement ) {
                 ProtocolElement test = ( ProtocolElement ) obj;
-                test.testProtocol( sessions );
+                test.testProtocol( sessions, continueAfterFailure );
             }
         }
     }
@@ -89,7 +100,7 @@
                         if (!nextTest.isClient()) {
                             break;
                         }
-                        nextTest.testProtocol( sessions );
+                        nextTest.testProtocol( sessions, continueAfterFailure );
                     }
                 }
                 if (!elementsIterator.hasNext()) {
@@ -195,7 +206,7 @@
          * writer for this session is writted to.
          * @throws Exception 
          */
-        public void testProtocol( HostSystem.Session[] sessions ) throws Exception
+        public void testProtocol( HostSystem.Session[] sessions, boolean continueAfterFailure ) throws Exception
         {
             if (sessionNumber < 0) {
                 for (int i = 0; i < sessions.length; i++) {
@@ -263,28 +274,32 @@
          * @throws InvalidServerResponseException If the actual server response didn't
          *          match the regular expression expected.
          */
-        public void testProtocol( HostSystem.Session[] sessions)
+        public void testProtocol( HostSystem.Session[] sessions, boolean continueAfterFailure)
                 throws Exception
         {
             if (sessionNumber < 0) {
                 for (int i = 0; i < sessions.length; i++) {
                     HostSystem.Session session = sessions[i];
-                    checkResponse(session);
+                    checkResponse(session, continueAfterFailure);
                 }
             }
             else {
                 HostSystem.Session session = sessions[sessionNumber];
-                checkResponse(session);
+                checkResponse(session, continueAfterFailure);
             }
         }
 
-        protected void checkResponse(HostSystem.Session session) throws Exception {
+        protected void checkResponse(HostSystem.Session session, boolean continueAfterFailure) throws Exception {
             String testLine = readLine(session);
             if ( ! match( expectedLine, testLine ) ) {
                 String errMsg = "\nLocation: " + location +
                         "\nExpected: " + expectedLine +
                         "\nActual   : " + testLine;
-                throw new InvalidServerResponseException( errMsg );
+                if (continueAfterFailure) {
+                    System.out.println(errMsg);
+                } else {
+                    throw new InvalidServerResponseException( errMsg );
+                }
             }
         }
 
@@ -366,7 +381,7 @@
          * @throws InvalidServerResponseException If a line is encountered which doesn't
          *              match one of the expected lines.
          */
-        protected void checkResponse(HostSystem.Session session) throws Exception {
+        protected void checkResponse(HostSystem.Session session, boolean continueAfterFailure) throws Exception {
             List testLines = new ArrayList(expectedLines);
             while (testLines.size() > 0) {
                 String actualLine = readLine(session);
@@ -393,8 +408,11 @@
                     }
                     errMsg.append("\nActual: ")
                             .append(actualLine);
-
-                    throw new InvalidServerResponseException(errMsg.toString());
+                    if (continueAfterFailure) {
+                        System.out.println(errMsg.toString());
+                    } else {
+                        throw new InvalidServerResponseException(errMsg.toString());
+                    }
                 }
             }
         }
@@ -408,19 +426,24 @@
             this.sessionNumber = sessionNumber < 0 ? 0 : sessionNumber ;
         }
         
-        public void testProtocol(Session[] sessions) throws Exception {
+        public void testProtocol(Session[] sessions, boolean continueAfterFailure) throws Exception {
             HostSystem.Session session = sessions[sessionNumber];
             continuationExpected = true;
             continued = false;
             String testLine = session.readLine();
             if ( ! "+".equals(testLine) || ! continued) {
-                throw new InvalidServerResponseException( "Expected continuation" );
+                final String message = "Expected continuation";
+                if (continueAfterFailure) {
+                    System.out.print(message);
+                } else {
+                    throw new InvalidServerResponseException( message );
+                }
             }
             continuationExpected = false;
             continued = false;
             
             if (nextTest != null) {
-                nextTest.testProtocol(sessions);
+                nextTest.testProtocol(sessions, continueAfterFailure);
             }
         }
 
@@ -438,9 +461,10 @@
     {
         /**
          * Executes the ProtocolElement against the supplied session.
+         * @param continueAfterFailure TODO
          * @throws Exception 
          */
-        void testProtocol( HostSystem.Session[] sessions) throws Exception;
+        void testProtocol( HostSystem.Session[] sessions, boolean continueAfterFailure) throws Exception;
         
         boolean isClient();
     }

Added: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalFetchRunner.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalFetchRunner.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalFetchRunner.java (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalFetchRunner.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,35 @@
+/****************************************************************
+ * 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.test.functional.imap.manual;
+
+import org.apache.james.test.functional.imap.AbstractTestFetch;
+
+/**
+ * Script verification against local IMAP.
+ * 
+ */
+public class ExternalFetchRunner extends AbstractTestFetch {
+
+    public ExternalFetchRunner() {
+        super(ExternalHostSystem.createLocalImap());
+        continueAfterFailure();
+    }
+
+}

Added: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalHostSystem.java
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalHostSystem.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalHostSystem.java (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/manual/ExternalHostSystem.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,161 @@
+/****************************************************************
+ * 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.test.functional.imap.manual;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.nio.charset.Charset;
+
+import org.apache.james.test.functional.imap.HostSystem;
+
+public class ExternalHostSystem implements HostSystem {
+
+    public static final HostSystem createLocalImap() {
+        final ExternalHostSystem result = new ExternalHostSystem("localhost", 143, new SystemLoggingMonitor());
+        return result;
+    }
+    
+    private final InetSocketAddress address;
+    private final Monitor monitor;
+        
+    public ExternalHostSystem(final String host, final int port, final Monitor monitor) {
+        super();
+        this.address = new InetSocketAddress(host, port);
+        this.monitor = monitor;
+    }
+
+    public boolean addUser(String user, String password) throws Exception {
+        monitor.note("Please ensure user '"+ user + "' with password '" + password + "' exists.");
+        return true;
+    }
+
+    public Session newSession(Continuation continuation) throws Exception {
+        final SocketChannel channel = SocketChannel.open(address);
+        channel.configureBlocking(false);
+        final SessionImpl result = new SessionImpl(channel, monitor);
+        return result;
+    }
+
+    public void reset() throws Exception {
+        monitor.note("Please reset system.");
+    }
+
+    public interface Monitor {
+        void note(String message);
+    }
+    
+    public static final class SystemLoggingMonitor implements Monitor {
+
+        public void note(String message) {
+            System.out.println(message);
+        }
+        
+    }
+    
+    private final static class SessionImpl implements Session {
+
+        private static final byte[] CRLF = {'\r','\n'};
+        
+        private final SocketChannel socket;
+        private final Monitor monitor;
+        private final ByteBuffer readBuffer;
+        private final Charset ascii;
+        private final ByteBuffer lineEndBuffer;
+        
+        private boolean first = true;
+        
+        public SessionImpl(final SocketChannel socket,final Monitor monitor) {
+            super();
+            this.socket = socket;
+            this.monitor = monitor;
+            readBuffer = ByteBuffer.allocateDirect(2048);
+            ascii = Charset.forName("US-ASCII");
+            lineEndBuffer = ByteBuffer.wrap(CRLF);
+        }
+
+        public String readLine() throws Exception {
+            StringBuffer buffer = new StringBuffer();
+            readlineInto(buffer);
+            final String result;
+            if (first) {
+                // fake shabang
+                monitor.note("<-" + buffer.toString());
+                result = "* OK IMAP4rev1 Server ready";
+                first = false;
+            } else {
+                result = buffer.toString();
+                monitor.note("<-" + result);
+            }
+            return result;
+        }
+
+        private void readlineInto(StringBuffer buffer) throws Exception {
+            while (socket.read(readBuffer) == 0);
+            readBuffer.flip();
+            while (readOneMore(buffer));
+            readBuffer.compact();
+        }
+
+        private boolean readOneMore(StringBuffer buffer) throws Exception {
+            final boolean result;
+            if (readBuffer.hasRemaining()) {
+                char next = (char) readBuffer.get();
+                if (next == '\n') {
+                    result = false;
+                } else if (next == '\r') {
+                    result = true;
+                } else {
+                    buffer.append(next);
+                    result = true;
+                }
+            } else {
+                readBuffer.clear();
+                readlineInto(buffer);
+                result = true;
+            }
+            return result;
+        }
+        
+        public void start() throws Exception {
+            while (!socket.finishConnect()) {
+                monitor.note("connecting...");
+                Thread.sleep(10);
+            }
+        }
+
+        public void stop() throws Exception {
+            monitor.note("closing");
+            socket.close();
+        }
+
+        public void writeLine(String line) throws Exception {
+            monitor.note("-> " + line);
+            ByteBuffer writeBuffer = ascii.encode(line);
+            while (writeBuffer.hasRemaining()) {
+                socket.write(writeBuffer);
+            }
+            lineEndBuffer.rewind();
+            while (lineEndBuffer.hasRemaining()) {
+                socket.write(lineEndBuffer);
+            }
+        }
+    }
+}

Added: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchBodyNoSection.test
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchBodyNoSection.test?rev=591578&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchBodyNoSection.test (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchBodyNoSection.test Sat Nov  3 03:47:19 2007
@@ -0,0 +1,35 @@
+################################################################
+# 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.                                           #
+################################################################
+# BODY[]
+# Not PEEK, so the Seen flag is implicitly set.
+C: f1 FETCH 1 (BODY[])
+S: \* 1 FETCH \(FLAGS \(\\Seen\) BODY\[\] \{254\}
+S: Date: Mon, 7 Feb 1994 21:52:25 -0800 \(PST\)
+S: From: Fred Foobar <foobar@Blurdybloop\.COM>
+S: Subject: Test 01
+S: To: mooch@owatagu\.siam\.edu
+S: Message-Id: <B27397-0100000@Blurdybloop\.COM>
+S: MIME-Version: 1\.0
+S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
+S:
+S: Test 01
+S: \)
+# TODO: this is wrong - need to fix event model
+S: \* 1 FETCH \(FLAGS \(\\Seen\)\)
+S: f1 OK FETCH completed.

Added: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchText.test
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchText.test?rev=591578&view=auto
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchText.test (added)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/FetchText.test Sat Nov  3 03:47:19 2007
@@ -0,0 +1,26 @@
+################################################################
+# Licensed to the Apache Software Foundation (ASF) under one   #
+# or more contributor license agreements.  See the NOTICE file #
+# distributed with this work for additional information        #
+# regarding copyright ownership.  The ASF licenses this file   #
+# to you under the Apache License, Version 2.0 (the            #
+# "License"); you may not use this file except in compliance   #
+# with the License.  You may obtain a copy of the License at   #
+#                                                              #
+#   http://www.apache.org/licenses/LICENSE-2.0                 #
+#                                                              #
+# Unless required by applicable law or agreed to in writing,   #
+# software distributed under the License is distributed on an  #
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       #
+# KIND, either express or implied.  See the License for the    #
+# specific language governing permissions and limitations      #
+# under the License.                                           #
+################################################################
+# BODY[TEXT]
+C: f1 FETCH 1 (BODY[TEXT])
+S: \* 1 FETCH \(FLAGS \(\\Seen\) BODY\[TEXT\] \{9\}
+S: Test 01
+S: \)
+# TODO: this is wrong - need to fix event model
+S: \* 1 FETCH \(FLAGS \(\\Seen\)\)
+S: f1 OK FETCH completed.
\ No newline at end of file

Modified: james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/SelectedStateSetup.test
URL: http://svn.apache.org/viewvc/james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/SelectedStateSetup.test?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/SelectedStateSetup.test (original)
+++ james/server/trunk/experimental-seda-imap-function/src/test/java/org/apache/james/test/functional/imap/scripts/SelectedStateSetup.test Sat Nov  3 03:47:19 2007
@@ -32,7 +32,7 @@
 C:
 S: A003 OK APPEND completed.
 
-C: A003 APPEND selected {254+}
+C: A004 APPEND selected {254+}
 C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
 C: From: Fred Foobar <foobar@Blurdybloop.COM>
 C: Subject: Test 02
@@ -43,9 +43,9 @@
 C:
 C: Test 02
 C:
-S: A003 OK APPEND completed.
+S: A004 OK APPEND completed.
 
-C: A003 APPEND selected {254+}
+C: A005 APPEND selected {254+}
 C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
 C: From: Fred Foobar <foobar@Blurdybloop.COM>
 C: Subject: Test 03
@@ -56,9 +56,9 @@
 C:
 C: Test 03
 C:
-S: A003 OK APPEND completed.
+S: A005 OK APPEND completed.
 
-C: A003 APPEND selected {254+}
+C: A006 APPEND selected {254+}
 C: Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
 C: From: Fred Foobar <foobar@Blurdybloop.COM>
 C: Subject: Test 04
@@ -69,7 +69,7 @@
 C:
 C: Test 04
 C:
-S: A003 OK APPEND completed.
+S: A006 OK APPEND completed.
 
 C: a1 SELECT selected
 S: \* FLAGS \(\\Answered \\Deleted \\Draft \\Flagged \\Seen\)

Modified: james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java (original)
+++ james/server/trunk/imap-mailbox-processor-function/src/main/java/org/apache/james/imapserver/processor/imap4rev1/FetchProcessor.java Sat Nov  3 03:47:19 2007
@@ -98,15 +98,7 @@
         boolean omitExpunged = (!useUids);
 
         // TODO only fetch needed results
-        int resultToFetch = MessageResult.FLAGS
-                | MessageResult.INTERNAL_DATE | MessageResult.MSN
-                | MessageResult.SIZE;
-        if (fetchMessage(fetch)) {
-            resultToFetch = resultToFetch | MessageResult.MIME_MESSAGE;
-        }
-        if (bodyElementRequiresHeaders(fetch.getBodyElements())) {
-            resultToFetch = resultToFetch | MessageResult.HEADERS;
-        }
+        int resultToFetch = getNeededMessageResult(fetch);
         ImapMailboxSession mailbox = ImapSessionUtils.getMailbox(session);
         for (int i = 0; i < idSet.length; i++) {
             GeneralMessageSet messageSet = GeneralMessageSetImpl.range(idSet[i]
@@ -129,50 +121,48 @@
         result.addUnsolicitedResponses(unsolicitedResponses);
         return result;
     }
-    
 
-    private boolean fetchMessage(FetchData fetch) {
-        final boolean result;
+    private int getNeededMessageResult(FetchData fetch) {
+        int result = MessageResult.MSN;
+        if (fetch.isFlags() || fetch.isSetSeen()) {
+            result |= MessageResult.FLAGS;
+        }
+        if (fetch.isInternalDate()) {
+            result |= MessageResult.INTERNAL_DATE;
+        }
+        if (fetch.isSize()) {
+            result |= MessageResult.SIZE;
+        }
+        if (fetch.isUid()) {
+            result |= MessageResult.UID;
+        }
         if (fetch.isEnvelope() || fetch.isBody() || fetch.isBodyStructure()) {
-            result = true;
-        } else {
-            final Collection bodyElements = fetch.getBodyElements();
-            result = bodyElementRequiresMessage(bodyElements);
+            // TODO: structure
+            //result |= MessageResult.ENVELOPE;
+            result |= MessageResult.MIME_MESSAGE;
         }
-        return result;
-    }
 
-    private boolean bodyElementRequiresMessage(final Collection bodyElements) {
-        boolean result = false;
-        if (bodyElements != null) {
-            for (final Iterator it=bodyElements.iterator();it.hasNext();) {
-                final BodyFetchElement element = (BodyFetchElement) it.next();
-                final String section = element.getParameters();
-                if ("TEXT".equalsIgnoreCase(section) || section.length() == 0) {
-                    result = true;
-                    break;
-                }
-            }
-        }
+        result |= fetchForBodyElements(fetch.getBodyElements());
+
         return result;
     }
 
-    private boolean bodyElementRequiresHeaders(final Collection bodyElements) {
-        boolean result = false;
+    private int fetchForBodyElements(final Collection bodyElements) {
+        int result = 0;
         if (bodyElements != null) {
             for (final Iterator it=bodyElements.iterator();it.hasNext();) {
                 final BodyFetchElement element = (BodyFetchElement) it.next();
                 final String section = element.getParameters();
                 if ("HEADER".equalsIgnoreCase(section)) {
-                    result = true;
-                    break;
+                    result |=  MessageResult.HEADERS;
                 } else if ( section.startsWith( "HEADER.FIELDS.NOT " ) ) {
-                    result = true;
-                    break;
-                }
-                else if ( section.startsWith( "HEADER.FIELDS " ) ) {
-                    result = true;
-                    break;
+                    result |=  MessageResult.HEADERS;
+                } else if ( section.startsWith( "HEADER.FIELDS " ) ) {
+                    result |=  MessageResult.HEADERS;
+                } else if (section.equalsIgnoreCase("TEXT")) {;
+                    result |=  MessageResult.BODY_CONTENT;
+                } else if (section.length() == 0) {
+                    result |=  MessageResult.FULL_CONTENT;
                 }
             }
         }
@@ -289,17 +279,8 @@
             throws ProtocolException, MessagingException {
         // TODO: section specifier should be fully parsed during parsing phase
         if (sectionSpecifier.length() == 0) {
-            final MimeMessage mimeMessage = result.getMimeMessage();
-            // TODO - need to use an InputStream from the response here.
-            ByteArrayOutputStream bout = new ByteArrayOutputStream();
-            try {
-                mimeMessage.writeTo(new CRLFOutputStream(bout));
-            } catch (IOException e) {
-                throw new ProtocolException("Error reading message source", e);
-            }
-            byte[] bytes = bout.toByteArray();
-            addLiteral(bytes, response);
-            // TODO JD maybe we've to add CRLF here
+            final MessageResult.Content fullMessage = result.getFullMessage();
+            addLiteralContent(fullMessage, response);
         }
         else if ( sectionSpecifier.equalsIgnoreCase( "HEADER" ) ) {
             final MessageResult.Headers headers = result.getHeaders();
@@ -320,27 +301,11 @@
         } else if (sectionSpecifier.equalsIgnoreCase("MIME")) {
             // TODO implement
             throw new ProtocolException("MIME not yet implemented.");
+            
         } else if (sectionSpecifier.equalsIgnoreCase("TEXT")) {
-            final MimeMessage mimeMessage = result.getMimeMessage();
-            // TODO - need to use an InputStream from the response here.
-            // TODO - this is a hack. To get just the body content, I'm using a
-            // null
-            // input stream to take the headers. Need to have a way of ignoring
-            // headers.
-            ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
-            ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
-            try {
-                // TODO James Trunk : Is this okay?
-                MimeMessageWrapper mmw = new MimeMessageWrapper(mimeMessage);
-
-                mmw.writeTo(headerOut, bodyOut);
-                byte[] bytes = bodyOut.toByteArray();
-
-                addLiteral(bytes, response);
-
-            } catch (IOException e) {
-                throw new ProtocolException("Error reading message source", e);
-            }
+            final MessageResult.Content messageBody = result.getMessageBody();
+            addLiteralContent(messageBody, response);
+            
         } else {
             // Should be a part specifier followed by a section specifier.
             // See if there's a leading part specifier.
@@ -378,6 +343,16 @@
         }
     }
 
+    private void addLiteralContent(final MessageResult.Content content, final StringBuffer response) throws MessagingException {
+        response.append('{' );
+        final long length = content.size();
+        response.append( length ); // TODO JD addLiteral: why was it  bytes.length +1 here?
+        response.append( '}' );
+        response.append( "\r\n" );
+        content.writeTo(response);
+    }
+
+    
     // TODO should do this at parse time.
     private String[] extractHeaderList(String headerList, int prefixLen) {
         // Remove the trailing and leading ')('

Modified: james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java
URL: http://svn.apache.org/viewvc/james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java (original)
+++ james/server/trunk/imapserver-function/src/main/java/org/apache/james/imapserver/commands/FetchCommand.java Sat Nov  3 03:47:19 2007
@@ -44,6 +44,7 @@
 import org.apache.james.mailboxmanager.GeneralMessageSet;
 import org.apache.james.mailboxmanager.MailboxManagerException;
 import org.apache.james.mailboxmanager.MessageResult;
+import org.apache.james.mailboxmanager.MessageResult.Content;
 import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
 import org.apache.james.mailboxmanager.mailbox.ImapMailboxSession;
 import org.apache.mailet.dates.RFC822DateFormat;
@@ -85,10 +86,7 @@
             fetch.uid = true;
         }
 
-        // TODO only fetch needed results
-        int resultToFetch = MessageResult.FLAGS | MessageResult.MIME_MESSAGE
-                | MessageResult.INTERNAL_DATE | MessageResult.MSN
-                | MessageResult.SIZE;
+        int resultToFetch = fetch.getNeededMessageResult();
         ImapMailboxSession mailbox = session.getSelected().getMailbox();
         for (int i = 0; i < idSet.length; i++) {
             GeneralMessageSet messageSet=GeneralMessageSetImpl.range(idSet[i].getLowVal(),idSet[i].getHighVal(),useUids);
@@ -151,27 +149,29 @@
                 response.append(result.getSize());
             }
 
-            SimpleMessageAttributes attrs = new SimpleMessageAttributes(result
-                    .getMimeMessage(), getLogger());
-
-            // ENVELOPE response
-            if (fetch.envelope) {
-                response.append(" ENVELOPE ");
-                response.append(attrs.getEnvelope());
-            }
-
-            // BODY response
-            if (fetch.body) {
-                response.append(" BODY ");
-                response.append(attrs.getBodyStructure(false));
-            }
-
-            // BODYSTRUCTURE response
-            if (fetch.bodyStructure) {
-                response.append(" BODYSTRUCTURE ");
-                response.append(attrs.getBodyStructure(true));
+            if (fetch.envelope || fetch.body || fetch.bodyStructure) {
+                SimpleMessageAttributes attrs = new SimpleMessageAttributes(result
+                        .getMimeMessage(), getLogger());
+    
+                // ENVELOPE response
+                if (fetch.envelope) {
+                    response.append(" ENVELOPE ");
+                    response.append(attrs.getEnvelope());
+                }
+    
+                // BODY response
+                if (fetch.body) {
+                    response.append(" BODY ");
+                    response.append(attrs.getBodyStructure(false));
+                }
+    
+                // BODYSTRUCTURE response
+                if (fetch.bodyStructure) {
+                    response.append(" BODYSTRUCTURE ");
+                    response.append(attrs.getBodyStructure(true));
+                }
             }
-
+            
             // UID response
             if (fetch.uid) {
                 response.append(" UID ");
@@ -190,9 +190,8 @@
                 // Various mechanisms for returning message body.
                 String sectionSpecifier = fetchElement.getParameters();
 
-                MimeMessage mimeMessage = result.getMimeMessage();
                 try {
-                    handleBodyFetch(mimeMessage, sectionSpecifier, response);
+                    handleBodyFetch(result, sectionSpecifier, response);
                 } catch (MessagingException e) {
                     throw new MailboxException(e.getMessage(), e);
                 }
@@ -212,62 +211,36 @@
     }
 
 
-    private void handleBodyFetch( MimeMessage mimeMessage,
-                                  String sectionSpecifier,
-                                  StringBuffer response )
+    private void handleBodyFetch( final MessageResult result,
+                                  final String sectionSpecifier,
+                                  final StringBuffer response )
             throws ProtocolException, MessagingException
     {
         if ( sectionSpecifier.length() == 0 ) {
-            // TODO - need to use an InputStream from the response here.
-            ByteArrayOutputStream bout = new ByteArrayOutputStream();
-            try {
-                mimeMessage.writeTo(new CRLFOutputStream(bout));
-            }
-            catch ( IOException e ) {
-                throw new ProtocolException( "Error reading message source", e);
-            }
-            byte[] bytes = bout.toByteArray();
-            addLiteral( bytes, response );
-            // TODO JD maybe we've to add CRLF here
-            
+            final Content fullMessage = result.getFullMessage();
+            addLiteralContent(fullMessage, response);
         }
         else if ( sectionSpecifier.equalsIgnoreCase( "HEADER" ) ) {
-            Enumeration e = mimeMessage.getAllHeaderLines();
-            addHeaders( e, response );
+            final List lines = result.getHeaders().getAllLines();
+            addHeaders( lines, response );
         }
         else if ( sectionSpecifier.startsWith( "HEADER.FIELDS.NOT " ) ) {
             String[] excludeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS.NOT ".length() );
-            Enumeration e = mimeMessage.getNonMatchingHeaderLines( excludeNames );
-            addHeaders( e, response );
+            final List lines = result.getHeaders().getOtherLines( excludeNames );
+            addHeaders( lines, response );
         }
         else if ( sectionSpecifier.startsWith( "HEADER.FIELDS " ) ) {
             String[] includeNames = extractHeaderList( sectionSpecifier, "HEADER.FIELDS ".length() );
-            Enumeration e = mimeMessage.getMatchingHeaderLines( includeNames );
-            addHeaders( e, response );
+            final List lines = result.getHeaders().getMatchingLines( includeNames );
+            addHeaders( lines, response );
         }
         else if ( sectionSpecifier.equalsIgnoreCase( "MIME" ) ) {
             // TODO implement
             throw new ProtocolException( "MIME not yet implemented." );
         }
         else if ( sectionSpecifier.equalsIgnoreCase( "TEXT" ) ) {
-            // TODO - need to use an InputStream from the response here.
-            // TODO - this is a hack. To get just the body content, I'm using a null
-            // input stream to take the headers. Need to have a way of ignoring headers.
-            ByteArrayOutputStream headerOut = new ByteArrayOutputStream();
-            ByteArrayOutputStream bodyOut = new ByteArrayOutputStream();
-            try {
-                // TODO James Trunk : Is this okay?
-                MimeMessageWrapper mmw=new MimeMessageWrapper(mimeMessage);
-                
-                mmw.writeTo(headerOut, bodyOut );
-                byte[] bytes = bodyOut.toByteArray();
-
-                addLiteral( bytes, response );
-
-            }
-            catch ( IOException e ) {
-                throw new ProtocolException( "Error reading message source", e);
-            }
+            final Content messageBody = result.getMessageBody();
+            addLiteralContent(messageBody, response);
         }
         else {
             // Should be a part specifier followed by a section specifier.
@@ -289,17 +262,13 @@
 
     }
 
-    private void addLiteral( byte[] bytes, StringBuffer response )
-    {
+    private void addLiteralContent(final MessageResult.Content content, final StringBuffer response) throws MessagingException {
         response.append('{' );
-        response.append( bytes.length ); // TODO JD addLiteral: why was it  bytes.length +1 here?
+        final long length = content.size();
+        response.append( length ); // TODO JD addLiteral: why was it  bytes.length +1 here?
         response.append( '}' );
         response.append( "\r\n" );
-
-        for ( int i = 0; i < bytes.length; i++ ) {
-            byte b = bytes[i];
-            response.append((char)b);
-        }
+        content.writeTo(response);
     }
 
     // TODO should do this at parse time.
@@ -326,23 +295,21 @@
         return (String[]) strings.toArray(new String[0]);
     }
 
-    private void addHeaders( Enumeration e, StringBuffer response )
+    private void addHeaders( final List headers, final StringBuffer response )
     {
-        List lines = new ArrayList();
         int count = 0;
-        while (e.hasMoreElements()) {
-            String line = (String)e.nextElement();
+        for (final Iterator it=headers.iterator();it.hasNext();) {
+            final String line = (String) it.next();
             count += line.length() + 2;
-            lines.add(line);
         }
+
         response.append( '{' );
         response.append( count + 2 );
         response.append( '}' );
         response.append("\r\n");
 
-        Iterator lit = lines.iterator();
-        while (lit.hasNext()) {
-            String line = (String)lit.next();
+        for (final Iterator it=headers.iterator();it.hasNext();) {
+            final String line = (String) it.next();
             response.append( line );
             response.append( "\r\n" );
         }
@@ -448,7 +415,6 @@
                 else {
                     consumeChar( command, '[' );
 
-                    
                     String parameter = readWord(command, "]");
 
                     consumeChar( command, ']');
@@ -508,7 +474,11 @@
         
         private boolean setSeen = false;
         
-        private Set bodyElements = new HashSet();
+        private final Set bodyElements = new HashSet();
+        boolean headerFetchElement = false;
+        boolean mailFetchElement = false;
+        boolean bodyFetch = false;
+        boolean fullContentFetch = false;
         
         public Collection getBodyElements() {
             return bodyElements;
@@ -523,7 +493,55 @@
             if (!peek) {
                 setSeen = true;
             }
+
+            // we only need the headers, if the following element added:
+            String sectionIdentifier = element.sectionIdentifier.toUpperCase();
+            if ("HEADERS".equals(sectionIdentifier) || sectionIdentifier.startsWith("HEADER.FIELDS.NOT ") 
+                    || sectionIdentifier.startsWith("HEADER.FIELDS ")) {
+                headerFetchElement = true;
+            } else if (sectionIdentifier.length() == 0) {
+                fullContentFetch= true;
+            } else if ("TEXT".equals(sectionIdentifier)) {
+                bodyFetch = true;
+            } else {
+                // unfortunately we need to fetch the whole mail
+                mailFetchElement = true;
+            }
             bodyElements.add(element);
+        }
+                
+        public int getNeededMessageResult() {
+            int result = MessageResult.MSN;
+            if (flags || setSeen) {
+                result |= MessageResult.FLAGS;
+            }
+            if (internalDate) {
+                result |= MessageResult.INTERNAL_DATE;
+            }
+            if (size) {
+                result |= MessageResult.SIZE;
+            }
+            if (uid) {
+                result |= MessageResult.UID;
+            }
+            if (mailFetchElement) {
+                result |= MessageResult.MIME_MESSAGE;
+            }
+            if (body || bodyStructure || envelope) {
+                // TODO: structure
+                //result |= MessageResult.ENVELOPE;
+                result |= MessageResult.MIME_MESSAGE;
+            }
+            if (headerFetchElement || mailFetchElement) {
+                result |= MessageResult.HEADERS;
+            }
+            if (bodyFetch) {
+                result |= MessageResult.BODY_CONTENT;
+            }
+            if (fullContentFetch) {
+                result |= MessageResult.FULL_CONTENT;
+            }
+            return result;
         }
     }
 

Added: james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalFetchTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalFetchTest.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalFetchTest.java (added)
+++ james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalFetchTest.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,30 @@
+/****************************************************************
+ * 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.experimental.imapserver;
+
+import org.apache.james.test.functional.imap.AbstractTestFetch;
+
+public class ExperimentalFetchTest extends AbstractTestFetch {
+
+    public ExperimentalFetchTest() throws Exception {
+        super(HostSystemFactory.createStandardImap());
+    }
+
+}

Modified: james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalSelectedStateTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalSelectedStateTest.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalSelectedStateTest.java (original)
+++ james/server/trunk/phoenix-deployment/src/test/org/apache/james/experimental/imapserver/ExperimentalSelectedStateTest.java Sat Nov  3 03:47:19 2007
@@ -44,7 +44,7 @@
     public void testFetchSingleMessage() throws Exception {
         // BODY octet count is buggy.
         // The total size of the message is used rather than the size of the part
-        // super.testFetchSingleMessage();
+        //super.testFetchSingleMessage();
     }
 
     public void testStore() throws Exception {

Added: james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/MessageUtils.java
URL: http://svn.apache.org/viewvc/james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/MessageUtils.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/MessageUtils.java (added)
+++ james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/MessageUtils.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,96 @@
+/****************************************************************
+ * 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.mailboxmanager.torque;
+
+/**
+ * Utility methods for messages.
+ * 
+ */
+public class MessageUtils {
+    
+    public static final byte CR = 0x0D;
+    public static final byte LF = 0x0A;
+    
+    /**
+     * Counts the number of <code>CR</code>'s and <code>LF</code>'s 
+     * which do are part of <code>CRLF</code>s.
+     * @param contents bytes, not null
+     * @return the number of lines which are not normal <code>CRLF</code>
+     */
+    public static long countUnnormalLines(final byte[] contents) {
+        int count = 0;
+        if (contents != null) {
+            final int length = contents.length;
+            for (int i=0;i<length;i++) {
+                byte current = contents[i];
+                if (current == CR) {
+                    final int next = i+1;
+                    if (next < length) {
+                        if (contents[next] != LF) {
+                            count++;
+                        }
+                    } else {
+                        count++;
+                    }
+                } else if (current == LF) { 
+                    final int last = i-1;
+                    if (last >= 0) {
+                        if (contents[last] != CR) {
+                            count++;
+                        }
+                    } else {
+                        count++;
+                    }
+                }
+            }
+        }
+        return count;
+    }
+    
+    /**
+     * Writes bytes into the buffer using naive encoding
+     * and converts isolated LF and CR to CRLF.
+     * @param contents bytes to write, not null
+     * @param buffer <code>StringBuffer</code> sink, not null
+     */
+    public static void normalisedWriteTo(final byte[] contents, final StringBuffer buffer) {
+        char last = 0;
+        for (int i=0;i<contents.length;i++) {
+            final char current = (char) contents[i];
+            if (current == '\n') {
+                if (last == '\r') {
+                    buffer.append('\n');
+                } else {
+                    buffer.append('\r');
+                    buffer.append('\n');
+                }
+            } else {
+                if (last == '\r') {
+                    buffer.append('\n');
+                }
+                buffer.append(current);
+            }
+            last = current;
+        }
+        if (last == '\r') {
+            buffer.append('\n');
+        }
+    }
+}

Modified: james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/TorqueMailbox.java
URL: http://svn.apache.org/viewvc/james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/TorqueMailbox.java?rev=591578&r1=591577&r2=591578&view=diff
==============================================================================
--- james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/TorqueMailbox.java (original)
+++ james/server/trunk/torque-mailboxmanager-function/src/main/java/org/apache/james/mailboxmanager/torque/TorqueMailbox.java Sat Nov  3 03:47:19 2007
@@ -40,6 +40,7 @@
 import org.apache.james.mailboxmanager.MailboxManagerException;
 import org.apache.james.mailboxmanager.MessageResult;
 import org.apache.james.mailboxmanager.SearchParameters;
+import org.apache.james.mailboxmanager.MessageResult.Content;
 import org.apache.james.mailboxmanager.impl.GeneralMessageSetImpl;
 import org.apache.james.mailboxmanager.impl.MailboxEventDispatcher;
 import org.apache.james.mailboxmanager.impl.MessageResultImpl;
@@ -352,14 +353,107 @@
             messageResult.setHeaders(createHeaders(messageRow));
             result -= MessageResult.HEADERS;
         }
-        
+        if ((result & MessageResult.BODY_CONTENT) > 0) {
+            messageResult.setMessageBody(createBodyContent(messageRow));
+            result -= MessageResult.BODY_CONTENT;
+        }
+        if ((result & MessageResult.FULL_CONTENT) > 0) {
+            messageResult.setFullMessage(createFullContent(messageRow, messageResult.getHeaders()));
+            result -= MessageResult.FULL_CONTENT;
+        }
         if (result != 0) {
             throw new RuntimeException("Unsupported result: " + result);
         }
         
         return messageResult;
     }
+    
+    private final static class FullContent implements MessageResult.Content {
+        private final byte[] contents;
+        private final List headers;
+        private final long size;
+        
+        public FullContent(final byte[] contents, final List headers) {
+            this.contents =  contents;
+            this.headers = headers;
+            this.size = caculateSize();
+        }
+
+        private long caculateSize(){
+            long result = contents.length + MessageUtils.countUnnormalLines(contents);
+            result += 2;
+            for (final Iterator it=headers.iterator(); it.hasNext();) {
+                final String line = (String) it.next();
+                if (line != null) {
+                    // we don't know the appropriate encoding for the headers
+                    // TODO: sort out on entry (if possible)
+                    result += line.getBytes().length;
+                    result += 2;
+                }
+            }
+            return result;
+        }
+
+        public void writeTo(StringBuffer buffer) throws MessagingException {
+            for (final Iterator it=headers.iterator(); it.hasNext();) {
+                final String line = (String) it.next();
+                if (line != null) {
+                    // we don't know the appropriate encoding for the headers
+                    // TODO: sort out on entry (if possible)
+                    byte[] bytes = line.getBytes();
+                    for (int i=0;i<bytes.length;i++) {
+                        buffer.append((char) bytes[i]);
+                    }
+                }
+                buffer.append('\r');
+                buffer.append('\n');
+            }
+            buffer.append('\r');
+            buffer.append('\n');
+            MessageUtils.normalisedWriteTo(contents, buffer);
+        }
+
+        public long size() throws MessagingException {
+            return size;
+        }
+    }
 
+    private Content createFullContent(final MessageRow messageRow, MessageResult.Headers headers) throws TorqueException, MessagingException {
+        if (headers == null) {
+            headers = createHeaders(messageRow);
+        }
+        final MessageBody body = (MessageBody) messageRow.getMessageBodys().get(0);
+        final byte[] bytes = body.getBody();
+        final List lines = headers.getAllLines();
+        final FullContent results = new FullContent(bytes, lines);
+        return results;
+    }
+    
+    private Content createBodyContent(MessageRow messageRow) throws TorqueException {
+        final MessageBody body = (MessageBody) messageRow.getMessageBodys().get(0);
+        final byte[] bytes = body.getBody();
+        final ByteContent result = new ByteContent(bytes);
+        return result;
+    }
+    
+    private final static class ByteContent implements MessageResult.Content {
+       
+        private final byte[] contents;
+        private final long size;
+        public ByteContent(final byte[] contents) {
+            this.contents = contents;
+            size = contents.length + MessageUtils.countUnnormalLines(contents);
+        }
+        
+        public long size() throws MessagingException {
+            return size;
+        }
+        
+        public void writeTo(StringBuffer buffer) throws MessagingException {
+            MessageUtils.normalisedWriteTo(contents, buffer);
+        }
+    } 
+    
     private MessageResult.Headers createHeaders(MessageRow messageRow) throws TorqueException {
         final List headers=messageRow.getMessageHeaders();
         Collections.sort(headers, new Comparator() {

Added: james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsCountUnnormalLinesTest.java
URL: http://svn.apache.org/viewvc/james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsCountUnnormalLinesTest.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsCountUnnormalLinesTest.java (added)
+++ james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsCountUnnormalLinesTest.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,61 @@
+/****************************************************************
+ * 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.mailboxmanager.torque;
+
+import junit.framework.TestCase;
+
+public class MessageUtilsCountUnnormalLinesTest extends TestCase {
+
+    protected void setUp() throws Exception {
+    }
+
+    protected void tearDown() throws Exception {
+    }
+
+    public void testEmpty() throws Exception {
+        assertEquals("Check processing of empty array", 0, MessageUtils.countUnnormalLines("".getBytes()));
+    }
+    
+    public void testNormal() throws Exception {
+        assertEquals("Check processing of normal data", 0, 
+                MessageUtils.countUnnormalLines("One\r\nTwo\r\nThree\r\n".getBytes()));
+    }
+    
+    public void testMissing() throws Exception {
+        assertEquals("Check processing simple data containing unnormal lines", 2, 
+                MessageUtils.countUnnormalLines("One\rTwo\nThree\r\n".getBytes()));
+    }
+    
+    public void testBoundaries() throws Exception {
+        assertEquals("CR at end", 1, 
+                MessageUtils.countUnnormalLines("One\r\nTwo\r\nThree\r".getBytes()));
+        assertEquals("LF at end", 1, 
+                MessageUtils.countUnnormalLines("One\r\nTwo\r\nThree\n".getBytes()));
+        assertEquals("CR at start", 1, 
+                MessageUtils.countUnnormalLines("\rOne\r\nTwo\r\nThree".getBytes()));
+        assertEquals("LF at start", 1, 
+                MessageUtils.countUnnormalLines("\nOne\r\nTwo\r\nThree".getBytes()));
+    }
+    
+    public void testSwitchOrder() throws Exception {
+        assertEquals("Check processing simple data containing unnormal lines", 8, 
+                MessageUtils.countUnnormalLines("\n\rOne\n\rTwo\n\rThree\n\r".getBytes()));
+    }
+}

Added: james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsNormalisedWriteTo.java
URL: http://svn.apache.org/viewvc/james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsNormalisedWriteTo.java?rev=591578&view=auto
==============================================================================
--- james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsNormalisedWriteTo.java (added)
+++ james/server/trunk/torque-mailboxmanager-function/src/test/java/org/apache/james/mailboxmanager/torque/MessageUtilsNormalisedWriteTo.java Sat Nov  3 03:47:19 2007
@@ -0,0 +1,85 @@
+/****************************************************************
+ * 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.mailboxmanager.torque;
+
+import junit.framework.TestCase;
+
+public class MessageUtilsNormalisedWriteTo extends TestCase {
+
+    StringBuffer buffer;
+    
+    protected void setUp() throws Exception {
+        super.setUp();
+        buffer = new StringBuffer();
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testEmpty() throws Exception {
+        MessageUtils.normalisedWriteTo("".getBytes(), buffer);
+        assertEquals("Check processing of empty array", "", buffer.toString());
+    }
+    
+    public void testNormal() throws Exception {
+        MessageUtils.normalisedWriteTo("One\r\nTwo\r\nThree\r\n".getBytes(), buffer);
+        assertEquals("Check processing of normal data", "One\r\nTwo\r\nThree\r\n", buffer.toString());
+    }
+    
+    public void testMissing() throws Exception {
+        MessageUtils.normalisedWriteTo("One\rTwo\nThree\r\n".getBytes(), buffer);        
+        assertEquals("Check processing simple data containing unnormal lines", "One\r\nTwo\r\nThree\r\n", 
+                buffer.toString());
+    }
+    
+    public void testCRAtEnd() throws Exception {
+        MessageUtils.normalisedWriteTo("One\r\nTwo\r\nThree\r".getBytes(), buffer);        
+        assertEquals("CR at end", "One\r\nTwo\r\nThree\r\n", 
+                buffer.toString());
+    }
+    
+    
+    public void testLFAtEnd() throws Exception {
+        MessageUtils.normalisedWriteTo("One\r\nTwo\r\nThree\n".getBytes(), buffer);        
+        assertEquals("LF at end", "One\r\nTwo\r\nThree\r\n", 
+                buffer.toString());
+    }
+    
+    
+    public void testCRAtStart() throws Exception {
+        MessageUtils.normalisedWriteTo("\rOne\r\nTwo\r\nThree\r".getBytes(), buffer);        
+        assertEquals("CR at start", "\r\nOne\r\nTwo\r\nThree\r\n", 
+                buffer.toString());
+    }
+    
+    
+    public void testLFAtStart() throws Exception {
+        MessageUtils.normalisedWriteTo("\nOne\r\nTwo\r\nThree".getBytes(), buffer);        
+        assertEquals("CR at start", "\r\nOne\r\nTwo\r\nThree", 
+                buffer.toString());
+    }
+    
+    public void testSwitchOrder() throws Exception {
+        MessageUtils.normalisedWriteTo("\n\rOne\n\rTwo\n\rThree\n\r".getBytes(), buffer);        
+        assertEquals("Check processing simple data containing unnormal lines", "\r\n\r\nOne\r\n\r\nTwo\r\n\r\nThree\r\n\r\n", 
+                buffer.toString());
+    }
+}



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


Mime
View raw message