Get correct next execution date from the trigger

With the `time-source` in Spring Cloud Stream we noticed
that first two items have the same (or close) value
when the `time-supplier` is used with the reactive output channel.
Turns out the `contactMap()` with `prefetch == 1` asks two
upstream elements immediately not waiting for a completion
for the fist one.
This way we were not able to update `triggerContext` for the proper
`lastCompletionTime`

* Change `AbstractPollingEndpoint` to `prefetch = 0` for the
"fair" upstream request
* Verify the behavior with the new
`ReactiveInboundChannelAdapterTests.testTimeSupplierConsistency()`
* Mark `ReactiveInboundChannelAdapterTests` as `@LongRunningTest`
since it is now pretty long waiting for all the 3 dates to verify
* Fix `InboundChannelAdapterAnnotationPostProcessor` to properly
register `MethodInvokingMessageSource` when we have more than one
`Supplier` with the `@InboundChannelAdapter` in the same configuration
class
This commit is contained in:
Artem Bilan
2020-10-21 13:48:31 -04:00
committed by Gary Russell
parent 3cf66cc539
commit fb35a736d6
3 changed files with 49 additions and 4 deletions

View File

@@ -85,10 +85,11 @@ public class InboundChannelAdapterAnnotationPostProcessor extends
return adapter;
}
private MessageSource<?> createMessageSource(Object beanArg, String beanName, Method methodArg) {
private MessageSource<?> createMessageSource(Object beanArg, String beanNameArg, Method methodArg) {
MessageSource<?> messageSource = null;
Object bean = beanArg;
Method method = methodArg;
String beanName = beanNameArg;
if (AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
Object target = resolveTargetBeanFromMethodWithBeanAnnotation(method);
Class<?> targetClass = target.getClass();
@@ -106,10 +107,12 @@ public class InboundChannelAdapterAnnotationPostProcessor extends
else if (target instanceof Supplier<?>) {
method = ClassUtils.SUPPLIER_GET_METHOD;
bean = target;
beanName += '.' + methodArg.getName();
}
else if (ClassUtils.KOTLIN_FUNCTION_0_INVOKE_METHOD != null) {
method = ClassUtils.KOTLIN_FUNCTION_0_INVOKE_METHOD;
bean = target;
beanName += '.' + methodArg.getName();
}
}
if (messageSource == null) {

View File

@@ -366,7 +366,7 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
.update(triggerContext.lastScheduledExecutionTime(),
triggerContext.lastActualExecutionTime(),
new Date())
)), 1)
)), 0)
.repeat(this::isRunning)
.doOnSubscribe(subs -> this.subscription = subs);
}

View File

@@ -16,7 +16,12 @@
package org.springframework.integration.endpoint;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
@@ -31,6 +36,7 @@ import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.channel.FluxMessageChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.test.condition.LongRunningTest;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.annotation.DirtiesContext;
@@ -46,15 +52,16 @@ import reactor.test.StepVerifier;
*/
@SpringJUnitConfig
@DirtiesContext
@LongRunningTest
public class ReactiveInboundChannelAdapterTests {
@Autowired
private FluxMessageChannel fluxMessageChannel;
private FluxMessageChannel fluxChannel;
@Test
public void testReactiveInboundChannelAdapter() {
Flux<Integer> testFlux =
Flux.from(this.fluxMessageChannel)
Flux.from(this.fluxChannel)
.map(Message::getPayload)
.cast(Integer.class);
@@ -64,6 +71,30 @@ public class ReactiveInboundChannelAdapterTests {
.verify(Duration.ofSeconds(10));
}
@Autowired
private FluxMessageChannel fluxChannel2;
@Test
public void testTimeSupplierConsistency() {
Flux<Long> testFlux =
Flux.from(this.fluxChannel2)
.map(Message::getPayload)
.cast(Date.class)
.map(Date::getTime);
List<Long> dates = new ArrayList<>();
StepVerifier.create(testFlux)
.consumeNextWith(dates::add)
.consumeNextWith(dates::add)
.consumeNextWith(dates::add)
.thenCancel()
.verify(Duration.ofSeconds(10));
assertThat(dates.get(1) - dates.get(0)).isGreaterThanOrEqualTo(1000);
assertThat(dates.get(2) - dates.get(1)).isGreaterThanOrEqualTo(1000);
}
@Configuration
@EnableIntegration
public static class Config {
@@ -93,6 +124,17 @@ public class ReactiveInboundChannelAdapterTests {
return new FluxMessageChannel();
}
@Bean
@InboundChannelAdapter(value = "fluxChannel2", poller = @Poller(fixedDelay = "1000"))
public Supplier<Date> timeSupplier() {
return Date::new;
}
@Bean
public MessageChannel fluxChannel2() {
return new FluxMessageChannel();
}
}
}