diff --git a/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/PollerConfigEnvironmentPostProcessor.java b/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/PollerConfigEnvironmentPostProcessor.java index 8ced27582..6e1fc8c7d 100644 --- a/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/PollerConfigEnvironmentPostProcessor.java +++ b/core/spring-cloud-stream/src/main/java/org/springframework/cloud/stream/config/PollerConfigEnvironmentPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2021-2021 the original author or authors. + * Copyright 2021-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,15 +56,26 @@ public class PollerConfigEnvironmentPostProcessor implements EnvironmentPostProc } if (!streamPollerProperties.isEmpty()) { - logger.info("'spring.cloud.stream.poller' properties are deprecated in favor of 'spring.integration.poller' properties."); + logger.info("'spring.cloud.stream.poller' " + + "properties are deprecated in favor of 'spring.integration.poller' properties."); } //TODO Must remain after removal of deprecated code above in the future - streamPollerProperties.putIfAbsent(INTEGRATION_PROPERTY_PREFIX + "fixed-delay", "1s"); - streamPollerProperties.putIfAbsent(INTEGRATION_PROPERTY_PREFIX + "max-messages-per-poll", "1"); + if (!streamPollerProperties.containsKey(INTEGRATION_PROPERTY_PREFIX + "cron") && + !environment.containsProperty(INTEGRATION_PROPERTY_PREFIX + "cron") && + !environment.containsProperty(INTEGRATION_PROPERTY_PREFIX + "fixed-rate") && + !environment.containsProperty(INTEGRATION_PROPERTY_PREFIX + "fixed-delay")) { - environment.getPropertySources() - .addLast(new MapPropertySource("spring.integration.poller", streamPollerProperties)); + streamPollerProperties.putIfAbsent(INTEGRATION_PROPERTY_PREFIX + "fixed-delay", "1s"); + } + if (!environment.containsProperty(INTEGRATION_PROPERTY_PREFIX + "max-messages-per-poll")) { + streamPollerProperties.putIfAbsent(INTEGRATION_PROPERTY_PREFIX + "max-messages-per-poll", "1"); + } + + if (!streamPollerProperties.isEmpty()) { + environment.getPropertySources() + .addLast(new MapPropertySource("spring.integration.poller", streamPollerProperties)); + } } } diff --git a/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/PollableSourceTests.java b/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/PollableSourceTests.java index 7a702b45d..23426179d 100644 --- a/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/PollableSourceTests.java +++ b/core/spring-cloud-stream/src/test/java/org/springframework/cloud/stream/function/PollableSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2020 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,42 +18,113 @@ package org.springframework.cloud.stream.function; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException; import org.springframework.cloud.stream.binder.DefaultPollableMessageSource; import org.springframework.cloud.stream.binder.PollableMessageSource; import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; +import org.springframework.integration.scheduling.PollerMetadata; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.scheduling.support.CronTrigger; +import org.springframework.scheduling.support.PeriodicTrigger; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * * @author Oleg Zhurakousky + * @author Artem Bilan * */ public class PollableSourceTests { @Test - void test() { + void testPollableSource() { try (ConfigurableApplicationContext context = new SpringApplicationBuilder(TestChannelBinderConfiguration .getCompleteConfiguration(PollableAppSampleConfiguration.class)) .web(WebApplicationType.NONE).run( "--spring.jmx.enabled=false", - "--spring.cloud.stream.pollable-source=blah")) { + "--spring.cloud.stream.pollable-source=blah", + "--spring.integration.poller.cron=*/2 * * * * *", + "--spring.cloud.stream.poller.max-messages-per-poll=4")) { DefaultPollableMessageSource pollableSource = (DefaultPollableMessageSource) context.getBean(PollableMessageSource.class); - pollableSource.poll(message -> { - assertThat(message.getPayload()).isNotNull(); - }); + pollableSource.poll(message -> assertThat(message.getPayload()).isNotNull()); + + PollerMetadata pollerMetadata = context.getBean(PollerMetadata.class); + assertThat(pollerMetadata.getTrigger()).isInstanceOf(CronTrigger.class); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "expression.expression")) + .isEqualTo("*/2 * * * * *"); + assertThat(pollerMetadata.getMaxMessagesPerPoll()).isEqualTo(4); } } + @Test + void testPollerDefaultFixedDelay() { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(TestChannelBinderConfiguration + .getCompleteConfiguration(PollableAppSampleConfiguration.class)) + .web(WebApplicationType.NONE).run()) { + + PollerMetadata pollerMetadata = context.getBean(PollerMetadata.class); + assertThat(pollerMetadata.getTrigger()).isInstanceOf(PeriodicTrigger.class); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "fixedRate")).isEqualTo(false); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "period")).isEqualTo(1000L); + assertThat(pollerMetadata.getMaxMessagesPerPoll()).isEqualTo(1); + } + } + + @Test + void testPollerProvidedFixedDelayAndMaxMessagesPerPoll() { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(TestChannelBinderConfiguration + .getCompleteConfiguration(PollableAppSampleConfiguration.class)) + .web(WebApplicationType.NONE).run( + "--spring.integration.poller.fixed-delay=7s", + "--spring.integration.poller.max-messages-per-poll=13")) { + + PollerMetadata pollerMetadata = context.getBean(PollerMetadata.class); + assertThat(pollerMetadata.getTrigger()).isInstanceOf(PeriodicTrigger.class); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "fixedRate")).isEqualTo(false); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "period")).isEqualTo(7000L); + assertThat(pollerMetadata.getMaxMessagesPerPoll()).isEqualTo(13); + } + } + + @Test + void testNoPollerFixedDelayIfFixedRatePresent() { + try (ConfigurableApplicationContext context = new SpringApplicationBuilder(TestChannelBinderConfiguration + .getCompleteConfiguration(PollableAppSampleConfiguration.class)) + .web(WebApplicationType.NONE).run("--spring.integration.poller.fixed-rate=200")) { + + PollerMetadata pollerMetadata = context.getBean(PollerMetadata.class); + assertThat(pollerMetadata.getTrigger()).isInstanceOf(PeriodicTrigger.class); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "fixedRate")).isEqualTo(true); + assertThat(TestUtils.getPropertyValue(pollerMetadata.getTrigger(), "period")).isEqualTo(200L); + assertThat(pollerMetadata.getMaxMessagesPerPoll()).isEqualTo(1); + } + } + + @Test + void testPollerMutualProperties() { + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> new SpringApplicationBuilder(TestChannelBinderConfiguration + .getCompleteConfiguration(PollableAppSampleConfiguration.class)) + .web(WebApplicationType.NONE).run( + "--spring.integration.poller.fixed-rate=200", + "--spring.cloud.stream.poller.fixed-delay=300")) + .withRootCauseExactlyInstanceOf(MutuallyExclusiveConfigurationPropertiesException.class) + .withMessageContaining("are mutually exclusive"); + } + @EnableAutoConfiguration @Configuration public static class PollableAppSampleConfiguration { } + }