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