diff --git a/spring-integration-core/src/main/java/org/springframework/integration/aop/CompoundTriggerAdvice.java b/spring-integration-core/src/main/java/org/springframework/integration/aop/CompoundTriggerAdvice.java new file mode 100644 index 0000000000..4d14608fdf --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/aop/CompoundTriggerAdvice.java @@ -0,0 +1,65 @@ +/* + * Copyright 2015 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 + * + * 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.springframework.integration.aop; + +import org.springframework.integration.core.MessageSource; +import org.springframework.integration.util.CompoundTrigger; +import org.springframework.messaging.Message; +import org.springframework.scheduling.Trigger; +import org.springframework.util.Assert; + +/** + * An {@link AbstractMessageSourceAdvice} that uses a {@link CompoundTrigger} to adjust + * the poller - when a message is present, the compound trigger's primary trigger is + * used to determine the next poll. When no message is present, the override trigger is + * used. + *
+ * The poller advised by this class must be configured to use the same + * {@link CompoundTrigger} instance and must not use a task executor. + * + * @author Gary Russell + * @since 4.3 + * + */ +public class CompoundTriggerAdvice extends AbstractMessageSourceAdvice { + + private final CompoundTrigger compoundTrigger; + + private final Trigger override; + + public CompoundTriggerAdvice(CompoundTrigger compoundTrigger, Trigger overrideTrigger) { + Assert.notNull(compoundTrigger, "'compoundTrigger' cannot be null"); + this.compoundTrigger = compoundTrigger; + this.override = overrideTrigger; + } + + @Override + public boolean beforeReceive(MessageSource> source) { + return true; + } + + @Override + public Message> afterReceive(Message> result, MessageSource> source) { + if (result == null) { + this.compoundTrigger.setOverride(this.override); + } + else { + this.compoundTrigger.setOverride(null); + } + return result; + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/CompoundTrigger.java b/spring-integration-core/src/main/java/org/springframework/integration/util/CompoundTrigger.java new file mode 100644 index 0000000000..f35bce3859 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/CompoundTrigger.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 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 + * + * 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.springframework.integration.util; + +import java.util.Date; + +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.TriggerContext; + +import reactor.core.support.Assert; + +/** + * A {@link Trigger} that delegates the {@link #nextExecutionTime(TriggerContext)} + * to one of two Triggers. If the {@link #setOverride(Trigger) override} trigger is + * {@code null}, the primary trigger is invoked; otherwise the override trigger is + * invoked. + * + * @author Gary Russell + * @since 4.3 + * + */ +public class CompoundTrigger implements Trigger { + + private volatile Trigger primary; + + private volatile Trigger override; + + /** + * Construct a compound trigger with the supplied primary trigger. + * @param primary the primary trigger. + */ + public CompoundTrigger(Trigger primary) { + setPrimary(primary); + } + + /** + * Set the primary trigger. + * @param primary the trigger. + */ + public final void setPrimary(Trigger primary) { + Assert.notNull(primary, "'primary' cannot be null"); + this.primary = primary; + } + + /** + * Set the override trigger; set to null to revert to using the + * primary trigger. + * @param override the override trigger, or null. + */ + public void setOverride(Trigger override) { + this.override = override; + } + + @Override + public Date nextExecutionTime(TriggerContext triggerContext) { + if (this.override != null) { + return this.override.nextExecutionTime(triggerContext); + } + else { + return this.primary.nextExecutionTime(triggerContext); + } + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/PollerAdviceTests.java b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/PollerAdviceTests.java index 66619c34c2..0bd00e4f8a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/endpoint/PollerAdviceTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/endpoint/PollerAdviceTests.java @@ -18,9 +18,14 @@ package org.springframework.integration.endpoint; import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import java.util.ArrayList; import java.util.Collections; @@ -33,23 +38,27 @@ import java.util.concurrent.TimeUnit; import org.aopalliance.aop.Advice; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; -import org.hamcrest.Matchers; import org.junit.Test; import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.aop.AbstractMessageSourceAdvice; +import org.springframework.integration.aop.CompoundTriggerAdvice; import org.springframework.integration.aop.SimpleActiveIdleMessageSourceAdvice; import org.springframework.integration.channel.NullChannel; -import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.core.MessageSource; import org.springframework.integration.scheduling.PollSkipAdvice; import org.springframework.integration.scheduling.PollSkipStrategy; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.integration.util.CompoundTrigger; import org.springframework.integration.util.DynamicPeriodicTrigger; import org.springframework.messaging.Message; 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; /** * @author Gary Russell @@ -221,11 +230,11 @@ public class PollerAdviceTests { return m; } }); - QueueChannel channel = new QueueChannel(); SimpleActiveIdleMessageSourceAdvice toggling = new SimpleActiveIdleMessageSourceAdvice(trigger); toggling.setActivePollPeriod(11); toggling.setIdlePollPeriod(12); adapter.setAdviceChain(Collections.singletonList(toggling)); + adapter.setTrigger(trigger); configure(adapter); adapter.afterPropertiesSet(); adapter.start(); @@ -234,7 +243,42 @@ public class PollerAdviceTests { while (triggerPeriods.size() > 5) { triggerPeriods.removeLast(); } - assertThat(triggerPeriods, Matchers.contains(10L, 12L, 11L, 12L, 11L)); + assertThat(triggerPeriods, contains(10L, 12L, 11L, 12L, 11L)); + } + + @Test + public void testCompoundTriggerAdvice() throws Exception { + SourcePollingChannelAdapter adapter = new SourcePollingChannelAdapter(); + final CountDownLatch latch = new CountDownLatch(5); + final LinkedList