Introduce a ReceiveMessageAdvice (#3265)

* Introduce a `ReceiveMessageAdvice`

* Deprecate an `AbstractMessageSourceAdvice` in favor of
`default` method in the `MessageSourceMutator`
* Move a `applyReceiveOnlyAdviceChain()` logic into the `AbstractPollingEndpoint`:
now both `PollingConsumer` and `SourcePollingChannelAdapter` can use
`ReceiveMessageAdvice`
* Introduce a `SimpleActiveIdleReceiveMessageAdvice` based already
on the `ReceiveMessageAdvice` and deprecate a `SimpleActiveIdleMessageSourceAdvice`
which is fully replaceable with newly introduced `SimpleActiveIdleReceiveMessageAdvice`
* Add `@SuppressWarnings("deprecation")` for those out-of-the-box `ReceiveMessageAdvice`
implementation which still use an `AbstractMessageSourceAdvice` for
backward compatibility
* Document a new feature and give the `MessageSourceMutator` a new meaning

* * Fix language in the `polling-consumer.adoc`
This commit is contained in:
Artem Bilan
2020-04-28 13:03:30 -04:00
committed by GitHub
parent cfd03f89a0
commit 2d9a5f60f4
16 changed files with 394 additions and 128 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2020 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.
@@ -16,34 +16,20 @@
package org.springframework.integration.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.integration.core.MessageSource;
import org.springframework.messaging.Message;
/**
* Advice for a {@link MessageSource#receive()} method to decide whether a poll
* should be ignored and/or take action after the receive.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 4.2
*
* @deprecated since 5.3 in favor of {@link MessageSourceMutator}.
*/
public abstract class AbstractMessageSourceAdvice implements MethodInterceptor, MessageSourceMutator {
@Override
public final Object invoke(MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
if (!(target instanceof MessageSource)) {
return invocation.proceed();
}
Message<?> result = null;
if (beforeReceive((MessageSource<?>) target)) {
result = (Message<?>) invocation.proceed();
}
return afterReceive(result, (MessageSource<?>) target);
}
@Deprecated
public abstract class AbstractMessageSourceAdvice implements MessageSourceMutator {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2020 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,6 +18,7 @@ package org.springframework.integration.aop;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.util.CompoundTrigger;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.scheduling.Trigger;
import org.springframework.util.Assert;
@@ -35,7 +36,10 @@ import org.springframework.util.Assert;
* @since 4.3
*
*/
public class CompoundTriggerAdvice extends AbstractMessageSourceAdvice {
@SuppressWarnings("deprecation")
public class CompoundTriggerAdvice
extends AbstractMessageSourceAdvice
implements ReceiveMessageAdvice {
private final CompoundTrigger compoundTrigger;
@@ -47,8 +51,21 @@ public class CompoundTriggerAdvice extends AbstractMessageSourceAdvice {
this.override = overrideTrigger;
}
/**
* @param result the received message.
* @param source the message source.
* @return the message or null
* @deprecated since 5.3 in favor of {@link #afterReceive(Message, Object)}
*/
@Override
@Deprecated
public Message<?> afterReceive(Message<?> result, MessageSource<?> source) {
return afterReceive(result, (Object) source);
}
@Override
@Nullable
public Message<?> afterReceive(@Nullable Message<?> result, Object source) {
if (result == null) {
this.compoundTrigger.setOverride(this.override);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 the original author or authors.
* Copyright 2018-2020 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.
@@ -17,19 +17,31 @@
package org.springframework.integration.aop;
import org.springframework.integration.core.MessageSource;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
/**
* An object that can mutate a {@link MessageSource} before and/or after
* A {@link ReceiveMessageAdvice} extension that can mutate a {@link MessageSource} before and/or after
* {@link MessageSource#receive()} is called.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 5.0.7.
*
* @since 5.0.7
*/
@FunctionalInterface
public interface MessageSourceMutator {
public interface MessageSourceMutator extends ReceiveMessageAdvice {
@Override
default boolean beforeReceive(Object source) {
if (source instanceof MessageSource<?>) {
return beforeReceive((MessageSource<?>) source);
}
else {
throw new IllegalArgumentException(
"The 'MessageSourceMutator' supports only a 'MessageSource' in the before/after hooks: " + source);
}
}
/**
* Subclasses can decide whether to proceed with this poll.
@@ -40,6 +52,18 @@ public interface MessageSourceMutator {
return true;
}
@Override
@Nullable
default Message<?> afterReceive(@Nullable Message<?> result, Object source) {
if (source instanceof MessageSource<?>) {
return afterReceive(result, (MessageSource<?>) source);
}
else {
throw new IllegalArgumentException(
"The 'MessageSourceMutator' supports only a 'MessageSource' in the before/after hooks: " + source);
}
}
/**
* Subclasses can take actions based on the result of the poll; e.g.
* adjust the {@code trigger}. The message can also be replaced with a new one.
@@ -47,6 +71,7 @@ public interface MessageSourceMutator {
* @param source the message source.
* @return a message to continue to process the result, null to discard whatever the poll returned.
*/
Message<?> afterReceive(Message<?> result, MessageSource<?> source);
@Nullable
Message<?> afterReceive(@Nullable Message<?> result, MessageSource<?> source);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2020 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.
* You may obtain a copy of the License at
*
* https://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.springframework.integration.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.integration.core.MessageSource;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
/**
* An AOP advice to perform hooks before and/or after a {@code receive()} contract is called.
*
* @author Artem Bilan
*
* @since 5.3
*/
@FunctionalInterface
public interface ReceiveMessageAdvice extends MethodInterceptor {
/**
* Subclasses can decide whether to {@link MethodInvocation#proceed()} or not.
* @param source the source of the message to receive.
* @return true to proceed (default).
*/
default boolean beforeReceive(Object source) {
return true;
}
@Override
@Nullable
default Object invoke(MethodInvocation invocation) throws Throwable {
Object target = invocation.getThis();
if (!(target instanceof MessageSource) && !(target instanceof PollableChannel)) {
return invocation.proceed();
}
Message<?> result = null;
if (beforeReceive(target)) {
result = (Message<?>) invocation.proceed();
}
return afterReceive(result, target);
}
/**
* Subclasses can take actions based on the result of the {@link MethodInvocation#proceed()}; e.g.
* adjust the {@code trigger}. The message can also be replaced with a new one.
* @param result the received message.
* @param source the source of the message to receive.
* @return a message to continue to process the result, null to discard whatever
* the {@link MethodInvocation#proceed()} returned.
*/
@Nullable
Message<?> afterReceive(@Nullable Message<?> result, Object source);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2019 the original author or authors.
* Copyright 2015-2020 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.
@@ -31,7 +31,11 @@ import org.springframework.messaging.Message;
* @since 4.2
*
* @see DynamicPeriodicTrigger
*
* @deprecated since 5.3 in favor of {@link SimpleActiveIdleReceiveMessageAdvice} with the same
* (but more common) functionality.
*/
@Deprecated
public class SimpleActiveIdleMessageSourceAdvice extends AbstractMessageSourceAdvice {
private final DynamicPeriodicTrigger trigger;

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2020 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.
* You may obtain a copy of the License at
*
* https://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.springframework.integration.aop;
import java.time.Duration;
import org.springframework.integration.util.DynamicPeriodicTrigger;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
/**
A simple advice that polls at one rate when messages exist and another when
* there are no messages.
*
* @author Gary Russell
* @author Artem Bilan
*
* @since 5.3
*
* @see DynamicPeriodicTrigger
*/
public class SimpleActiveIdleReceiveMessageAdvice implements ReceiveMessageAdvice {
private final DynamicPeriodicTrigger trigger;
private volatile Duration idlePollPeriod;
private volatile Duration activePollPeriod;
public SimpleActiveIdleReceiveMessageAdvice(DynamicPeriodicTrigger trigger) {
Assert.notNull(trigger, "'trigger' must not be null");
this.trigger = trigger;
this.idlePollPeriod = trigger.getDuration();
this.activePollPeriod = trigger.getDuration();
}
/**
* Set the poll period when messages are not returned. Defaults to the
* trigger's period.
* @param idlePollPeriod the period in milliseconds.
*/
public void setIdlePollPeriod(long idlePollPeriod) {
this.idlePollPeriod = Duration.ofMillis(idlePollPeriod);
}
/**
* Set the poll period when messages are returned. Defaults to the
* trigger's period.
* @param activePollPeriod the period in milliseconds.
*/
public void setActivePollPeriod(long activePollPeriod) {
this.activePollPeriod = Duration.ofMillis(activePollPeriod);
}
@Override
public Message<?> afterReceive(Message<?> result, Object source) {
if (result == null) {
this.trigger.setDuration(this.idlePollPeriod);
}
else {
this.trigger.setDuration(this.activePollPeriod);
}
return result;
}
}

View File

@@ -19,6 +19,7 @@ package org.springframework.integration.endpoint;
import java.time.Duration;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@@ -28,10 +29,14 @@ import java.util.stream.Collectors;
import org.aopalliance.aop.Advice;
import org.reactivestreams.Subscription;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.integration.aop.ReceiveMessageAdvice;
import org.springframework.integration.channel.ChannelUtils;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.support.MessagingExceptionWrapper;
@@ -72,6 +77,8 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
*/
public static final long DEFAULT_POLLING_PERIOD = 10;
private final Collection<Advice> appliedAdvices = new HashSet<>();
private final Object initializationMonitor = new Object();
private Executor taskExecutor = new SyncTaskExecutor();
@@ -173,7 +180,7 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
* @return true to only advise the receive operation.
*/
protected boolean isReceiveOnlyAdvice(Advice advice) {
return false;
return advice instanceof ReceiveMessageAdvice;
}
/**
@@ -181,6 +188,37 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
* @param chain the advice chain {@code Collection}.
*/
protected void applyReceiveOnlyAdviceChain(Collection<Advice> chain) {
if (!CollectionUtils.isEmpty(chain)) {
Object source = getReceiveMessageSource();
if (source != null) {
if (AopUtils.isAopProxy(source)) {
Advised advised = (Advised) source;
this.appliedAdvices.forEach(advised::removeAdvice);
chain.forEach(advice -> advised.addAdvisor(adviceToReceiveAdvisor(advice)));
}
else {
ProxyFactory proxyFactory = new ProxyFactory(source);
chain.forEach(advice -> proxyFactory.addAdvisor(adviceToReceiveAdvisor(advice)));
source = proxyFactory.getProxy(getBeanClassLoader());
}
this.appliedAdvices.clear();
this.appliedAdvices.addAll(chain);
if (!(isSyncExecutor()) && logger.isWarnEnabled()) {
logger.warn(getComponentName() + ": A task executor is supplied and " + chain.size()
+ "ReceiveMessageAdvice(s) is/are provided. If an advice mutates the source, such "
+ "mutations are not thread safe and could cause unexpected results, especially with "
+ "high frequency pollers. Consider using a downstream ExecutorChannel instead of "
+ "adding an executor to the poller");
}
setReceiveMessageSource(source);
}
}
}
private NameMatchMethodPointcutAdvisor adviceToReceiveAdvisor(Advice advice) {
NameMatchMethodPointcutAdvisor sourceAdvisor = new NameMatchMethodPointcutAdvisor(advice);
sourceAdvisor.addMethodName("receive");
return sourceAdvisor;
}
protected boolean isReactive() {
@@ -191,6 +229,14 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
return this.pollingFlux;
}
protected Object getReceiveMessageSource() {
return null;
}
protected void setReceiveMessageSource(Object source) {
}
@Override
protected void onInit() {
synchronized (this.initializationMonitor) {

View File

@@ -57,13 +57,13 @@ public class PollingConsumer extends AbstractPollingEndpoint implements Integrat
*/
public static final long DEFAULT_RECEIVE_TIMEOUT = 1000;
private final PollableChannel inputChannel;
private final MessageHandler handler;
private final List<ChannelInterceptor> channelInterceptors;
private volatile long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
private PollableChannel inputChannel;
private long receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
public PollingConsumer(PollableChannel inputChannel, MessageHandler handler) {
Assert.notNull(inputChannel, "inputChannel must not be null");
@@ -110,6 +110,16 @@ public class PollingConsumer extends AbstractPollingEndpoint implements Integrat
return this.handler;
}
@Override
protected Object getReceiveMessageSource() {
return this.inputChannel;
}
@Override
protected void setReceiveMessageSource(Object source) {
this.inputChannel = (PollableChannel) source;
}
@Override
protected boolean isReactive() {
return getOutputChannel() instanceof ReactiveStreamsSubscribableChannel &&

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2020 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.
@@ -16,21 +16,12 @@
package org.springframework.integration.endpoint;
import java.util.Collection;
import java.util.HashSet;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.Lifecycle;
import org.springframework.integration.StaticMessageHeaderAccessor;
import org.springframework.integration.acks.AckUtils;
import org.springframework.integration.acks.AcknowledgmentCallback;
import org.springframework.integration.aop.MessageSourceMutator;
import org.springframework.integration.channel.ReactiveStreamsSubscribableChannel;
import org.springframework.integration.context.ExpressionCapable;
import org.springframework.integration.core.MessageSource;
@@ -43,7 +34,6 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* A Channel Adapter implementation for connecting a
@@ -59,9 +49,7 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private final Collection<Advice> appliedAdvices = new HashSet<>();
private volatile MessageSource<?> originalSource;
private MessageSource<?> originalSource;
private volatile MessageSource<?> source;
@@ -136,45 +124,19 @@ public class SourcePollingChannelAdapter extends AbstractPollingEndpoint
((NamedComponent) this.source).getComponentType() : "inbound-channel-adapter";
}
@Override
protected boolean isReceiveOnlyAdvice(Advice advice) {
return advice instanceof MessageSourceMutator;
}
@Override
protected void applyReceiveOnlyAdviceChain(Collection<Advice> chain) {
if (!CollectionUtils.isEmpty(chain)) {
if (AopUtils.isAopProxy(this.source)) {
Advised advised = (Advised) this.source;
this.appliedAdvices.forEach(advised::removeAdvice);
chain.forEach(advice -> advised.addAdvisor(adviceToReceiveAdvisor(advice)));
}
else {
ProxyFactory proxyFactory = new ProxyFactory(this.source);
chain.forEach(advice -> proxyFactory.addAdvisor(adviceToReceiveAdvisor(advice)));
this.source = (MessageSource<?>) proxyFactory.getProxy(getBeanClassLoader());
}
this.appliedAdvices.clear();
this.appliedAdvices.addAll(chain);
if (!(isSyncExecutor()) && logger.isWarnEnabled()) {
logger.warn(getComponentName() + ": A task executor is supplied and " + chain.size()
+ "MessageSourceMutator(s) is/are provided. If an advice mutates the source, such "
+ "mutations are not thread safe and could cause unexpected results, especially with "
+ "high frequency pollers. Consider using a downstream ExecutorChannel instead of "
+ "adding an executor to the poller");
}
}
}
@Override
protected boolean isReactive() {
return getOutputChannel() instanceof ReactiveStreamsSubscribableChannel;
}
private NameMatchMethodPointcutAdvisor adviceToReceiveAdvisor(Advice advice) {
NameMatchMethodPointcutAdvisor sourceAdvisor = new NameMatchMethodPointcutAdvisor(advice);
sourceAdvisor.addMethodName("receive");
return sourceAdvisor;
@Override
protected Object getReceiveMessageSource() {
return getMessageSource();
}
@Override
protected final void setReceiveMessageSource(Object source) {
this.source = (MessageSource<?>) source;
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2020 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.
@@ -35,8 +35,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.Joinpoint;
import org.aopalliance.intercept.MethodInterceptor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
@@ -48,9 +47,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.aop.AbstractMessageSourceAdvice;
import org.springframework.integration.aop.CompoundTriggerAdvice;
import org.springframework.integration.aop.SimpleActiveIdleMessageSourceAdvice;
import org.springframework.integration.aop.ReceiveMessageAdvice;
import org.springframework.integration.aop.SimpleActiveIdleReceiveMessageAdvice;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.config.EnableIntegration;
@@ -64,14 +63,14 @@ import org.springframework.integration.util.CompoundTrigger;
import org.springframework.integration.util.DynamicPeriodicTrigger;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Gary Russell
@@ -80,8 +79,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
* @since 4.1
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@SpringJUnitConfig
@DirtiesContext
public class PollerAdviceTests {
@@ -194,10 +192,10 @@ public class PollerAdviceTests {
});
final AtomicInteger count = new AtomicInteger();
class TestSourceAdvice extends AbstractMessageSourceAdvice {
class TestSourceAdvice implements ReceiveMessageAdvice {
@Override
public boolean beforeReceive(MessageSource<?> target) {
public boolean beforeReceive(Object target) {
count.incrementAndGet();
callOrder.add("b");
latch.get().countDown();
@@ -205,7 +203,7 @@ public class PollerAdviceTests {
}
@Override
public Message<?> afterReceive(Message<?> result, MessageSource<?> target) {
public Message<?> afterReceive(Message<?> result, Object target) {
callOrder.add("d");
latch.get().countDown();
return result;
@@ -267,7 +265,7 @@ public class PollerAdviceTests {
latch.countDown();
return m;
});
SimpleActiveIdleMessageSourceAdvice toggling = new SimpleActiveIdleMessageSourceAdvice(trigger);
SimpleActiveIdleReceiveMessageAdvice toggling = new SimpleActiveIdleReceiveMessageAdvice(trigger);
toggling.setActivePollPeriod(11);
toggling.setIdlePollPeriod(12);
adapter.setAdviceChain(Collections.singletonList(toggling));
@@ -282,6 +280,56 @@ public class PollerAdviceTests {
}
}
@Test
public void testActiveIdleAdviceOnQueueChannel() throws Exception {
final CountDownLatch latch = new CountDownLatch(5);
final LinkedList<Long> triggerPeriods = new LinkedList<>();
final DynamicPeriodicTrigger trigger = new DynamicPeriodicTrigger(10);
PollingConsumer pollingConsumer =
new PollingConsumer(new PollableChannel() {
@Override
public Message<?> receive() {
synchronized (triggerPeriods) {
triggerPeriods.add(trigger.getDuration().toMillis());
}
Message<Object> m = null;
if (latch.getCount() % 2 == 0) {
m = new GenericMessage<>("foo");
}
latch.countDown();
return m;
}
@Override
public Message<?> receive(long timeout) {
return receive();
}
@Override
public boolean send(Message<?> message, long timeout) {
return false;
}
}, m -> { });
SimpleActiveIdleReceiveMessageAdvice toggling = new SimpleActiveIdleReceiveMessageAdvice(trigger);
toggling.setActivePollPeriod(11);
toggling.setIdlePollPeriod(12);
pollingConsumer.setAdviceChain(Collections.singletonList(toggling));
pollingConsumer.setTrigger(trigger);
pollingConsumer.setBeanFactory(this.beanFactory);
pollingConsumer.setTaskScheduler(this.threadPoolTaskScheduler);
pollingConsumer.afterPropertiesSet();
pollingConsumer.start();
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
pollingConsumer.stop();
synchronized (triggerPeriods) {
assertThat(triggerPeriods.subList(0, 5)).containsExactly(10L, 12L, 11L, 12L, 11L);
}
}
@Test
public void testCompoundTriggerAdvice() throws Exception {
SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter();
@@ -361,18 +409,18 @@ public class PollerAdviceTests {
}
public static class OtherAdvice extends AbstractMessageSourceAdvice {
public static class OtherAdvice implements ReceiveMessageAdvice {
private int calls;
@Override
public boolean beforeReceive(MessageSource<?> source) {
public boolean beforeReceive(Object source) {
this.calls++;
return true;
}
@Override
public Message<?> afterReceive(Message<?> result, MessageSource<?> source) {
public Message<?> afterReceive(Message<?> result, Object source) {
return result;
}