MAILBOX:270: support setMetadata command, imap protocol add request for update 
annotation


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ecaaf3bf
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ecaaf3bf
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ecaaf3bf

Branch: refs/heads/master
Commit: ecaaf3bfb56a8f1ed873c7b0e0562ec2b1b6f734
Parents: caf66ab
Author: Quynh Nguyen <qngu...@linagora.com>
Authored: Thu Jun 23 14:14:57 2016 +0700
Committer: Benoit Tellier <btell...@linagora.com>
Committed: Wed Jun 29 11:25:02 2016 +0700

----------------------------------------------------------------------
 protocols/imap/pom.xml                          |   5 +
 .../apache/james/imap/api/ImapConstants.java    |   4 +
 .../imap/decode/parser/ImapParserFactory.java   |   3 +
 .../parser/SetAnnotationCommandParser.java      |  83 ++++++++++
 .../message/request/SetAnnotationRequest.java   |  47 ++++++
 .../imap/processor/DefaultProcessorChain.java   |   9 +-
 .../imap/processor/SetAnnotationProcessor.java  |  71 ++++++++
 .../parser/SetAnnotationCommandParserTest.java  | 163 +++++++++++++++++++
 .../processor/SetAnnotationProcessorTest.java   | 160 ++++++++++++++++++
 protocols/pom.xml                               |   5 +
 10 files changed, 549 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/pom.xml
----------------------------------------------------------------------
diff --git a/protocols/imap/pom.xml b/protocols/imap/pom.xml
index 305985d..810ca87 100644
--- a/protocols/imap/pom.xml
+++ b/protocols/imap/pom.xml
@@ -92,6 +92,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.assertj</groupId>
+            <artifactId>assertj-guava</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.jmock</groupId>
             <artifactId>jmock</artifactId>
             <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java 
b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
index e758477..70d1b22 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
@@ -106,6 +106,8 @@ public interface ImapConstants {
     String SUPPORTS_ACL = "ACL";
 
     String SUPPORTS_QUOTA = "QUOTA";
+
+    String SUPPORTS_ANNOTATION = "ANNOTATION";
     
     String INBOX_NAME = "INBOX";
 
@@ -235,6 +237,8 @@ public interface ImapConstants {
 
     String SETQUOTA_COMMAND_NAME = "SETQUOTA";
 
+    String SETANNOTATION_COMMAND_NAME = "SETMETADATA";
+
     String LIST_RESPONSE_NAME = "LIST";
 
     String XLIST_RESPONSE_NAME = "XLIST";

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
index ec89b13..ea57de6 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ImapParserFactory.java
@@ -107,6 +107,9 @@ public class ImapParserFactory implements 
ImapCommandParserFactory {
         _imapCommands.put(ImapConstants.GETQUOTA_COMMAND_NAME, 
GetQuotaCommandParser.class);
         _imapCommands.put(ImapConstants.SETQUOTA_COMMAND_NAME, 
SetQuotaCommandParser.class);
 
+        //RFC5464
+        //SETMETADATA
+        _imapCommands.put(ImapConstants.SETANNOTATION_COMMAND_NAME, 
SetAnnotationCommandParser.class);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParser.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParser.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParser.java
new file mode 100644
index 0000000..fb66888
--- /dev/null
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParser.java
@@ -0,0 +1,83 @@
+/****************************************************************
+ * 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.decode.parser;
+
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapMessage;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.decode.ImapRequestLineReader;
+import org.apache.james.imap.decode.base.AbstractImapCommandParser;
+import org.apache.james.imap.message.request.SetAnnotationRequest;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.apache.james.protocols.imap.DecodingException;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+
+public class SetAnnotationCommandParser extends AbstractImapCommandParser {
+    public SetAnnotationCommandParser() {
+        
super(ImapCommand.authenticatedStateCommand(ImapConstants.SETANNOTATION_COMMAND_NAME));
+    }
+
+    @Override
+    protected ImapMessage decode(ImapCommand command, ImapRequestLineReader 
request, String tag, ImapSession session)
+            throws DecodingException {
+        String mailboxName = request.mailbox();
+        ImmutableList.Builder<MailboxAnnotation> listMailboxAnnotations = 
ImmutableList.<MailboxAnnotation>builder();
+
+        if (request.nextWordChar() == '(') {
+            request.consumeChar('(');
+
+            do {
+                listMailboxAnnotations.add(readNextAnnotation(request));
+            } while (request.nextWordChar() != ')');
+
+            request.consumeChar(')');
+        }
+        request.eol();
+
+        return new SetAnnotationRequest(tag, command, mailboxName, 
listMailboxAnnotations.build());
+    }
+
+    private MailboxAnnotation readNextAnnotation(ImapRequestLineReader 
request) throws DecodingException {
+        try {
+            String key = request.atom();
+            String value = request.nstring();
+
+            return Optional.fromNullable(value)
+                .transform(transforMailboxAnnotation(key))
+                .or(MailboxAnnotation.nil(key));
+        } catch (IllegalArgumentException e) {
+            throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, 
"The key is not valid: " + e.getMessage());
+        }
+    }
+
+    private Function<String, MailboxAnnotation> 
transforMailboxAnnotation(final String key) {
+        return new Function<String, MailboxAnnotation>() {
+            public MailboxAnnotation apply(String value) {
+                return MailboxAnnotation.newInstance(key, value);
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
new file mode 100644
index 0000000..fdd82b0
--- /dev/null
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SetAnnotationRequest.java
@@ -0,0 +1,47 @@
+/****************************************************************
+ * 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.message.request;
+
+import java.util.List;
+
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+
+import com.google.common.collect.ImmutableList;
+
+public class SetAnnotationRequest extends AbstractImapRequest {
+    private final String mailboxName;
+    private final List<MailboxAnnotation> mailboxAnnotations;
+
+    public SetAnnotationRequest(String tag, ImapCommand command, String 
mailboxName, List<MailboxAnnotation> mailboxAnnotations) {
+        super(tag, command);
+        this.mailboxName = mailboxName;
+        this.mailboxAnnotations = ImmutableList.copyOf(mailboxAnnotations);
+    }
+
+    public String getMailboxName() {
+        return mailboxName;
+    }
+
+    public List<MailboxAnnotation> getMailboxAnnotations() {
+        return mailboxAnnotations;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
index edd71ac..c8a8ceb 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessorChain.java
@@ -55,7 +55,14 @@ public class DefaultProcessorChain {
         final CreateProcessor createProcessor = new 
CreateProcessor(deleteProcessor, mailboxManager, statusResponseFactory);
         final CloseProcessor closeProcessor = new 
CloseProcessor(createProcessor, mailboxManager, statusResponseFactory);
         final UnsubscribeProcessor unsubscribeProcessor = new 
UnsubscribeProcessor(closeProcessor, mailboxManager, subscriptionManager, 
statusResponseFactory);
-        final SubscribeProcessor subscribeProcessor = new 
SubscribeProcessor(unsubscribeProcessor, mailboxManager, subscriptionManager, 
statusResponseFactory);
+        final SubscribeProcessor subscribeProcessor;
+        if 
(mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Annotation)) {
+            final SetAnnotationProcessor setAnnotationProcessor = new 
SetAnnotationProcessor(unsubscribeProcessor, mailboxManager, 
statusResponseFactory);
+            capabilityProcessor.addProcessor(setAnnotationProcessor);
+            subscribeProcessor = new 
SubscribeProcessor(setAnnotationProcessor, mailboxManager, subscriptionManager, 
statusResponseFactory);
+        } else {
+            subscribeProcessor = new SubscribeProcessor(unsubscribeProcessor, 
mailboxManager, subscriptionManager, statusResponseFactory);
+        }
         final CopyProcessor copyProcessor = new 
CopyProcessor(subscribeProcessor, mailboxManager, statusResponseFactory);
         AuthenticateProcessor authenticateProcessor;
         if 
(mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Move)) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/main/java/org/apache/james/imap/processor/SetAnnotationProcessor.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/SetAnnotationProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetAnnotationProcessor.java
new file mode 100644
index 0000000..feb5d68
--- /dev/null
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetAnnotationProcessor.java
@@ -0,0 +1,71 @@
+/****************************************************************
+ * 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.processor;
+
+import java.util.List;
+
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapSessionUtils;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.response.StatusResponse;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapProcessor;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.message.request.SetAnnotationRequest;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+
+public class SetAnnotationProcessor extends 
AbstractMailboxProcessor<SetAnnotationRequest> implements 
CapabilityImplementingProcessor {
+
+    public SetAnnotationProcessor(ImapProcessor next, MailboxManager 
mailboxManager, StatusResponseFactory factory) {
+        super(SetAnnotationRequest.class, next, mailboxManager, factory);
+    }
+
+    public List<String> getImplementedCapabilities(ImapSession session) {
+        return ImmutableList.of(ImapConstants.SUPPORTS_ANNOTATION);
+    }
+
+    protected void doProcess(SetAnnotationRequest message, ImapSession 
session, String tag, ImapCommand command,
+            Responder responder) {
+        final MailboxManager mailboxManager = getMailboxManager();
+        final MailboxSession mailboxSession = 
ImapSessionUtils.getMailboxSession(session);
+        final String mailboxName = message.getMailboxName();
+        try {
+            MailboxPath mailboxPath = buildFullPath(session, mailboxName);
+
+            mailboxManager.updateAnnotations(mailboxPath, mailboxSession, 
message.getMailboxAnnotations());
+
+            okComplete(command, tag, responder);
+        } catch (MailboxNotFoundException e) {
+            session.getLog().info(command.getName() + " failed for mailbox " + 
mailboxName, e);
+            no(command, tag, responder, 
HumanReadableText.FAILURE_NO_SUCH_MAILBOX, 
StatusResponse.ResponseCode.tryCreate());
+        } catch (MailboxException e) {
+            session.getLog().info(command.getName() + " failed for mailbox " + 
mailboxName, e);
+            no(command, tag, responder, 
HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParserTest.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParserTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParserTest.java
new file mode 100644
index 0000000..9a793c6
--- /dev/null
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SetAnnotationCommandParserTest.java
@@ -0,0 +1,163 @@
+/****************************************************************
+ * 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.decode.parser;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.guava.api.Assertions.assertThat;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.decode.ImapRequestStreamLineReader;
+import org.apache.james.imap.message.request.SetAnnotationRequest;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.apache.james.protocols.imap.DecodingException;
+import org.junit.Test;
+
+public class SetAnnotationCommandParserTest {
+
+    private static final String INBOX = "anyMailboxName";
+    private static final String TAG = "A1";
+    private static final MailboxAnnotation PRIVATE_ANNOTATION = 
MailboxAnnotation.newInstance("/private/comment", "This is my comment");
+    private static final MailboxAnnotation SHARED_ANNOTATION = 
MailboxAnnotation.newInstance("/shared/comment", "This one is for you!");
+    private static final MailboxAnnotation NIL_ANNOTATION = 
MailboxAnnotation.nil("/private/comment");
+    private SetAnnotationCommandParser parser = new 
SetAnnotationCommandParser();
+    private ImapCommand command = ImapCommand.anyStateCommand("Command");
+
+    @Test
+    public void decodeMessageShouldReturnRequestContainsOneAnnotation() throws 
DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment \"This is my comment\") \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        assertThat(request.getMailboxName()).isEqualTo(INBOX);
+        
assertThat(request.getMailboxAnnotations()).containsOnly(PRIVATE_ANNOTATION);
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenContainsInvalidAnnotationKey() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/pri*vate/comment \"This is my comment\") \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+
+    @Test
+    public void decodeMessageShouldReturnRequestContainsOneNilAnnotation() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment NIL) \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        assertThat(request.getMailboxName()).isEqualTo(INBOX);
+        
assertThat(request.getMailboxAnnotations()).containsOnly(NIL_ANNOTATION);
+    }
+
+    @Test
+    public void 
decodeMessageShouldReturnRequestContainsOneAnnotationWithMultiLinesValue() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment {32}\nMy new comment across two lines.) \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, new ByteArrayOutputStream());
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        assertThat(request.getMailboxName()).isEqualTo(INBOX);
+        
assertThat(request.getMailboxAnnotations()).containsOnly(MailboxAnnotation.newInstance("/private/comment",
 "My new comment across two lines."));
+    }
+
+    @Test
+    public void decodeMessageShouldReturnRequestContainsMultiAnnotations() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment \"This is my comment\" /shared/comment \"This one is for 
you!\") \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, new ByteArrayOutputStream());
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        assertThat(request.getMailboxName()).isEqualTo(INBOX);
+        
assertThat(request.getMailboxAnnotations()).containsExactly(PRIVATE_ANNOTATION, 
SHARED_ANNOTATION);
+    }
+
+    @Test
+    public void 
decodeMessageShouldReturnRequestContainsMultiAnnotationsWithNil() throws 
DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment NIL /shared/comment \"This one is for you!\") 
\n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, new ByteArrayOutputStream());
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        assertThat(request.getMailboxName()).isEqualTo(INBOX);
+        
assertThat(request.getMailboxAnnotations()).containsExactly(NIL_ANNOTATION, 
SHARED_ANNOTATION);
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenCommandDoesNotStartWithSlash() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream("INBOX 
/private/comment \"This is my comment\") \n".getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenCommandDoesNotEndWithSlash() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment \"This is my comment\" \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenCommandDoesNotHaveAnnotationValue()
 throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment) \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+
+    @Test
+    public void 
decodeMessageShouldReturnRequestWhenCommandHasEmptyAnnotationValue() throws 
DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment \"   \") \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        
assertThat(request.getMailboxAnnotations().get(0).getValue()).isPresent();
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenCommandAnnotationValueNotInQuote() 
throws DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment This is my comment) \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+
+    @Test
+    public void 
decodeMessageShouldReturnRequestWhenCommandAnnotationValueIsNILString() throws 
DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " 
(/private/comment \"NIL\") \n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        SetAnnotationRequest request = (SetAnnotationRequest) 
parser.decode(command, lineReader, TAG, null);
+
+        
assertThat(request.getMailboxAnnotations().get(0).getValue()).isPresent();
+    }
+
+    @Test(expected = DecodingException.class)
+    public void 
decodeMessageShouldThrowDecodingExceptionWhenCommandMissingAnnotations() throws 
DecodingException {
+        InputStream inputStream = new ByteArrayInputStream((INBOX + " () 
\n").getBytes());
+        ImapRequestStreamLineReader lineReader = new 
ImapRequestStreamLineReader(inputStream, null);
+
+        parser.decode(command, lineReader, TAG, null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/imap/src/test/java/org/apache/james/imap/processor/SetAnnotationProcessorTest.java
----------------------------------------------------------------------
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/processor/SetAnnotationProcessorTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetAnnotationProcessorTest.java
new file mode 100644
index 0000000..65d62ea
--- /dev/null
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetAnnotationProcessorTest.java
@@ -0,0 +1,160 @@
+/****************************************************************
+ * 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.processor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+import org.apache.james.imap.api.ImapCommand;
+import org.apache.james.imap.api.ImapConstants;
+import org.apache.james.imap.api.ImapSessionState;
+import org.apache.james.imap.api.ImapSessionUtils;
+import org.apache.james.imap.api.display.HumanReadableText;
+import org.apache.james.imap.api.message.response.StatusResponse;
+import org.apache.james.imap.api.message.response.StatusResponseFactory;
+import org.apache.james.imap.api.process.ImapProcessor;
+import org.apache.james.imap.api.process.ImapSession;
+import org.apache.james.imap.encode.FakeImapSession;
+import org.apache.james.imap.message.request.SetAnnotationRequest;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
+import org.apache.james.mailbox.mock.MockMailboxSession;
+import org.apache.james.mailbox.model.MailboxAnnotation;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.MockitoAnnotations;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+
+public class SetAnnotationProcessorTest {
+
+    private static final String TAG = "TAG";
+
+    @InjectMocks
+    private SetAnnotationProcessor processor;
+
+    private ImapProcessor mockNextProcessor;
+    private MailboxManager mockMailboxManager;
+    private StatusResponseFactory mockStatusResponseFactory;
+    private ImapProcessor.Responder mockResponder;
+    private ImapSession mockImapSession;
+    private MailboxSession mockMailboxSession;
+
+    private List<MailboxAnnotation> MAILBOX_ANNOTATIONS;
+    private StatusResponse okResponse;
+    private Logger log;
+
+    private MailboxPath inbox; 
+
+    private SetAnnotationRequest request;
+    private ArgumentCaptor<HumanReadableText> humanTextCaptor;
+    
+    private void initAndMockData() {
+        okResponse = mock(StatusResponse.class);
+        mockNextProcessor = mock(ImapProcessor.class);
+        mockMailboxManager = mock(MailboxManager.class);
+        mockStatusResponseFactory = mock(StatusResponseFactory.class);
+        mockResponder = mock(ImapProcessor.Responder.class);
+        mockImapSession = mock(ImapSession.class);
+        log = mock(Logger.class);;
+
+        mockMailboxSession = new MockMailboxSession("username");
+        inbox = MailboxPath.inbox(mockMailboxSession);
+        MAILBOX_ANNOTATIONS = 
ImmutableList.of(MailboxAnnotation.newInstance("/private/key", "anyValue"));
+        request = new SetAnnotationRequest(TAG, 
ImapCommand.anyStateCommand("Name"), ImapConstants.INBOX_NAME, 
MAILBOX_ANNOTATIONS);
+        humanTextCaptor = ArgumentCaptor.forClass(HumanReadableText.class);
+
+        when(mockImapSession.getState()).thenReturn(ImapSessionState.SELECTED);
+        
when(mockImapSession.getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY)).thenReturn(mockMailboxSession);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        initAndMockData();
+        processor = new SetAnnotationProcessor(mockNextProcessor, 
mockMailboxManager, mockStatusResponseFactory);
+    }
+    @After
+    public void tearDown() {
+        processor = null;
+    }
+
+    @Test
+    public void 
getImplementedCapabilitiesShouldContainSupportAnnotationWhenMailboxManagerHasAnnotationCapability()
 {
+        assertThat(processor.getImplementedCapabilities(new 
FakeImapSession())).containsExactly(ImapConstants.SUPPORTS_ANNOTATION);
+    }
+
+    @Test
+    public void 
processShouldResponseNoWithNoSuchMailboxWhenManagerThrowMailboxNotFoundException()
 throws Exception {
+        when(mockImapSession.getLog()).thenReturn(log);
+
+        
doThrow(MailboxNotFoundException.class).when(mockMailboxManager).updateAnnotations(eq(inbox),
+            eq(mockMailboxSession), eq(MAILBOX_ANNOTATIONS));
+
+        processor.process(request, mockResponder, mockImapSession);
+
+        verify(mockStatusResponseFactory, 
times(1)).taggedNo(any(String.class), any(ImapCommand.class),
+                humanTextCaptor.capture(), 
any(StatusResponse.ResponseCode.class));
+
+        
assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.FAILURE_NO_SUCH_MAILBOX);
+    }
+
+    @Test
+    public void 
processShouldResponseNoWithGenericFailureWhenManagerThrowMailboxException() 
throws Exception {
+        when(mockImapSession.getLog()).thenReturn(log);
+
+        
doThrow(MailboxException.class).when(mockMailboxManager).updateAnnotations(eq(inbox),
 eq(mockMailboxSession), eq(MAILBOX_ANNOTATIONS));
+
+        processor.process(request, mockResponder, mockImapSession);
+
+        verify(mockStatusResponseFactory, 
times(1)).taggedNo(any(String.class), any(ImapCommand.class), 
humanTextCaptor.capture());
+
+        
assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING);
+    }
+
+    @Test
+    public void processShouldWorkWithCompleteResponse() throws Exception {
+        when(mockStatusResponseFactory.taggedOk(any(String.class), 
any(ImapCommand.class), any(HumanReadableText.class)))
+            .thenReturn(okResponse);
+
+        processor.process(request, mockResponder, mockImapSession);
+
+        verify(mockMailboxManager).updateAnnotations(inbox, 
mockMailboxSession, MAILBOX_ANNOTATIONS);
+        verify(mockResponder).respond(okResponse);
+        verify(mockStatusResponseFactory, 
times(1)).taggedOk(any(String.class), any(ImapCommand.class), 
humanTextCaptor.capture());
+
+        
assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/ecaaf3bf/protocols/pom.xml
----------------------------------------------------------------------
diff --git a/protocols/pom.xml b/protocols/pom.xml
index 470cb72..ee5d7d2 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -251,6 +251,11 @@
                 <version>1.4.4</version>
             </dependency>
             <dependency>
+                <groupId>org.assertj</groupId>
+                <artifactId>assertj-guava</artifactId>
+                <version>1.3.1</version>
+            </dependency>
+            <dependency>
                 <groupId>org.mockito</groupId>
                 <artifactId>mockito-core</artifactId>
                 <version>${mockito-core.version}</version>


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

Reply via email to