This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 591aa677b73f24f8e31d54b4af849f5ca97ec643 Author: Rene Cordier <rcord...@linagora.com> AuthorDate: Wed Mar 11 13:47:14 2020 +0700 JAMES-3078 DefaultMailboxesReactiveProvisioner and tests --- .../http/DefaultMailboxesReactiveProvisioner.java | 107 +++++++++++++++++++++ .../DefaultMailboxesReactiveProvisionerTest.java | 103 ++++++++++++++++++++ ...aultMailboxesReactiveProvisionerThreadTest.java | 74 ++++++++++++++ 3 files changed, 284 insertions(+) diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisioner.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisioner.java new file mode 100644 index 0000000..7b0568d --- /dev/null +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisioner.java @@ -0,0 +1,107 @@ +/**************************************************************** + * 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.jmap.http; + +import java.util.Optional; +import java.util.function.Function; + +import javax.inject.Inject; + +import org.apache.james.core.Username; +import org.apache.james.mailbox.DefaultMailboxes; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.SubscriptionManager; +import org.apache.james.mailbox.exception.MailboxException; +import org.apache.james.mailbox.exception.MailboxExistsException; +import org.apache.james.mailbox.model.MailboxId; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.metrics.api.MetricFactory; +import org.apache.james.metrics.api.TimeMetric; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +import reactor.core.publisher.Mono; + +public class DefaultMailboxesReactiveProvisioner { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMailboxesReactiveProvisioner.class); + private final MailboxManager mailboxManager; + private final SubscriptionManager subscriptionManager; + private final MetricFactory metricFactory; + + @Inject + @VisibleForTesting + DefaultMailboxesReactiveProvisioner(MailboxManager mailboxManager, + SubscriptionManager subscriptionManager, + MetricFactory metricFactory) { + this.mailboxManager = mailboxManager; + this.subscriptionManager = subscriptionManager; + this.metricFactory = metricFactory; + } + + public Mono<Void> createMailboxesIfNeeded(MailboxSession session) { + return Mono.fromRunnable(() -> { + TimeMetric timeMetric = metricFactory.timer("JMAP-mailboxes-provisioning"); + try { + Username username = session.getUser(); + createDefaultMailboxes(username); + } catch (MailboxException e) { + throw new RuntimeException(e); + } finally { + timeMetric.stopAndPublish(); + } + }); + } + + private void createDefaultMailboxes(Username username) throws MailboxException { + MailboxSession session = mailboxManager.createSystemSession(username); + DefaultMailboxes.DEFAULT_MAILBOXES.stream() + .map(toMailboxPath(session)) + .filter(mailboxPath -> mailboxDoesntExist(mailboxPath, session)) + .forEach(mailboxPath -> createMailbox(mailboxPath, session)); + } + + private boolean mailboxDoesntExist(MailboxPath mailboxPath, MailboxSession session) { + try { + return !mailboxManager.mailboxExists(mailboxPath, session); + } catch (MailboxException e) { + throw new RuntimeException(e); + } + } + + private Function<String, MailboxPath> toMailboxPath(MailboxSession session) { + return mailbox -> MailboxPath.forUser(session.getUser(), mailbox); + } + + private void createMailbox(MailboxPath mailboxPath, MailboxSession session) { + try { + Optional<MailboxId> mailboxId = mailboxManager.createMailbox(mailboxPath, session); + if (mailboxId.isPresent()) { + subscriptionManager.subscribe(session, mailboxPath.getName()); + } + LOGGER.info("Provisioning {}. {} created.", mailboxPath, mailboxId); + } catch (MailboxExistsException e) { + LOGGER.info("Mailbox {} have been created concurrently", mailboxPath); + } catch (MailboxException e) { + throw new RuntimeException(e); + } + } +} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerTest.java new file mode 100644 index 0000000..97b9b0f --- /dev/null +++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerTest.java @@ -0,0 +1,103 @@ +/**************************************************************** + * 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.jmap.http; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; + +import org.apache.james.core.Username; +import org.apache.james.mailbox.DefaultMailboxes; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSessionUtil; +import org.apache.james.mailbox.inmemory.InMemoryMailboxManager; +import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.store.StoreSubscriptionManager; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.util.concurrency.ConcurrentTestRunner; +import org.junit.Before; +import org.junit.Test; + +import com.github.fge.lambdas.Throwing; +import com.github.steveash.guavate.Guavate; + +public class DefaultMailboxesReactiveProvisionerTest { + + public static final Username USERNAME = Username.of("username"); + private DefaultMailboxesReactiveProvisioner testee; + private MailboxSession session; + private InMemoryMailboxManager mailboxManager; + private StoreSubscriptionManager subscriptionManager; + + @Before + public void before() { + session = MailboxSessionUtil.create(USERNAME); + + mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager(); + subscriptionManager = new StoreSubscriptionManager(mailboxManager.getMapperFactory()); + testee = new DefaultMailboxesReactiveProvisioner(mailboxManager, subscriptionManager, new RecordingMetricFactory()); + } + + @Test + public void createMailboxesIfNeededShouldCreateSystemMailboxes() throws Exception { + testee.createMailboxesIfNeeded(session).block(); + + assertThat(mailboxManager.list(session)) + .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES + .stream() + .map(mailboxName -> MailboxPath.forUser(USERNAME, mailboxName)) + .collect(Guavate.toImmutableList())); + } + + @Test + public void createMailboxesIfNeededShouldCreateSpamWhenOtherSystemMailboxesExist() throws Exception { + DefaultMailboxes.DEFAULT_MAILBOXES + .stream() + .filter(mailbox -> !DefaultMailboxes.SPAM.equals(mailbox)) + .forEach(Throwing.consumer(mailbox -> mailboxManager.createMailbox(MailboxPath.forUser(USERNAME, mailbox), session))); + + testee.createMailboxesIfNeeded(session).block(); + + assertThat(mailboxManager.list(session)).contains(MailboxPath.forUser(USERNAME, DefaultMailboxes.SPAM)); + } + + @Test + public void createMailboxesIfNeededShouldSubscribeMailboxes() throws Exception { + testee.createMailboxesIfNeeded(session).block(); + + assertThat(subscriptionManager.subscriptions(session)) + .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES); + } + + @Test + public void createMailboxesIfNeededShouldNotGenerateExceptionsInConcurrentEnvironment() throws Exception { + ConcurrentTestRunner.builder() + .operation((threadNumber, step) -> testee.createMailboxesIfNeeded(session).block()) + .threadCount(10) + .runSuccessfullyWithin(Duration.ofSeconds(10)); + + assertThat(mailboxManager.list(session)) + .containsOnlyElementsOf(DefaultMailboxes.DEFAULT_MAILBOXES + .stream() + .map(mailboxName -> MailboxPath.forUser(USERNAME, mailboxName)) + .collect(Guavate.toImmutableList())); + } + +} diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerThreadTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerThreadTest.java new file mode 100644 index 0000000..d0a5904 --- /dev/null +++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/DefaultMailboxesReactiveProvisionerThreadTest.java @@ -0,0 +1,74 @@ +/**************************************************************** + * 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.jmap.http; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.time.Duration; +import java.util.Optional; + +import org.apache.james.core.Username; +import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MailboxSessionUtil; +import org.apache.james.mailbox.SubscriptionManager; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.TestId; +import org.apache.james.metrics.tests.RecordingMetricFactory; +import org.apache.james.util.concurrency.ConcurrentTestRunner; +import org.junit.Before; +import org.junit.Test; + +public class DefaultMailboxesReactiveProvisionerThreadTest { + + private static final Username USERNAME = Username.of("username"); + + private DefaultMailboxesReactiveProvisioner testee; + private MailboxSession session; + private MailboxManager mailboxManager; + private SubscriptionManager subscriptionManager; + + @Before + public void before() { + session = MailboxSessionUtil.create(USERNAME); + mailboxManager = mock(MailboxManager.class); + subscriptionManager = mock(SubscriptionManager.class); + testee = new DefaultMailboxesReactiveProvisioner(mailboxManager, subscriptionManager, new RecordingMetricFactory()); + } + + @Test + public void testConcurrentAccessToFilterShouldNotThrow() throws Exception { + doNothing().when(subscriptionManager).subscribe(eq(session), anyString()); + + when(mailboxManager.createMailbox(any(MailboxPath.class), eq(session))).thenReturn(Optional.of(TestId.of(18L))); + when(mailboxManager.mailboxExists(any(MailboxPath.class), eq(session))).thenReturn(false); + when(mailboxManager.createSystemSession(USERNAME)).thenReturn(session); + + ConcurrentTestRunner + .builder() + .operation((threadNumber, step) -> testee.createMailboxesIfNeeded(session).block()) + .threadCount(2) + .runSuccessfullyWithin(Duration.ofMinutes(1)); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org