MAILBOX-270: Apache James supports metadata extension (RFC5464), store metadata
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/60b612ae Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/60b612ae Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/60b612ae Branch: refs/heads/master Commit: 60b612aed5c9996039c4c2b9731888d0c6d1f941 Parents: 6e153c4 Author: Quynh Nguyen <[email protected]> Authored: Thu Jun 2 16:23:56 2016 +0700 Committer: Benoit Tellier <[email protected]> Committed: Thu Jun 23 11:35:54 2016 +0700 ---------------------------------------------------------------------- mailbox/api/pom.xml | 9 ++ .../apache/james/mailbox/MailboxManager.java | 42 +++++- .../james/mailbox/model/MailboxAnnotation.java | 105 +++++++++++++ .../james/mailbox/MailboxManagerTest.java | 102 +++++++++++++ .../mailbox/model/MailboxAnnotationTest.java | 128 ++++++++++++++++ .../mailbox/caching/CachingMailboxMapper.java | 1 - .../CachingMailboxSessionMapperFactory.java | 9 ++ .../cassandra/CassandraMailboxManager.java | 2 +- .../CassandraMailboxSessionMapperFactory.java | 10 ++ .../mail/CassandraAnnotationMapper.java | 98 ++++++++++++ .../modules/CassandraAnnotationModule.java | 68 +++++++++ .../table/CassandraAnnotationTable.java | 30 ++++ .../cassandra/CassandraMailboxManagerTest.java | 6 +- .../cassandra/mail/CassandraMapperProvider.java | 15 +- .../hbase/HBaseMailboxSessionMapperFactory.java | 9 ++ .../jcr/JCRMailboxSessionMapperFactory.java | 9 ++ .../jpa/JPAMailboxSessionMapperFactory.java | 9 ++ .../MaildirMailboxSessionMapperFactory.java | 10 ++ .../inmemory/InMemoryMailboxManager.java | 2 +- .../InMemoryMailboxSessionMapperFactory.java | 9 ++ .../inmemory/mail/InMemoryAnnotationMapper.java | 112 ++++++++++++++ .../inmemory/mail/InMemoryMapperProvider.java | 7 + mailbox/pom.xml | 5 + .../store/MailboxSessionMapperFactory.java | 15 +- .../mailbox/store/StoreMailboxManager.java | 42 +++++- .../mailbox/store/mail/AnnotationMapper.java | 62 ++++++++ .../StoreMailboxManagerAnnotationTest.java | 148 +++++++++++++++++++ .../store/mail/model/AnnotationMapperTest.java | 117 +++++++++++++++ .../store/mail/model/MapperProvider.java | 3 + mpt/impl/imap-mailbox/elasticsearch/.gitignore | 1 + .../base/MailboxEventAnalyserTest.java | 24 +++ .../FirstUserConnectionFilterThreadTest.java | 23 ++- 32 files changed, 1221 insertions(+), 11 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/api/pom.xml ---------------------------------------------------------------------- diff --git a/mailbox/api/pom.xml b/mailbox/api/pom.xml index 12999c6..9dbca1d 100644 --- a/mailbox/api/pom.xml +++ b/mailbox/api/pom.xml @@ -37,6 +37,10 @@ <artifactId>guava</artifactId> </dependency> <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + </dependency> + <dependency> <groupId>${javax.mail.groupId}</groupId> <artifactId>${javax.mail.artifactId}</artifactId> </dependency> @@ -61,6 +65,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-guava</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java ---------------------------------------------------------------------- diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java index 4e597b8..cfbdc15 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxManager.java @@ -21,6 +21,7 @@ package org.apache.james.mailbox; import java.util.EnumSet; import java.util.List; +import java.util.Set; import org.apache.james.mailbox.exception.BadCredentialsException; import org.apache.james.mailbox.exception.MailboxException; @@ -28,6 +29,7 @@ import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.exception.UnsupportedRightException; import org.apache.james.mailbox.model.MailboxACL; +import org.apache.james.mailbox.model.MailboxAnnotation; import org.apache.james.mailbox.model.MailboxMetaData; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MailboxQuery; @@ -73,11 +75,14 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport { enum MailboxCapabilities { Move, UserFlag, - Namespace + Namespace, + Annotation } EnumSet<MailboxCapabilities> getSupportedMailboxCapabilities(); - + + boolean hasCapability(MailboxCapabilities capability); + enum MessageCapabilities { Attachment } @@ -336,4 +341,37 @@ public interface MailboxManager extends RequestAware, MailboxListenerSupport { */ List<MailboxPath> list(MailboxSession session) throws MailboxException; + /** + * Return all mailbox's annotation as the {@link List} of {@link MailboxAnnotation} without order and not duplicated by key + * + * @param mailboxPath the current mailbox + * @param session the current session + * @return List<MailboxAnnotation> + * @throws MailboxException in case of selected mailbox does not exist + */ + List<MailboxAnnotation> getAllAnnotations(MailboxPath mailboxPath, MailboxSession session) throws MailboxException; + + /** + * Return all mailbox's annotation filter by the list of the keys without order and not duplicated by key + * + * @param mailboxPath the current mailbox + * @param session the current session + * @param keys list of the keys should be filter + * @return List<MailboxAnnotation> + * @throws MailboxException in case of selected mailbox does not exist + */ + List<MailboxAnnotation> getAnnotationsByKeys(MailboxPath mailboxPath, MailboxSession session, Set<String> keys) throws MailboxException; + + /** + * Update the mailbox's annotations. This method can: + * - Insert new annotation if it does not exist + * - Update the new value for existed annotation + * - Delete the existed annotation if its value is nil + * + * @param mailboxPath the current mailbox + * @param session the current session + * @param mailboxAnnotations the list of annotation should be insert/udpate/delete + * @throws MailboxException in case of selected mailbox does not exist + */ + void updateAnnotations(MailboxPath mailboxPath, MailboxSession session, List<MailboxAnnotation> mailboxAnnotations) throws MailboxException; } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotation.java ---------------------------------------------------------------------- diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotation.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotation.java new file mode 100644 index 0000000..b2152d4 --- /dev/null +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxAnnotation.java @@ -0,0 +1,105 @@ +/**************************************************************** + * 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.mailbox.model; + +import org.apache.commons.lang.StringUtils; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; + +public class MailboxAnnotation { + + private static final CharMatcher NAME_ANNOTATION_PATTERN = CharMatcher.ASCII + .and(CharMatcher.inRange('\u0000', '\u0019').negate()).and(CharMatcher.isNot('*')) + .and(CharMatcher.isNot('%')); + + public static final String SLASH_CHARACTER = "/"; + + public static final String TWO_SLASH_CHARACTER = "//"; + + public static MailboxAnnotation nil(String key) { + return new MailboxAnnotation(key, Optional.<String> absent()); + } + + public static MailboxAnnotation newInstance(String key, String value) { + return new MailboxAnnotation(key, Optional.of(value)); + } + + private final String key; + private final Optional<String> value; + + private MailboxAnnotation(String key, Optional<String> value) { + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(value); + Preconditions.checkArgument(isValidKey(key), + "Key must start with '/' and not end with '/' and does not contain charater with hex from '\u0000' to '\u00019' or {'*', '%', two consecutive '/'} "); + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public Optional<String> getValue() { + return value; + } + + private static boolean isValidKey(String input) { + if (StringUtils.isBlank(input)) { + return false; + } + String key = input.trim(); + if (!key.startsWith(SLASH_CHARACTER)) { + return false; + } + if (key.contains(TWO_SLASH_CHARACTER)) { + return false; + } + if (key.endsWith(SLASH_CHARACTER)) { + return false; + } + if (!NAME_ANNOTATION_PATTERN.matchesAllOf(key)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(key, value); + } + + public boolean isNil() { + return !value.isPresent(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof MailboxAnnotation) { + MailboxAnnotation o = (MailboxAnnotation) obj; + return Objects.equal(key, o.getKey()) && Objects.equal(value, o.getValue()); + } else { + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java ---------------------------------------------------------------------- diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java index 6c9e741..8504123 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java @@ -27,9 +27,11 @@ import java.util.List; import javax.mail.Flags; +import org.apache.james.mailbox.MailboxManager.MailboxCapabilities; import org.apache.james.mailbox.exception.BadCredentialsException; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.mock.MockMailboxManager; +import org.apache.james.mailbox.model.MailboxAnnotation; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxMetaData; import org.apache.james.mailbox.model.MailboxPath; @@ -43,6 +45,9 @@ import org.xenei.junit.contract.Contract; import org.xenei.junit.contract.ContractTest; import org.xenei.junit.contract.IProducer; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + /** * Test the {@link StoreMailboxManager} methods that * are not covered by the protocol-tester suite. @@ -58,6 +63,12 @@ public class MailboxManagerTest<T extends MailboxManager> { public final static String USER_1 = "USER_1"; public final static String USER_2 = "USER_2"; + private static final MailboxAnnotation PRIVATE_ANNOTATION = MailboxAnnotation.newInstance("/private/comment", "My private comment"); + private static final MailboxAnnotation PRIVATE_ANNOTATION_UPDATE = MailboxAnnotation.newInstance("/private/comment", "My updated private comment"); + private static final MailboxAnnotation SHARED_ANNOTATION = MailboxAnnotation.newInstance("/shared/comment", "My shared comment"); + + private static final List<MailboxAnnotation> ANNOTATIONS = ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION); + @Rule public ExpectedException expected = ExpectedException.none(); @@ -231,4 +242,95 @@ public class MailboxManagerTest<T extends MailboxManager> { assertThat(metaDatas.get(0).getPath()).isEqualTo(MailboxPath.inbox(session)); } + @ContractTest + public void updateAnnotationsShouldUpdateStoredAnnotation() throws MailboxException { + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + mailboxManager.createMailbox(inbox, session); + + mailboxManager.updateAnnotations(inbox, session, ImmutableList.of(PRIVATE_ANNOTATION)); + + mailboxManager.updateAnnotations(inbox, session, ImmutableList.of(PRIVATE_ANNOTATION_UPDATE)); + assertThat(mailboxManager.getAllAnnotations(inbox, session)).containsOnly(PRIVATE_ANNOTATION_UPDATE); + } + + @ContractTest + public void updateAnnotationsShouldDeleteAnnotationWithNilValue() throws BadCredentialsException, MailboxException { + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + mailboxManager.createMailbox(inbox, session); + + mailboxManager.updateAnnotations(inbox, session, ImmutableList.of(PRIVATE_ANNOTATION)); + + mailboxManager.updateAnnotations(inbox, session, ImmutableList.of(MailboxAnnotation.nil("/private/comment"))); + assertThat(mailboxManager.getAllAnnotations(inbox, session)).isEmpty(); + } + + @ContractTest + public void updateAnnotationsShouldThrowExceptionIfMailboxDoesNotExist() throws MailboxException { + expected.expect(MailboxException.class); + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + + mailboxManager.updateAnnotations(inbox, session, ImmutableList.of(PRIVATE_ANNOTATION)); + } + + @ContractTest + public void getAnnotationsShouldReturnEmptyForNonStoredAnnotation() throws BadCredentialsException, MailboxException { + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + mailboxManager.createMailbox(inbox, session); + + assertThat(mailboxManager.getAllAnnotations(inbox, session)).isEmpty(); + } + + @ContractTest + public void getAllAnnotationsShouldRetrieveStoredAnnotations() throws BadCredentialsException, MailboxException { + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + mailboxManager.createMailbox(inbox, session); + + mailboxManager.updateAnnotations(inbox, session, ANNOTATIONS); + + assertThat(mailboxManager.getAllAnnotations(inbox, session)).isEqualTo(ANNOTATIONS); + } + + @ContractTest + public void getAllAnnotationsShouldThrowExceptionIfMailboxDoesNotExist() throws MailboxException { + expected.expect(MailboxException.class); + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + + mailboxManager.getAllAnnotations(inbox, session); + } + + @ContractTest + public void getAnnotationsByKeysShouldRetrieveStoresAnnotationsByKeys() throws BadCredentialsException, MailboxException { + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + mailboxManager.createMailbox(inbox, session); + + mailboxManager.updateAnnotations(inbox, session, ANNOTATIONS); + + assertThat(mailboxManager.getAnnotationsByKeys(inbox, session, ImmutableSet.of("/private/comment"))) + .containsOnly(PRIVATE_ANNOTATION); + } + + @ContractTest + public void getAnnotationsByKeysShouldThrowExceptionIfMailboxDoesNotExist() throws MailboxException { + expected.expect(MailboxException.class); + Assume.assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.Annotation)); + session = mailboxManager.createSystemSession(USER_2, LoggerFactory.getLogger("Test")); + MailboxPath inbox = MailboxPath.inbox(session); + + mailboxManager.getAnnotationsByKeys(inbox, session, ImmutableSet.of("/private/comment")); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAnnotationTest.java ---------------------------------------------------------------------- diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAnnotationTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAnnotationTest.java new file mode 100644 index 0000000..ba85a58 --- /dev/null +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/MailboxAnnotationTest.java @@ -0,0 +1,128 @@ +/**************************************************************** + * 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.mailbox.model; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.guava.api.Assertions.assertThat; + +import org.junit.Test; + +public class MailboxAnnotationTest { + private static final String ASTERISK_CHARACTER = "*"; + + private static final String PERCENT_CHARACTER = "%"; + + private static final String ANY_KEY = "shared"; + private static final String ANNOTATION_KEY = "/private/comment"; + private static final String ANNOTATION_VALUE = "anyValue"; + + @Test(expected = NullPointerException.class) + public void newInstanceShouldThrowsExceptionWithNullKey() throws Exception { + MailboxAnnotation.newInstance(null, null); + } + + @Test(expected = NullPointerException.class) + public void newInstanceShouldThrowsExceptionWithNullValue() throws Exception { + MailboxAnnotation.newInstance(ANY_KEY, null); + } + + @Test(expected = IllegalArgumentException.class) + public void newInstanceShouldThrowsExceptionWithEmptyKey() throws Exception { + MailboxAnnotation.newInstance("", ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void newInstanceShouldThrowsExceptionWithBlankKey() throws Exception { + MailboxAnnotation.newInstance(" ", ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void newInstanceShouldThrowsExceptionWhenKeyDoesNotStartWithSlash() throws Exception { + MailboxAnnotation.newInstance(ANY_KEY, ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void newInstanceShouldThrowsExceptionWhenKeyContainsAsterisk() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey(ASTERISK_CHARACTER), ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void newInstanceShouldThrowsExceptionWhenKeyContainsPercent() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey(PERCENT_CHARACTER), ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void validKeyShouldThrowsExceptionWhenKeyContainsTwoConsecutiveSlash() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey(MailboxAnnotation.TWO_SLASH_CHARACTER), ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void validKeyShouldThrowsExceptionWhenKeyEndsWithSlash() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey(MailboxAnnotation.SLASH_CHARACTER) + MailboxAnnotation.SLASH_CHARACTER, + ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void validKeyShouldThrowsExceptionWhenKeyContainsNonASCII() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey("â¬Ã¡"), ANNOTATION_VALUE); + } + + @Test(expected = IllegalArgumentException.class) + public void validKeyShouldThrowsExceptionWhenKeyContainsTabCharacter() throws Exception { + MailboxAnnotation.newInstance(buildAnnotationKey("\t"), ANNOTATION_VALUE); + } + + @Test + public void nilInstanceShouldReturnAbsentValue() throws Exception { + MailboxAnnotation annotation = MailboxAnnotation.nil(ANNOTATION_KEY); + + assertThat(annotation.getValue()).isAbsent(); + } + + @Test + public void isNilShouldReturnTrueForNilObject() throws Exception { + MailboxAnnotation nilAnnotation = MailboxAnnotation.nil(ANNOTATION_KEY); + assertThat(nilAnnotation.isNil()).isTrue(); + } + + @Test + public void isNilShouldReturnFalseForNotNilObject() throws Exception { + MailboxAnnotation nilAnnotation = MailboxAnnotation.newInstance(ANNOTATION_KEY, ANY_KEY); + assertThat(nilAnnotation.isNil()).isFalse(); + } + + @Test(expected = NullPointerException.class) + public void newInstanceMailboxAnnotationShouldThrowExceptionWithNullValue() throws Exception { + MailboxAnnotation.newInstance(ANY_KEY, null); + } + + @Test + public void newInstanceMailboxAnnotationShouldCreateNewInstance() throws Exception { + MailboxAnnotation annotation = MailboxAnnotation.newInstance(ANNOTATION_KEY, ANNOTATION_VALUE); + + assertThat(annotation.getKey()).contains(ANNOTATION_KEY); + assertThat(annotation.getValue()).contains(ANNOTATION_VALUE); + } + + private String buildAnnotationKey(String apartOfKey) { + return MailboxAnnotation.SLASH_CHARACTER + ANY_KEY + apartOfKey + ANY_KEY; + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxMapper.java b/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxMapper.java index 8c5ee7f..3c034a9 100644 --- a/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxMapper.java +++ b/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxMapper.java @@ -86,5 +86,4 @@ public class CachingMailboxMapper implements MailboxMapper { cache.invalidate(mailbox); } - } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxSessionMapperFactory.java b/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxSessionMapperFactory.java index 0495d59..2b9ee48 100644 --- a/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxSessionMapperFactory.java +++ b/mailbox/caching/src/main/java/org/apache/james/mailbox/caching/CachingMailboxSessionMapperFactory.java @@ -1,13 +1,16 @@ package org.apache.james.mailbox.caching; +import org.apache.commons.lang.NotImplementedException; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; /** @@ -52,4 +55,10 @@ public class CachingMailboxSessionMapperFactory extends return new NoopAttachmentMapper(); } + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + throw new NotImplementedException(); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java index 1e087bd..2219604 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java @@ -64,7 +64,7 @@ public class CassandraMailboxManager extends StoreMailboxManager { @Override public EnumSet<MailboxManager.MailboxCapabilities> getSupportedMailboxCapabilities() { - return EnumSet.of(MailboxCapabilities.Move, MailboxCapabilities.UserFlag, MailboxCapabilities.Namespace); + return EnumSet.of(MailboxCapabilities.Move, MailboxCapabilities.UserFlag, MailboxCapabilities.Namespace, MailboxCapabilities.Annotation); } @Override http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java index 7e18c26..77cf9d7 100644 --- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java @@ -23,15 +23,19 @@ import javax.inject.Inject; import org.apache.james.backends.cassandra.init.CassandraTypesProvider; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper; import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper; import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper; import org.apache.james.mailbox.cassandra.mail.CassandraMessageMapper; import org.apache.james.mailbox.cassandra.user.CassandraSubscriptionMapper; +import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.ModSeqProvider; import org.apache.james.mailbox.store.mail.UidProvider; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; import com.datastax.driver.core.Session; @@ -93,4 +97,10 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa Session getSession() { return session; } + + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession mailboxSession) + throws MailboxException { + return new CassandraAnnotationMapper((CassandraId)mailboxId, session); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAnnotationMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAnnotationMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAnnotationMapper.java new file mode 100644 index 0000000..a96ea69 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraAnnotationMapper.java @@ -0,0 +1,98 @@ +/**************************************************************** + * 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.mailbox.cassandra.mail; + +import static com.datastax.driver.core.querybuilder.QueryBuilder.delete; +import static com.datastax.driver.core.querybuilder.QueryBuilder.eq; +import static com.datastax.driver.core.querybuilder.QueryBuilder.in; +import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto; +import static com.datastax.driver.core.querybuilder.QueryBuilder.select; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.james.backends.cassandra.utils.CassandraUtils; +import org.apache.james.mailbox.cassandra.CassandraId; +import org.apache.james.mailbox.cassandra.table.CassandraAnnotationTable; +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.store.mail.AnnotationMapper; +import org.apache.james.mailbox.store.transaction.NonTransactionalMapper; + +import com.datastax.driver.core.Row; +import com.datastax.driver.core.Session; +import com.datastax.driver.core.querybuilder.Select; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public class CassandraAnnotationMapper extends NonTransactionalMapper implements AnnotationMapper { + + private final CassandraId mailboxId; + private final Session session; + + public CassandraAnnotationMapper(CassandraId mailboxId, Session session) { + this.mailboxId = mailboxId; + this.session = session; + } + + public List<MailboxAnnotation> getAllAnnotations() { + return CassandraUtils.convertToStream(session.execute(getStoredAnnotationsQuery())) + .map(this::toAnnotation) + .collect(Collectors.toList()); + } + + public List<MailboxAnnotation> getAnnotationsByKeys(Set<String> keys) { + return CassandraUtils.convertToStream(session.execute(getStoredAnnotationsQueryForKeys(keys))) + .map(this::toAnnotation) + .collect(Collectors.toList()); + } + + public void deleteAnnotation(String key) { + session.execute(delete().from(CassandraAnnotationTable.TABLE_NAME) + .where(eq(CassandraAnnotationTable.MAILBOX_ID, mailboxId.asUuid())) + .and(eq(CassandraAnnotationTable.KEY, key))); + } + + public void insertAnnotation(MailboxAnnotation mailboxAnnotation) { + Preconditions.checkArgument(!mailboxAnnotation.isNil()); + session.execute(insertInto(CassandraAnnotationTable.TABLE_NAME) + .value(CassandraAnnotationTable.MAILBOX_ID, mailboxId.asUuid()) + .value(CassandraAnnotationTable.KEY, mailboxAnnotation.getKey()) + .value(CassandraAnnotationTable.VALUE, mailboxAnnotation.getValue().get())); + } + + private MailboxAnnotation toAnnotation(Row row) { + return MailboxAnnotation.newInstance(row.getString(CassandraAnnotationTable.KEY), row.getString(CassandraAnnotationTable.VALUE)); + } + + private Select.Where getStoredAnnotationsQuery() { + return select(CassandraAnnotationTable.SELECT_FIELDS) + .from(CassandraAnnotationTable.TABLE_NAME) + .where(eq(CassandraAnnotationTable.MAILBOX_ID, mailboxId.asUuid())); + } + + private Select.Where getStoredAnnotationsQueryForKeys(Set<String> keys) { + return getStoredAnnotationsQuery().and(in(CassandraAnnotationTable.KEY, Lists.newArrayList(keys))); + } + + @Override + public void endRequest() { + } +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAnnotationModule.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAnnotationModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAnnotationModule.java new file mode 100644 index 0000000..443a065 --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraAnnotationModule.java @@ -0,0 +1,68 @@ +/**************************************************************** + * 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.mailbox.cassandra.modules; + +import static com.datastax.driver.core.DataType.text; +import static com.datastax.driver.core.DataType.timeuuid; + +import java.util.Collections; +import java.util.List; + +import org.apache.james.backends.cassandra.components.CassandraIndex; +import org.apache.james.backends.cassandra.components.CassandraModule; +import org.apache.james.backends.cassandra.components.CassandraTable; +import org.apache.james.backends.cassandra.components.CassandraType; +import org.apache.james.mailbox.cassandra.table.CassandraAnnotationTable; + +import com.datastax.driver.core.schemabuilder.SchemaBuilder; + +public class CassandraAnnotationModule implements CassandraModule { + private final List<CassandraTable> tables; + private final List<CassandraIndex> index; + private final List<CassandraType> types; + + public CassandraAnnotationModule() { + tables = Collections.singletonList( + new CassandraTable(CassandraAnnotationTable.TABLE_NAME, + SchemaBuilder.createTable(CassandraAnnotationTable.TABLE_NAME) + .ifNotExists() + .addPartitionKey(CassandraAnnotationTable.MAILBOX_ID, timeuuid()) + .addClusteringColumn(CassandraAnnotationTable.KEY, text()) + .addColumn(CassandraAnnotationTable.VALUE, text()))); + index = Collections.emptyList(); + types = Collections.emptyList(); + } + + @Override + public List<CassandraTable> moduleTables() { + return tables; + } + + @Override + public List<CassandraIndex> moduleIndex() { + return index; + } + + @Override + public List<CassandraType> moduleTypes() { + return types; + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAnnotationTable.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAnnotationTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAnnotationTable.java new file mode 100644 index 0000000..fd9631b --- /dev/null +++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraAnnotationTable.java @@ -0,0 +1,30 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.mailbox.cassandra.table; + +public interface CassandraAnnotationTable { + String TABLE_NAME = "annotation"; + + String MAILBOX_ID = "mailboxId"; + String KEY = "key"; + String VALUE = "value"; + + String[] SELECT_FIELDS = { KEY, VALUE }; +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java index 88387b1..230992d 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java @@ -23,13 +23,14 @@ import org.apache.james.backends.cassandra.init.CassandraModuleComposite; import org.apache.james.mailbox.cassandra.mail.CassandraModSeqProvider; import org.apache.james.mailbox.cassandra.mail.CassandraUidProvider; import org.apache.james.mailbox.cassandra.modules.CassandraAclModule; +import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule; import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule; import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule; +import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule; import org.apache.james.mailbox.cassandra.modules.CassandraSubscriptionModule; import org.apache.james.mailbox.cassandra.modules.CassandraUidModule; -import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.store.NoMailboxPathLocker; import org.apache.james.mailbox.store.mail.model.impl.MessageParser; @@ -53,7 +54,8 @@ public class CassandraMailboxManagerTest { new CassandraUidModule(), new CassandraModSeqModule(), new CassandraSubscriptionModule(), - new CassandraAttachmentModule())); + new CassandraAttachmentModule(), + new CassandraAnnotationModule())); private IProducer<CassandraMailboxManager> producer = new IProducer<CassandraMailboxManager>() { http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java index 0bb915b..af504f6 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java @@ -23,6 +23,7 @@ import org.apache.james.backends.cassandra.init.CassandraModuleComposite; import org.apache.james.mailbox.cassandra.CassandraId; import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory; import org.apache.james.mailbox.cassandra.modules.CassandraAclModule; +import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule; import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule; import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule; @@ -31,6 +32,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule; import org.apache.james.mailbox.cassandra.modules.CassandraUidModule; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.mock.MockMailboxSession; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; @@ -45,7 +47,8 @@ public class CassandraMapperProvider implements MapperProvider { new CassandraMailboxCounterModule(), new CassandraModSeqModule(), new CassandraUidModule(), - new CassandraAttachmentModule())); + new CassandraAttachmentModule(), + new CassandraAnnotationModule())); @Override public MailboxMapper createMailboxMapper() throws MailboxException { @@ -96,4 +99,14 @@ public class CassandraMapperProvider implements MapperProvider { public boolean supportPartialAttachmentFetch() { return true; } + + @Override + public AnnotationMapper createAnnotationMapper() throws MailboxException { + return new CassandraMailboxSessionMapperFactory( + new CassandraUidProvider(cassandra.getConf()), + new CassandraModSeqProvider(cassandra.getConf()), + cassandra.getConf(), + cassandra.getTypesProvider() + ).getAnnotationMapper(CassandraId.timeBased(), new MockMailboxSession("benwa")); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxSessionMapperFactory.java b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxSessionMapperFactory.java index 0f93752..b2972a2 100644 --- a/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxSessionMapperFactory.java +++ b/mailbox/hbase/src/main/java/org/apache/james/mailbox/hbase/HBaseMailboxSessionMapperFactory.java @@ -29,6 +29,7 @@ import static org.apache.james.mailbox.hbase.HBaseNames.SUBSCRIPTION_CF; import java.io.IOException; +import org.apache.commons.lang.NotImplementedException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; @@ -43,12 +44,14 @@ import org.apache.james.mailbox.hbase.mail.HBaseMailboxMapper; import org.apache.james.mailbox.hbase.mail.HBaseMessageMapper; import org.apache.james.mailbox.hbase.user.HBaseSubscriptionMapper; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.ModSeqProvider; import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; import org.apache.james.mailbox.store.mail.UidProvider; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; /** @@ -170,4 +173,10 @@ public class HBaseMailboxSessionMapperFactory extends MailboxSessionMapperFactor public UidProvider getUidProvider() { return uidProvider; } + + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + throw new NotImplementedException(); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxSessionMapperFactory.java b/mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxSessionMapperFactory.java index 151fbb3..6fb5e43 100644 --- a/mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxSessionMapperFactory.java +++ b/mailbox/jcr/src/main/java/org/apache/james/mailbox/jcr/JCRMailboxSessionMapperFactory.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.mailbox.jcr; +import org.apache.commons.lang.NotImplementedException; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; @@ -25,12 +26,14 @@ import org.apache.james.mailbox.jcr.mail.JCRMailboxMapper; import org.apache.james.mailbox.jcr.mail.JCRMessageMapper; import org.apache.james.mailbox.jcr.user.JCRSubscriptionMapper; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.ModSeqProvider; import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; import org.apache.james.mailbox.store.mail.UidProvider; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; /** @@ -83,4 +86,10 @@ public class JCRMailboxSessionMapperFactory extends MailboxSessionMapperFactory return repository; } + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + throw new NotImplementedException(); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java index 7a3e4a1..9485ef5 100644 --- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java +++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/JPAMailboxSessionMapperFactory.java @@ -21,18 +21,21 @@ package org.apache.james.mailbox.jpa; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import org.apache.commons.lang.NotImplementedException; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.jpa.mail.JPAMailboxMapper; import org.apache.james.mailbox.jpa.mail.JPAMessageMapper; import org.apache.james.mailbox.jpa.user.JPASubscriptionMapper; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.ModSeqProvider; import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; import org.apache.james.mailbox.store.mail.UidProvider; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; /** @@ -81,4 +84,10 @@ public class JPAMailboxSessionMapperFactory extends MailboxSessionMapperFactory return entityManagerFactory.createEntityManager(); } + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + throw new NotImplementedException(); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirMailboxSessionMapperFactory.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirMailboxSessionMapperFactory.java index bd4c3f4..b129071 100644 --- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirMailboxSessionMapperFactory.java +++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/MaildirMailboxSessionMapperFactory.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.mailbox.maildir; +import org.apache.commons.lang.NotImplementedException; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; @@ -25,10 +26,12 @@ import org.apache.james.mailbox.maildir.mail.MaildirMailboxMapper; import org.apache.james.mailbox.maildir.mail.MaildirMessageMapper; import org.apache.james.mailbox.maildir.user.MaildirSubscriptionMapper; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.NoopAttachmentMapper; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; public class MaildirMailboxSessionMapperFactory extends @@ -66,4 +69,11 @@ public class MaildirMailboxSessionMapperFactory extends return new NoopAttachmentMapper(); } + + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + throw new NotImplementedException(); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManager.java ---------------------------------------------------------------------- diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManager.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManager.java index 0986aff..78bac42 100644 --- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManager.java +++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxManager.java @@ -44,7 +44,7 @@ public class InMemoryMailboxManager extends StoreMailboxManager { @Override public EnumSet<MailboxCapabilities> getSupportedMailboxCapabilities() { - return EnumSet.of(MailboxCapabilities.Move, MailboxCapabilities.UserFlag, MailboxCapabilities.Namespace); + return EnumSet.of(MailboxCapabilities.Move, MailboxCapabilities.UserFlag, MailboxCapabilities.Namespace, MailboxCapabilities.Annotation); } @Override http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java index 25f0adc..fe70b1c 100644 --- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java +++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/InMemoryMailboxSessionMapperFactory.java @@ -21,6 +21,7 @@ package org.apache.james.mailbox.inmemory; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; +import org.apache.james.mailbox.inmemory.mail.InMemoryAnnotationMapper; import org.apache.james.mailbox.inmemory.mail.InMemoryAttachmentMapper; import org.apache.james.mailbox.inmemory.mail.InMemoryMailboxMapper; import org.apache.james.mailbox.inmemory.mail.InMemoryMessageMapper; @@ -28,9 +29,11 @@ import org.apache.james.mailbox.inmemory.mail.InMemoryModSeqProvider; import org.apache.james.mailbox.inmemory.mail.InMemoryUidProvider; import org.apache.james.mailbox.inmemory.user.InMemorySubscriptionMapper; import org.apache.james.mailbox.store.MailboxSessionMapperFactory; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.user.SubscriptionMapper; public class InMemoryMailboxSessionMapperFactory extends MailboxSessionMapperFactory { @@ -73,4 +76,10 @@ public class InMemoryMailboxSessionMapperFactory extends MailboxSessionMapperFac ((InMemorySubscriptionMapper) subscriptionMapper).deleteAll(); } + @Override + public AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) + throws MailboxException { + return new InMemoryAnnotationMapper((InMemoryId)mailboxId); + } + } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAnnotationMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAnnotationMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAnnotationMapper.java new file mode 100644 index 0000000..66def77 --- /dev/null +++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryAnnotationMapper.java @@ -0,0 +1,112 @@ +/**************************************************************** + * 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.mailbox.inmemory.mail; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.inmemory.InMemoryId; +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.store.mail.AnnotationMapper; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Table; + +public class InMemoryAnnotationMapper implements AnnotationMapper { + private final InMemoryId mailboxId; + private final Table<InMemoryId, String, String> mailboxesAnnotations; + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + public InMemoryAnnotationMapper(InMemoryId mailboxId) { + this.mailboxId = mailboxId; + mailboxesAnnotations = HashBasedTable.create(); + } + + @Override + public void endRequest() { + + } + + @Override + public <T> T execute(Transaction<T> transaction) throws MailboxException { + return transaction.run(); + } + + private Iterable<MailboxAnnotation> retrieveAllAnnotations(InMemoryId maiboxId) { + lock.readLock().lock(); + try { + return Iterables.transform( + mailboxesAnnotations.row(maiboxId).entrySet(), + new Function<Map.Entry<String, String>, MailboxAnnotation>() { + public MailboxAnnotation apply(Entry<String, String> input) { + return MailboxAnnotation.newInstance(input.getKey(), input.getValue()); + } + }); + } finally { + lock.readLock().unlock(); + } + } + + @Override + public List<MailboxAnnotation> getAllAnnotations() { + return ImmutableList.copyOf(retrieveAllAnnotations(mailboxId)); + } + + @Override + public List<MailboxAnnotation> getAnnotationsByKeys(final Set<String> keys) { + return ImmutableList.copyOf( + Iterables.filter(retrieveAllAnnotations(mailboxId), + new Predicate<MailboxAnnotation>() { + public boolean apply(MailboxAnnotation input) { + return keys.contains(input.getKey()); + } + })); + } + + @Override + public void insertAnnotation(MailboxAnnotation mailboxAnnotation) { + lock.writeLock().lock(); + try { + mailboxesAnnotations.put(mailboxId, mailboxAnnotation.getKey(), mailboxAnnotation.getValue().get()); + } finally { + lock.writeLock().unlock(); + } + } + + @Override + public void deleteAnnotation(String key) { + lock.writeLock().lock(); + try { + mailboxesAnnotations.remove(mailboxId, key); + } finally { + lock.writeLock().unlock(); + } + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java ---------------------------------------------------------------------- diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java index 39a3623..b489e6f 100644 --- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java +++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/mail/InMemoryMapperProvider.java @@ -6,6 +6,7 @@ import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.inmemory.InMemoryMailboxSessionMapperFactory; import org.apache.james.mailbox.mock.MockMailboxSession; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MessageMapper; @@ -53,4 +54,10 @@ public class InMemoryMapperProvider implements MapperProvider { public boolean supportPartialAttachmentFetch() { return false; } + + @Override + public AnnotationMapper createAnnotationMapper() throws MailboxException { + return new InMemoryMailboxSessionMapperFactory().createAnnotationMapper(InMemoryId.of(random.nextInt()), + new MockMailboxSession("user")); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/pom.xml ---------------------------------------------------------------------- diff --git a/mailbox/pom.xml b/mailbox/pom.xml index a5e5d64..a7e1974 100644 --- a/mailbox/pom.xml +++ b/mailbox/pom.xml @@ -592,6 +592,11 @@ <!-- END HBASE/HADOOP --> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-guava</artifactId> + <version>1.3.1</version> + </dependency> </dependencies> </dependencyManagement> http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxSessionMapperFactory.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxSessionMapperFactory.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxSessionMapperFactory.java index c5cc747..383c3a3 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxSessionMapperFactory.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/MailboxSessionMapperFactory.java @@ -22,12 +22,14 @@ import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.RequestAware; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.AttachmentMapper; import org.apache.james.mailbox.store.mail.AttachmentMapperFactory; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.MailboxMapperFactory; import org.apache.james.mailbox.store.mail.MessageMapper; import org.apache.james.mailbox.store.mail.MessageMapperFactory; +import org.apache.james.mailbox.store.mail.model.MailboxId; import org.apache.james.mailbox.store.transaction.Mapper; import org.apache.james.mailbox.store.user.SubscriptionMapper; import org.apache.james.mailbox.store.user.SubscriptionMapperFactory; @@ -42,7 +44,7 @@ public abstract class MailboxSessionMapperFactory implements RequestAware, Mailb protected final static String MESSAGEMAPPER ="MESSAGEMAPPER"; protected final static String MAILBOXMAPPER ="MAILBOXMAPPER"; protected final static String SUBSCRIPTIONMAPPER ="SUBSCRIPTIONMAPPER"; - + protected final static String ANNOTATIONMAPPER = "ANNOTATIONMAPPER"; /** @@ -66,6 +68,17 @@ public abstract class MailboxSessionMapperFactory implements RequestAware, Mailb return mapper; } + public AnnotationMapper getAnnotationMapper(MailboxId mailboxId, MailboxSession session) throws MailboxException { + AnnotationMapper mapper = (AnnotationMapper)session.getAttributes().get(ANNOTATIONMAPPER); + if (mapper == null) { + mapper = createAnnotationMapper(mailboxId, session); + session.getAttributes().put(ANNOTATIONMAPPER, mapper); + } + return mapper; + } + + public abstract AnnotationMapper createAnnotationMapper(MailboxId mailboxId, MailboxSession session) throws MailboxException; + /** * Create a {@link MessageMapper} instance which will get reused during the whole {@link MailboxSession} * http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java index 4dbb447..1d671af 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java @@ -25,6 +25,7 @@ import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Random; +import java.util.Set; import javax.annotation.PostConstruct; import javax.inject.Inject; @@ -45,6 +46,7 @@ import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.model.MailboxACL; +import org.apache.james.mailbox.model.MailboxAnnotation; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxMetaData; import org.apache.james.mailbox.model.MailboxMetaData.Selectability; @@ -57,6 +59,7 @@ import org.apache.james.mailbox.quota.QuotaRootResolver; import org.apache.james.mailbox.store.event.DefaultDelegatingMailboxListener; import org.apache.james.mailbox.store.event.DelegatingMailboxListener; import org.apache.james.mailbox.store.event.MailboxEventDispatcher; +import org.apache.james.mailbox.store.mail.AnnotationMapper; import org.apache.james.mailbox.store.mail.MailboxMapper; import org.apache.james.mailbox.store.mail.model.Mailbox; import org.apache.james.mailbox.store.mail.model.impl.MessageParser; @@ -134,7 +137,6 @@ public class StoreMailboxManager implements MailboxManager { public void setMailboxSessionIdGenerator(MailboxSessionIdGenerator idGenerator) { this.idGenerator = idGenerator; } - public void setQuotaManager(QuotaManager quotaManager) { this.quotaManager = quotaManager; } @@ -701,4 +703,42 @@ public class StoreMailboxManager implements MailboxManager { ); } + private AnnotationMapper getAnnotationMapper(MailboxPath mailboxPath, MailboxSession session) + throws MailboxException, MailboxNotFoundException { + MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session); + Mailbox mailbox = mailboxMapper.findMailboxByPath(mailboxPath); + AnnotationMapper annotationMapper = mailboxSessionMapperFactory.getAnnotationMapper(mailbox.getMailboxId(), session); + return annotationMapper; + } + + @Override + public List<MailboxAnnotation> getAllAnnotations(MailboxPath mailboxPath, MailboxSession session) throws MailboxException { + AnnotationMapper annotationMapper = getAnnotationMapper(mailboxPath, session); + return annotationMapper.getAllAnnotations(); + } + + @Override + public List<MailboxAnnotation> getAnnotationsByKeys(MailboxPath mailboxPath, MailboxSession session, Set<String> keys) + throws MailboxException { + AnnotationMapper annotationMapper = getAnnotationMapper(mailboxPath, session); + return annotationMapper.getAnnotationsByKeys(keys); + } + + @Override + public void updateAnnotations(MailboxPath mailboxPath, MailboxSession session, List<MailboxAnnotation> mailboxAnnotations) + throws MailboxException { + AnnotationMapper annotationMapper = getAnnotationMapper(mailboxPath, session); + for (MailboxAnnotation annotation : mailboxAnnotations) { + if (annotation.isNil()) { + annotationMapper.deleteAnnotation(annotation.getKey()); + } else { + annotationMapper.insertAnnotation(annotation); + } + } + } + + @Override + public boolean hasCapability(MailboxCapabilities capability) { + return getSupportedMailboxCapabilities().contains(capability); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AnnotationMapper.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AnnotationMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AnnotationMapper.java new file mode 100644 index 0000000..d2b6e4d --- /dev/null +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/AnnotationMapper.java @@ -0,0 +1,62 @@ +/**************************************************************** + * 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.mailbox.store.mail; + +import java.util.List; +import java.util.Set; + +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.store.transaction.Mapper; + +public interface AnnotationMapper extends Mapper { + + /** + * Return all <code>MailboxAnnotation</code>. + * The result is not ordered and should not contain duplicate by key + * + * @return List<MailboxAnnotation> + */ + List<MailboxAnnotation> getAllAnnotations(); + + /** + * Search all the <code>MailboxAnnotation</code> by the set of annotation's keys. The result is not ordered and should not + * contain duplicate by key + * + * @param keys the set of the key should be filtered + * @return List<MailboxAnnotation> + */ + List<MailboxAnnotation> getAnnotationsByKeys(Set<String> keys); + + /** + * Delete the annotation by its key. + * + * @param key the key of annotation should be deleted + */ + void deleteAnnotation(String key); + + /** + * - Insert new annotation if it does not exist on store + * - Update the new value for existed annotation + * + * @param mailboxAnnotation the selected annotation + */ + void insertAnnotation(MailboxAnnotation mailboxAnnotation); + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/60b612ae/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxManagerAnnotationTest.java ---------------------------------------------------------------------- diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxManagerAnnotationTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxManagerAnnotationTest.java new file mode 100644 index 0000000..22efc19 --- /dev/null +++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/StoreMailboxManagerAnnotationTest.java @@ -0,0 +1,148 @@ +/**************************************************************** + * 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.mailbox.store; + +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.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.acl.GroupMembershipResolver; +import org.apache.james.mailbox.acl.MailboxACLResolver; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.model.MailboxAnnotation; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.store.mail.AnnotationMapper; +import org.apache.james.mailbox.store.mail.MailboxMapper; +import org.apache.james.mailbox.store.mail.model.Mailbox; +import org.apache.james.mailbox.store.mail.model.MailboxId; +import org.apache.james.mailbox.store.mail.model.impl.MessageParser; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + +public class StoreMailboxManagerAnnotationTest { + private static final MailboxAnnotation PRIVATE_ANNOTATION = MailboxAnnotation.newInstance("/private/comment", "My private comment"); + private static final MailboxAnnotation SHARED_ANNOTATION = MailboxAnnotation.newInstance("/shared/comment", "My shared comment"); + private static final Set<String> KEYS = ImmutableSet.of("/private/comment"); + + private static final List<MailboxAnnotation> ANNOTATIONS = ImmutableList.of(PRIVATE_ANNOTATION, SHARED_ANNOTATION); + private static final List<MailboxAnnotation> ANNOTATIONS_WITH_NIL_ENTRY = ImmutableList.of(PRIVATE_ANNOTATION, MailboxAnnotation.nil("/shared/comment")); + + @Mock private MailboxSessionMapperFactory mailboxSessionMapperFactory; + @Mock private Authenticator authenticator; + @Mock private MailboxACLResolver aclResolver; + @Mock private GroupMembershipResolver groupMembershipResolver; + + @Mock private MailboxSession session; + @Mock private MailboxMapper mailboxMapper; + @Mock private AnnotationMapper annotationMapper; + @Mock private MailboxPath mailboxPath; + @Mock private Mailbox mailbox; + @Mock private MessageParser messageParser; + @Mock private MailboxId mailboxId; + + @InjectMocks + private StoreMailboxManager storeMailboxManager; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mailboxSessionMapperFactory.getMailboxMapper(eq(session))).thenReturn(mailboxMapper); + when(mailbox.getMailboxId()).thenReturn(mailboxId); + when(mailboxSessionMapperFactory.getAnnotationMapper(eq(mailboxId), eq(session))).thenReturn(annotationMapper); + + storeMailboxManager = new StoreMailboxManager(mailboxSessionMapperFactory, authenticator, aclResolver, groupMembershipResolver, messageParser); + } + + @Test(expected = MailboxException.class) + public void updateAnnotationsShouldThrowExceptionWhenDoesNotLookupMailbox() throws Exception { + doThrow(MailboxException.class).when(mailboxMapper).findMailboxByPath(eq(mailboxPath)); + storeMailboxManager.updateAnnotations(mailboxPath, session, ImmutableList.of(PRIVATE_ANNOTATION)); + } + + @Test + public void updateAnnotationsShouldCallAnnotationMapperToInsertAnnotation() throws Exception { + when(mailboxMapper.findMailboxByPath(eq(mailboxPath))).thenReturn(mailbox); + storeMailboxManager.updateAnnotations(mailboxPath, session, ANNOTATIONS); + + verify(annotationMapper, times(2)).insertAnnotation(any(MailboxAnnotation.class)); + } + + @Test + public void updateAnnotationsShouldCallAnnotationMapperToDeleteAnnotation() throws Exception { + when(mailboxMapper.findMailboxByPath(eq(mailboxPath))).thenReturn(mailbox); + storeMailboxManager.updateAnnotations(mailboxPath, session, ANNOTATIONS_WITH_NIL_ENTRY); + + verify(annotationMapper, times(1)).insertAnnotation(eq(PRIVATE_ANNOTATION)); + verify(annotationMapper, times(1)).deleteAnnotation(eq("/shared/comment")); + } + + @Test(expected = MailboxException.class) + public void getAllAnnotationsShouldThrowExceptionWhenDoesNotLookupMailbox() throws Exception { + doThrow(MailboxException.class).when(mailboxMapper).findMailboxByPath(eq(mailboxPath)); + storeMailboxManager.getAllAnnotations(mailboxPath, session); + } + + @Test + public void getAllAnnotationsShouldReturnEmptyForNonStoredAnnotation() throws Exception { + when(mailboxMapper.findMailboxByPath(eq(mailboxPath))).thenReturn(mailbox); + when(annotationMapper.getAllAnnotations()).thenReturn(Collections.<MailboxAnnotation> emptyList()); + + assertThat(storeMailboxManager.getAllAnnotations(mailboxPath, session)).isEmpty(); + } + + @Test + public void getAllAnnotationsShouldReturnStoredAnnotation() throws Exception { + when(mailboxMapper.findMailboxByPath(eq(mailboxPath))).thenReturn(mailbox); + when(annotationMapper.getAllAnnotations()).thenReturn(ANNOTATIONS); + + assertThat(storeMailboxManager.getAllAnnotations(mailboxPath, session)).isEqualTo(ANNOTATIONS); + } + + @Test(expected = MailboxException.class) + public void getAnnotationsByKeysShouldThrowExceptionWhenDoesNotLookupMailbox() throws Exception { + doThrow(MailboxException.class).when(mailboxMapper).findMailboxByPath(eq(mailboxPath)); + storeMailboxManager.getAnnotationsByKeys(mailboxPath, session, KEYS); + } + + @Test + public void getAnnotationsByKeysShouldRetrieveStoreAnnotationsByKey() throws Exception { + when(mailboxMapper.findMailboxByPath(eq(mailboxPath))).thenReturn(mailbox); + when(annotationMapper.getAnnotationsByKeys(KEYS)).thenReturn(ANNOTATIONS); + + assertThat(storeMailboxManager.getAnnotationsByKeys(mailboxPath, session, KEYS)).isEqualTo(ANNOTATIONS); + } + +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
