MAILBOX-270: getmetadata command, add new definition for command parser and processor.
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/ced2d52f Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ced2d52f Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ced2d52f Branch: refs/heads/master Commit: ced2d52fe2085596e7fdd2f5cd0e8953176faf18 Parents: 709cd70 Author: Quynh Nguyen <[email protected]> Authored: Tue Jul 5 13:31:46 2016 +0700 Committer: Quynh Nguyen <[email protected]> Committed: Tue Aug 30 09:21:30 2016 +0700 ---------------------------------------------------------------------- .../apache/james/imap/api/ImapConstants.java | 4 + .../parser/GetAnnotationCommandParser.java | 131 +++++++ .../imap/decode/parser/ImapParserFactory.java | 3 +- .../message/request/GetAnnotationRequest.java | 158 ++++++++ .../imap/processor/DefaultProcessorChain.java | 4 +- .../imap/processor/GetAnnotationProcessor.java | 175 +++++++++ .../message/response/StatusResponseTest.java | 3 +- .../parser/GetAnnotationCommandParserTest.java | 390 +++++++++++++++++++ .../processor/GetAnnotationProcessorTest.java | 329 ++++++++++++++++ protocols/src/site/xdoc/imap4.xml | 5 +- 10 files changed, 1196 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/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 70d1b22..0d9ef2f 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 @@ -239,6 +239,8 @@ public interface ImapConstants { String SETANNOTATION_COMMAND_NAME = "SETMETADATA"; + String GETANNOTATION_COMMAND_NAME = "GETMETADATA"; + String LIST_RESPONSE_NAME = "LIST"; String XLIST_RESPONSE_NAME = "XLIST"; @@ -257,6 +259,8 @@ public interface ImapConstants { String MYRIGHTS_RESPONSE_NAME = "MYRIGHTS"; + String ANNOTATION_RESPONSE_NAME = "METADATA"; + String NAME_ATTRIBUTE_NOINFERIORS = "\\Noinferiors"; String NAME_ATTRIBUTE_NOSELECT = "\\Noselect"; http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java new file mode 100644 index 0000000..07cf408 --- /dev/null +++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParser.java @@ -0,0 +1,131 @@ +/**************************************************************** + * 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 com.google.common.base.Optional; +import org.apache.james.imap.message.request.GetAnnotationRequest; +import org.apache.james.mailbox.model.MailboxAnnotationKey; +import com.google.common.base.CharMatcher; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +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.GetAnnotationRequest.Depth; +import org.apache.james.protocols.imap.DecodingException; + +public class GetAnnotationCommandParser extends AbstractImapCommandParser { + private static final CharMatcher ENDOFLINE_PATTERN = CharMatcher.isNot('\n').and(CharMatcher.isNot('\r')); + private static final String MAXSIZE = "MAXSIZE"; + private static final String DEPTH = "DEPTH"; + private static final boolean STOP_ON_PAREN = true; + + public GetAnnotationCommandParser() { + super(ImapCommand.authenticatedStateCommand(ImapConstants.GETANNOTATION_COMMAND_NAME)); + } + + @Override + protected ImapMessage decode(ImapCommand command, ImapRequestLineReader requestReader, String tag, ImapSession session) + throws DecodingException { + try { + return buildAnnotationRequest(command, requestReader, tag); + } catch (NullPointerException e) { + throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage()); + } catch (IllegalArgumentException e) { + throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage()); + } catch (IllegalStateException e) { + throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, e.getMessage()); + } + } + + private ImapMessage buildAnnotationRequest(ImapCommand command, ImapRequestLineReader requestReader, String tag) throws DecodingException { + GetAnnotationRequest.Builder builder = GetAnnotationRequest.builder() + .tag(tag) + .command(command) + .mailboxName(requestReader.mailbox()); + + consumeOptionsAndKeys(requestReader, builder); + + if (ENDOFLINE_PATTERN.matches(requestReader.nextNonSpaceChar())) { + consumeKey(requestReader, builder); + } + + return builder.build(); + } + + private void consumeOptionsAndKeys(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException { + while (requestReader.nextNonSpaceChar() == '(') { + requestReader.consumeChar('('); + switch (requestReader.nextChar()) { + case 'M': + consumeMaxsizeOpt(requestReader, builder); + break; + + case 'D': + consumeDepthOpt(requestReader, builder); + break; + + default: + consumeKeys(requestReader, builder); + break; + } + } + } + + private void consumeDepthOpt(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException { + if (!requestReader.atom().equalsIgnoreCase(DEPTH)) { + throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Wrong on, it should be DEPTH"); + } + + builder.depth(Depth.fromString(requestReader.atom())); + requestReader.consumeChar(')'); + } + + private void consumeMaxsizeOpt(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException { + if (!requestReader.atom().equalsIgnoreCase(MAXSIZE)) { + throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Wrong on, it should be MAXSIZE"); + } + + builder.maxsize(Optional.of((int)requestReader.number(STOP_ON_PAREN))); + requestReader.consumeChar(')'); + } + + private void consumeKey(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException { + builder.keys(ImmutableSet.of(new MailboxAnnotationKey(requestReader.atom()))); + requestReader.eol(); + } + + private void consumeKeys(ImapRequestLineReader requestReader, GetAnnotationRequest.Builder builder) throws DecodingException { + Builder<MailboxAnnotationKey> keys = ImmutableSet.builder(); + + do { + keys.add(new MailboxAnnotationKey(requestReader.atom())); + } while (requestReader.nextWordChar() != ')'); + + builder.keys(keys.build()); + + requestReader.consumeChar(')'); + requestReader.eol(); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/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 ea57de6..c133f51 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 @@ -108,8 +108,9 @@ public class ImapParserFactory implements ImapCommandParserFactory { _imapCommands.put(ImapConstants.SETQUOTA_COMMAND_NAME, SetQuotaCommandParser.class); //RFC5464 - //SETMETADATA + //SETMETADATA, GETMETADATA _imapCommands.put(ImapConstants.SETANNOTATION_COMMAND_NAME, SetAnnotationCommandParser.class); + _imapCommands.put(ImapConstants.GETANNOTATION_COMMAND_NAME, GetAnnotationCommandParser.class); } /** http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java new file mode 100644 index 0000000..0da09fc --- /dev/null +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/request/GetAnnotationRequest.java @@ -0,0 +1,158 @@ +/**************************************************************** + * 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.Set; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; +import org.apache.james.imap.api.ImapCommand; + +import com.google.common.base.Preconditions; +import org.apache.james.mailbox.model.MailboxAnnotationKey; + +public class GetAnnotationRequest extends AbstractImapRequest { + public static class Builder { + private String tag; + private ImapCommand command; + private String mailboxName; + private Set<MailboxAnnotationKey> keys; + private Optional<Integer> maxsize; + private Depth depth; + + private Builder() { + this.depth = Depth.ZERO; + this.maxsize = Optional.absent(); + keys = ImmutableSet.of(); + } + + public Builder tag(String tag) { + this.tag = tag; + return this; + } + + public Builder command(ImapCommand command) { + this.command = command; + return this; + } + + public Builder mailboxName(String mailboxName) { + Preconditions.checkNotNull(mailboxName); + this.mailboxName = mailboxName; + return this; + } + + public Builder keys(Set<MailboxAnnotationKey> keys) { + this.keys = ImmutableSet.copyOf(keys); + return this; + } + + public Builder maxsize(Optional<Integer> maxsize) { + this.maxsize = maxsize; + return this; + } + + public Builder depth(Depth depth) { + this.depth = depth; + return this; + } + + public GetAnnotationRequest build() { + Preconditions.checkState(isNoDepth() || isDepthAndKeysNotEmpty()); + Preconditions.checkArgument(isNoMaxsize() || maxsize.get() > 0); + return new GetAnnotationRequest(this); + } + + private boolean isDepthAndKeysNotEmpty() { + return !Depth.ZERO.equals(depth) && !keys.isEmpty(); + } + + private boolean isNoDepth() { + return Depth.ZERO.equals(depth); + } + + private boolean isNoMaxsize() { + return !maxsize.isPresent(); + } + } + + public static Builder builder() { + return new Builder(); + } + + private final String mailboxName; + private final Set<MailboxAnnotationKey> keys; + private final Optional<Integer> maxsize; + private final Depth depth; + + private GetAnnotationRequest(Builder builder) { + super(builder.tag, builder.command); + this.mailboxName = builder.mailboxName; + this.depth = builder.depth; + this.maxsize = builder.maxsize; + this.keys = builder.keys; + } + + public String getMailboxName() { + return mailboxName; + } + + public Set<MailboxAnnotationKey> getKeys() { + return keys; + } + + public Optional<Integer> getMaxsize() { + return maxsize; + } + + public Depth getDepth() { + return depth; + } + + public enum Depth { + ZERO("0"), ONE("1"), INFINITY("infinity"); + + private final String code; + + Depth(String code) { + this.code = code; + } + + public final String getCode() { + return code; + } + + public String toString() { + return code; + } + + public static Depth fromString(String code) { + Preconditions.checkNotNull(code); + + for (Depth depth : Depth.values()) { + if (code.equalsIgnoreCase(depth.code)) { + return depth; + } + } + + throw new IllegalArgumentException("Cannot lookup Depth data for: " + code); + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/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 c8a8ceb..31b20b8 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 @@ -59,7 +59,9 @@ public class DefaultProcessorChain { if (mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Annotation)) { final SetAnnotationProcessor setAnnotationProcessor = new SetAnnotationProcessor(unsubscribeProcessor, mailboxManager, statusResponseFactory); capabilityProcessor.addProcessor(setAnnotationProcessor); - subscribeProcessor = new SubscribeProcessor(setAnnotationProcessor, mailboxManager, subscriptionManager, statusResponseFactory); + final GetAnnotationProcessor getAnnotationProcessor = new GetAnnotationProcessor(setAnnotationProcessor, mailboxManager, statusResponseFactory); + capabilityProcessor.addProcessor(getAnnotationProcessor); + subscribeProcessor = new SubscribeProcessor(getAnnotationProcessor, mailboxManager, subscriptionManager, statusResponseFactory); } else { subscribeProcessor = new SubscribeProcessor(unsubscribeProcessor, mailboxManager, subscriptionManager, statusResponseFactory); } http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java new file mode 100644 index 0000000..052e533 --- /dev/null +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetAnnotationProcessor.java @@ -0,0 +1,175 @@ +/**************************************************************** + * 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.apache.james.imap.api.message.response.StatusResponse.ResponseCode; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedSet; +import org.apache.commons.lang.NotImplementedException; +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.StatusResponseFactory; +import org.apache.james.imap.api.process.ImapProcessor; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.message.request.GetAnnotationRequest; +import org.apache.james.imap.message.response.AnnotationResponse; +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.MailboxAnnotation; +import org.apache.james.mailbox.model.MailboxAnnotationKey; +import org.apache.james.mailbox.model.MailboxPath; + +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class GetAnnotationProcessor extends AbstractMailboxProcessor<GetAnnotationRequest> implements CapabilityImplementingProcessor { + public GetAnnotationProcessor(ImapProcessor next, MailboxManager mailboxManager, StatusResponseFactory factory) { + super(GetAnnotationRequest.class, next, mailboxManager, factory); + } + + public List<String> getImplementedCapabilities(ImapSession session) { + return ImmutableList.of(ImapConstants.SUPPORTS_ANNOTATION); + } + + protected void doProcess(GetAnnotationRequest message, ImapSession session, String tag, ImapCommand command, + Responder responder) { + try { + proceed(message, session, tag, command, responder); + } catch (MailboxNotFoundException e) { + session.getLog().info("The command: {} is failed because not found mailbox {}", command.getName(), message.getMailboxName()); + no(command, tag, responder, HumanReadableText.FAILURE_NO_SUCH_MAILBOX, ResponseCode.tryCreate()); + } catch (MailboxException e) { + session.getLog().info("The command: {} on mailbox {} is failed", command.getName(), message.getMailboxName()); + no(command, tag, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); + } + } + + private void proceed(GetAnnotationRequest message, ImapSession session, String tag, ImapCommand command, Responder responder) throws MailboxException { + String mailboxName = message.getMailboxName(); + Optional<Integer> maxsize = message.getMaxsize(); + MailboxPath mailboxPath = buildFullPath(session, mailboxName); + + List<MailboxAnnotation> mailboxAnnotations = getMailboxAnnotations(session, message.getKeys(), message.getDepth(), mailboxPath); + Optional<Integer> maximumOversizedSize = getMaxSizeValue(mailboxAnnotations, maxsize); + + respond(tag, command, responder, mailboxName, mailboxAnnotations, maxsize, maximumOversizedSize); + } + + private void respond(String tag, ImapCommand command, Responder responder, String mailboxName, + List<MailboxAnnotation> mailboxAnnotations, Optional<Integer> maxsize, Optional<Integer> maximumOversizedSize) { + if (maximumOversizedSize.isPresent()) { + responder.respond(new AnnotationResponse(mailboxName, filterItemsBySize(responder, mailboxName, mailboxAnnotations, maxsize))); + okComplete(command, tag, ResponseCode.longestMetadataEntry(maximumOversizedSize.get()), responder); + } else { + responder.respond(new AnnotationResponse(mailboxName, mailboxAnnotations)); + okComplete(command, tag, responder); + } + } + + private Optional<Integer> getMaxSizeValue(final List<MailboxAnnotation> mailboxAnnotations, Optional<Integer> maxsize) { + if (maxsize.isPresent()) { + return maxsize.transform(new Function<Integer, Optional<Integer>>() { + @Override + public Optional<Integer> apply(Integer input) { + return getMaxSizeOfOversizedItems(mailboxAnnotations, input); + } + }).get(); + } + return Optional.absent(); + } + + private List<MailboxAnnotation> filterItemsBySize(Responder responder, String mailboxName, List<MailboxAnnotation> mailboxAnnotations, final Optional<Integer> maxsize) { + Predicate<MailboxAnnotation> lowerPredicate = new Predicate<MailboxAnnotation>() { + @Override + public boolean apply(final MailboxAnnotation input) { + return maxsize.transform(new Function<Integer, Boolean>() { + @Override + public Boolean apply(Integer maxSizeInput) { + return (input.size() <= maxSizeInput); + } + }).or(true); + } + }; + + return FluentIterable.from(mailboxAnnotations).filter(lowerPredicate).toList(); + } + + private List<MailboxAnnotation> getMailboxAnnotations(ImapSession session, Set<MailboxAnnotationKey> keys, GetAnnotationRequest.Depth depth, MailboxPath mailboxPath) throws MailboxException { + MailboxSession mailboxSession = ImapSessionUtils.getMailboxSession(session); + switch (depth) { + case ZERO: + return getMailboxAnnotationsWithDepthZero(keys, mailboxPath, mailboxSession); + case ONE: + return getMailboxManager().getAnnotationsByKeysWithOneDepth(mailboxPath, mailboxSession, keys); + case INFINITY: + return getMailboxManager().getAnnotationsByKeysWithAllDepth(mailboxPath, mailboxSession, keys); + default: + throw new NotImplementedException(); + } + } + + private List<MailboxAnnotation> getMailboxAnnotationsWithDepthZero(Set<MailboxAnnotationKey> keys, MailboxPath mailboxPath, MailboxSession mailboxSession) throws MailboxException { + if (keys.isEmpty()) { + return getMailboxManager().getAllAnnotations(mailboxPath, mailboxSession); + } else { + return getMailboxManager().getAnnotationsByKeys(mailboxPath, mailboxSession, keys); + } + } + + private Optional<Integer> getMaxSizeOfOversizedItems(List<MailboxAnnotation> mailboxAnnotations, final Integer maxsize) { + Predicate<MailboxAnnotation> filterOverSizedAnnotation = new Predicate<MailboxAnnotation>() { + @Override + public boolean apply(MailboxAnnotation input) { + return (input.size() > maxsize); + } + }; + + Function<MailboxAnnotation, Integer> transformToSize = new Function<MailboxAnnotation,Integer>(){ + public Integer apply(MailboxAnnotation input) { + return input.size(); + } + }; + + ImmutableSortedSet<Integer> overLimitSizes = FluentIterable.from(mailboxAnnotations) + .filter(filterOverSizedAnnotation) + .transform(transformToSize) + .toSortedSet(new Comparator<Integer>() { + @Override + public int compare(Integer annotationSize1, Integer annotationSize2) { + return annotationSize2.compareTo(annotationSize1); + } + }); + + if (overLimitSizes.isEmpty()) { + return Optional.absent(); + } + return Optional.of(overLimitSizes.first()); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java b/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java index e650685..adcfccb 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/api/message/response/StatusResponseTest.java @@ -19,9 +19,8 @@ package org.apache.james.imap.api.message.response; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; -import org.apache.james.imap.api.message.response.StatusResponse; import org.junit.Test; http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java new file mode 100644 index 0000000..f8ee791 --- /dev/null +++ b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/GetAnnotationCommandParserTest.java @@ -0,0 +1,390 @@ +/**************************************************************** + * 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.InputStream; +import java.io.OutputStream; + +import com.google.common.base.Charsets; +import org.apache.james.imap.api.ImapCommand; +import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.decode.ImapRequestStreamLineReader; +import org.apache.james.imap.message.request.GetAnnotationRequest; +import org.apache.james.imap.message.request.GetAnnotationRequest.Depth; +import org.apache.james.mailbox.model.MailboxAnnotationKey; +import org.apache.james.protocols.imap.DecodingException; +import org.junit.Before; +import org.junit.Test; + +public class GetAnnotationCommandParserTest { + + private static final String INBOX = "anyInboxName"; + private static final String TAG = "A1"; + private static final MailboxAnnotationKey PRIVATE_KEY = new MailboxAnnotationKey("/private/comment"); + private static final MailboxAnnotationKey SHARED_KEY = new MailboxAnnotationKey("/shared/comment"); + private static final ImapCommand command = ImapCommand.anyStateCommand("Command"); + private static final ImapSession session = null; + private static final OutputStream outputStream = null; + + private GetAnnotationCommandParser parser; + + @Before + public void setUp() throws Exception { + parser = new GetAnnotationCommandParser(); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowsExceptionWhenCommandHasNotMailbox() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream(" \n".getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test + public void decodeMessageShouldReturnRequestWhenCommandHasMailboxOnly() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getKeys()).isEmpty(); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).isAbsent(); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasOneKeyButInWrongFormat() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " /private/comment extrastring \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test + public void decodeMessageShouldReturnRequestWhenCommandHasOnlyOneKey() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " /private/comment \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getKeys()).containsOnly(PRIVATE_KEY); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).isAbsent(); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasOneInvalidKey() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + "/shared/comment private/comment \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test + public void decodeMessageShouldReturnRequestWhenCommandHasMultiKeys() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getKeys()).contains(PRIVATE_KEY, SHARED_KEY); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).isAbsent(); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButInWrongFormat() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (/another/key/group)\n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysAndSingleKey() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) /another/key \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButNotOpenQuote() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " /shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMultiKeysButNotCloseQuote() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeOptButInWrongPlace() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeWithWrongValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE invalid) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeWithoutValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeDoesNotInParenthesis() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " MAXSIZE 1024 (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasMaxsizeDoesNotInParenthesisAndNoValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " MAXSIZE (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test + public void decodeMessageShouldReturnRequestWhenCommandHasMaxsizeOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest) parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getKeys()).contains(PRIVATE_KEY, SHARED_KEY); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).contains(1024); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasWrongMaxsizeOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZErr 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasWrongMaxsizeValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 0) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasWrongDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH -1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasWrongDepthOptionName() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTHerr 1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButNoValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButInvalidValue() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH invalid) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionButNotInParenthesis() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " DEPTH (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldReturnRequestWhenCommandHasDepthOptionAndValueButNotInParenthesis() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " DEPTH 1 (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test + public void decodeMessageShouldReturnRequestWithZeroDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 0) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, null); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).contains(1024); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test + public void decodeMessageShouldReturnRequestWithOneDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 1) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.ONE); + assertThat(request.getMaxsize()).contains(1024); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test + public void decodeMessageShouldReturnRequestWhenCommandHasOptionsInAnyOrder() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (DEPTH 1) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.ONE); + assertThat(request.getMaxsize()).contains(1024); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test + public void decodeMessageShouldReturnRequestWithInfinityDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.INFINITY); + assertThat(request.getMaxsize()).contains(1024); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test + public void decodeMessageShouldReturnRequestWithOnlyInfinityDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.INFINITY); + assertThat(request.getMaxsize()).isAbsent(); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test + public void decodeMessageShouldReturnRequestWithDefaultDepthOptionWhenCommandHasDoesNotHaveDepthOption() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (MAXSIZE 1024) (/shared/comment /private/comment) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + GetAnnotationRequest request = (GetAnnotationRequest)parser.decode(command, lineReader, TAG, session); + + assertThat(request.getTag()).isEqualTo(TAG); + assertThat(request.getCommand()).isEqualTo(command); + assertThat(request.getMailboxName()).isEqualTo(INBOX); + assertThat(request.getDepth()).isEqualTo(Depth.ZERO); + assertThat(request.getMaxsize()).contains(1024); + assertThat(request.getKeys()).contains(SHARED_KEY, PRIVATE_KEY); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasOneDepthButWithoutKey() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH 1) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasInfinityDepthButWithoutKey() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (DEPTH infinity) (MAXSIZE 1024) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } + + @Test(expected = DecodingException.class) + public void decodeMessageShouldThrowExceptionWhenCommandHasDepthOptionInWrongPlace() throws DecodingException { + InputStream inputStream = new ByteArrayInputStream((INBOX + " (/shared/comment /private/comment) (DEPTH infinity) \n").getBytes(Charsets.US_ASCII)); + ImapRequestStreamLineReader lineReader = new ImapRequestStreamLineReader(inputStream, outputStream); + + parser.decode(command, lineReader, TAG, session); + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java ---------------------------------------------------------------------- diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java new file mode 100644 index 0000000..5e4fd0d --- /dev/null +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java @@ -0,0 +1,329 @@ +/**************************************************************** + * 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.apache.james.imap.api.message.response.StatusResponse.ResponseCode; +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.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +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.GetAnnotationRequest; +import org.apache.james.imap.message.request.GetAnnotationRequest.Depth; +import org.apache.james.imap.message.response.AnnotationResponse; +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.MailboxAnnotationKey; +import org.apache.james.mailbox.model.MailboxPath; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; + +import java.util.Set; + +public class GetAnnotationProcessorTest { + private static final String TAG = "TAG"; + private static final int FIRST_ELEMENT_INDEX = 0; + + private static final MailboxAnnotationKey PRIVATE_KEY = new MailboxAnnotationKey("/private/comment"); + private static final MailboxAnnotationKey PRIVATE_CHILD_KEY = new MailboxAnnotationKey("/private/comment/user"); + private static final MailboxAnnotationKey PRIVATE_GRANDCHILD_KEY = new MailboxAnnotationKey("/private/comment/user/name"); + private static final MailboxAnnotationKey SHARED_KEY = new MailboxAnnotationKey("/shared/comment"); + + private static final MailboxAnnotation SHARED_ANNOTATION = MailboxAnnotation.newInstance(SHARED_KEY, "The shared size"); + private static final MailboxAnnotation PRIVATE_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_KEY, "The short size"); + private static final MailboxAnnotation PRIVATE_CHILD_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_CHILD_KEY, "The middle size"); + private static final MailboxAnnotation PRIVATE_GRANDCHILD_ANNOTATION = MailboxAnnotation.newInstance(PRIVATE_GRANDCHILD_KEY, "The longest value size"); + + private GetAnnotationProcessor processor; + + private ImapProcessor mockNextProcessor; + private MailboxManager mockMailboxManager; + private StatusResponseFactory mockStatusResponseFactory; + private ImapProcessor.Responder mockResponder; + private ImapSession mockImapSession; + private MailboxSession mailboxSession; + + private Set<MailboxAnnotationKey> KEYS; + private StatusResponse statusResponse; + + private GetAnnotationRequest.Builder annotationRequestBuilder; + private MailboxPath inbox; + private Logger log; + private ArgumentCaptor<HumanReadableText> humanTextCaptor; + private ArgumentCaptor<ResponseCode> captorResponsecode; + private ArgumentCaptor<AnnotationResponse> captorAnnotationResponse; + + private void initAndMockData() { + statusResponse = 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); + + mailboxSession = new MockMailboxSession("username"); + inbox = MailboxPath.inbox(mailboxSession); + KEYS = ImmutableSet.of(PRIVATE_KEY); + annotationRequestBuilder = GetAnnotationRequest.builder() + .tag(TAG) + .command(ImapCommand.anyStateCommand("Name")) + .mailboxName(ImapConstants.INBOX_NAME); + humanTextCaptor = ArgumentCaptor.forClass(HumanReadableText.class); + captorResponsecode = ArgumentCaptor.forClass(ResponseCode.class); + captorAnnotationResponse = ArgumentCaptor.forClass(AnnotationResponse.class); + + when(mockImapSession.getState()).thenReturn(ImapSessionState.SELECTED); + when(mockImapSession.getAttribute(ImapSessionUtils.MAILBOX_SESSION_ATTRIBUTE_SESSION_KEY)).thenReturn(mailboxSession); + when(mockImapSession.getLog()).thenReturn(log); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + initAndMockData(); + + processor = new GetAnnotationProcessor(mockNextProcessor, mockMailboxManager, mockStatusResponseFactory); + } + + @Test + public void getImplementedCapabilitiesShouldContainSupportAnnotationWhenSupportedByMailboxManager() { + assertThat(processor.getImplementedCapabilities(new FakeImapSession())).containsExactly(ImapConstants.SUPPORTS_ANNOTATION); + } + + @Test + public void processShouldResponseNoWithFailureWhenMailboxDoesNotExist() throws Exception { + doThrow(MailboxNotFoundException.class).when(mockMailboxManager).getAllAnnotations(eq(inbox), eq(mailboxSession)); + when(mockStatusResponseFactory.taggedNo(any(String.class), any(ImapCommand.class), any(HumanReadableText.class), any(ResponseCode.class))) + .thenReturn(statusResponse); + + processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedNo(any(String.class), any(ImapCommand.class), humanTextCaptor.capture(), captorResponsecode.capture()); + verify(mockResponder).respond(statusResponse); + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.FAILURE_NO_SUCH_MAILBOX); + assertThat(captorResponsecode.getAllValues()).containsOnly(ResponseCode.tryCreate()); + } + + @Test + public void processShouldResponseNoWithGenericFailureWhenManagerThrowMailboxException() throws Exception { + doThrow(MailboxException.class).when(mockMailboxManager).getAllAnnotations(eq(inbox), eq(mailboxSession)); + when(mockStatusResponseFactory.taggedNo(any(String.class), any(ImapCommand.class), any(HumanReadableText.class))) + .thenReturn(statusResponse); + + processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedNo(any(String.class), any(ImapCommand.class), humanTextCaptor.capture()); + verify(mockResponder).respond(statusResponse); + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); + } + + @Test + public void processShouldGetAllAnnotationsAndReturnCompleteResponse() throws Exception { + processor.process(annotationRequestBuilder.build(), mockResponder, mockImapSession); + + verify(mockMailboxManager, times(1)).getAllAnnotations(inbox, mailboxSession); + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), any(ImapCommand.class), humanTextCaptor.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED); + } + + @Test + public void processShouldGetAnnotationsByKeysAndReturnCompleteResponse() throws Exception { + processor.process(annotationRequestBuilder.keys(KEYS).build(), mockResponder, mockImapSession); + + verify(mockMailboxManager, times(1)).getAnnotationsByKeys(eq(inbox), eq(mailboxSession), eq(KEYS)); + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), any(ImapCommand.class), humanTextCaptor.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED); + } + + @Test + public void processShouldGetAnnotationsAndReturnCompleteResponseWithTheLongestEntryInfoWhenLimitMaxsize() throws Exception { + when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(10)).build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture(), + captorResponsecode.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED); + assertThat(captorResponsecode.getAllValues()).containsOnly(ResponseCode.longestMetadataEntry(22)); + } + + @Test + public void processShouldGetAnnotationsAndReturnCompleteResponseDoesNotTruncateDataByMaxsize() throws Exception { + when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(100)).build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + assertThat(humanTextCaptor.getAllValues()).containsOnly(HumanReadableText.COMPLETED); + } + + @Test + public void processShouldGetAnnotationsAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception { + when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(SHARED_ANNOTATION, PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(15)).build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture(), + any(ResponseCode.class)); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION); + } + + @Test + public void processShouldGetAnnotationsAndReturnCompleteResponseDoesnotTruncateDataByMaxsizeWhenNoMoreOverSizeItem() throws Exception { + when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(SHARED_ANNOTATION, PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(100)).build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION); + } + + @Test + public void processShouldGetAnnotationsByOneDepthAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception { + when(mockMailboxManager.getAnnotationsByKeysWithOneDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(14)).depth(Depth.ONE).keys(KEYS).build(), mockResponder, mockImapSession); + + verify(mockMailboxManager, times(1)).getAnnotationsByKeysWithOneDepth(eq(inbox), eq(mailboxSession), eq(KEYS)); + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture(), + any(ResponseCode.class)); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION); + } + + @Test + public void processShouldGetAnnotationsAndReturnCompleteResponseWithTruncateDataByLessThenOrEqualMaxsize() throws Exception { + when(mockMailboxManager.getAllAnnotations(inbox, mailboxSession)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(15)).build(), mockResponder, mockImapSession); + + verify(mockMailboxManager, times(1)).getAllAnnotations(eq(inbox), eq(mailboxSession)); + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture()); + + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, SHARED_ANNOTATION); + } + + @Test + public void processShouldGetAnnotationsByInfinityDepthAndReturnCompleteResponseWithTruncateDataByMaxsize() throws Exception { + when(mockMailboxManager.getAnnotationsByKeysWithAllDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.maxsize(Optional.of(14)).depth(Depth.INFINITY).keys(KEYS).build(), mockResponder, mockImapSession); + + verify(mockMailboxManager, times(1)).getAnnotationsByKeysWithAllDepth(eq(inbox), eq(mailboxSession), eq(KEYS)); + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture(), + any(ResponseCode.class)); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION); + } + + @Test + public void processShouldGetAnnotationsByInfinityDepthAndReturnCompleteResponse() throws Exception { + when(mockMailboxManager.getAnnotationsByKeysWithAllDepth(inbox, mailboxSession, KEYS)).thenReturn(ImmutableList.of(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION)); + + processor.process(annotationRequestBuilder.depth(Depth.INFINITY).keys(KEYS).build(), mockResponder, mockImapSession); + + verify(mockStatusResponseFactory, times(1)).taggedOk(any(String.class), + any(ImapCommand.class), + humanTextCaptor.capture()); + verify(mockResponder, times(2)).respond(captorAnnotationResponse.capture()); + verifyNoMoreInteractions(mockResponder); + + AnnotationResponse resultAnnotation = captorAnnotationResponse.getAllValues().get(FIRST_ELEMENT_INDEX); + assertThat(resultAnnotation.getMailboxAnnotations()).contains(PRIVATE_ANNOTATION, PRIVATE_CHILD_ANNOTATION, PRIVATE_GRANDCHILD_ANNOTATION); + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/ced2d52f/protocols/src/site/xdoc/imap4.xml ---------------------------------------------------------------------- diff --git a/protocols/src/site/xdoc/imap4.xml b/protocols/src/site/xdoc/imap4.xml index ee37a0a..0ec2cbd 100644 --- a/protocols/src/site/xdoc/imap4.xml +++ b/protocols/src/site/xdoc/imap4.xml @@ -55,8 +55,9 @@ <li>SASL-IR (in release 0.2.1)</li> <li>ENABLE (in release 0.2.1)</li> <li>CONDSTORE (RFC 4551 http://www.ietf.org/rfc/rfc4551.txt in release 0.3)</li> - <li>RESYNCH (RFC 5162 http://www.ietf.org/rfc/rfc5162.txt in trunk)</li> - <li>MOVE (RFC 6851 https://tools.ietf.org/html/rfc6851 in trunk). This is enabled only if you use a MailboxManager exposing the Move capability</li> + <li>RESYNCH (RFC 5162 http://www.ietf.org/rfc/rfc5162.txt on master)</li> + <li>MOVE (RFC 6851 https://tools.ietf.org/html/rfc6851 on master). This is enabled only if you use a MailboxManager exposing the Move capability</li> + <li>METADATA Extension (RFC 5464 http://www.ietf.org/rfc/rfc5464.txt on master). This is enabled only if you use a MailboxManager exposing the Annotation capability</li> </ul> <p>We follow RFC2683 recommendations for our implementations:</p> <ul> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
