INT-4386: Support Instant in schedule expressions

JIRA: https://jira.spring.io/browse/INT-4386

* Expose `java.time` in the `IntegrationEvaluationContextFactoryBean`
ot simply a `T()` SpEL operator
* Add `Instant` result evaluation to the `delayExpression`
This commit is contained in:
Artem Bilan
2019-01-30 16:15:04 -05:00
committed by Gary Russell
parent 020ea76d59
commit f901c4e167
3 changed files with 44 additions and 10 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013-2018 the original author or authors.
* Copyright 2013-2019 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.
@@ -26,6 +26,7 @@ import org.springframework.expression.BeanResolver;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeLocator;
import org.springframework.integration.context.IntegrationContextUtils;
/**
@@ -65,7 +66,7 @@ import org.springframework.integration.context.IntegrationContextUtils;
public class IntegrationEvaluationContextFactoryBean extends AbstractEvaluationContextFactoryBean
implements FactoryBean<StandardEvaluationContext> {
private volatile TypeLocator typeLocator;
private TypeLocator typeLocator;
private BeanResolver beanResolver;
@@ -87,12 +88,21 @@ public class IntegrationEvaluationContextFactoryBean extends AbstractEvaluationC
}
@Override
public StandardEvaluationContext getObject() throws Exception {
public StandardEvaluationContext getObject() {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
if (this.typeLocator != null) {
evaluationContext.setTypeLocator(this.typeLocator);
}
TypeLocator typeLocator = evaluationContext.getTypeLocator();
if (typeLocator instanceof StandardTypeLocator) {
/*
* Register the 'java.time' package so its classes don't need a FQCN,
* for example ' T(Instant).now().plusSeconds(5)'.
*/
((StandardTypeLocator) typeLocator).registerImport("java.time");
}
evaluationContext.setBeanResolver(this.beanResolver);
evaluationContext.setTypeConverter(getTypeConverter());

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.handler;
import java.io.Serializable;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -158,8 +159,9 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
}
/**
* Specify the {@link Expression} that should be checked for a delay period (in
* milliseconds) or a Date to delay until. If this property is set, the result of the
* Specify the {@link Expression} that should be checked for a delay period
* (in milliseconds) or a {@link Date}, or {@link Instant} to delay until.
* If this property is set, the result of the
* expression evaluation (if not null) will take precedence over this handler's
* default delay.
* @param delayExpression The delay expression.
@@ -169,8 +171,9 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
}
/**
* Specify the {@code Expression} that should be checked for a delay period (in
* milliseconds) or a Date to delay until. If this property is set, the result of the
* Specify the {@code Expression} that should be checked for a delay period
* (in milliseconds) or a {@link Date}, or {@link Instant} to delay until.
* If this property is set, the result of the
* expression evaluation (if not null) will take precedence over this handler's
* default delay.
* @param delayExpression The delay expression.
@@ -364,6 +367,12 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
: System.currentTimeMillis();
delay = ((Date) delayValue).getTime() - current;
}
else if (delayValue instanceof Instant) {
long current = delayedMessageWrapper != null
? delayedMessageWrapper.getRequestDate()
: Instant.now().toEpochMilli();
delay = ((Instant) delayValue).minusMillis(current).toEpochMilli();
}
else if (delayValue != null) {
try {
delay = Long.valueOf(delayValue.toString());
@@ -391,8 +400,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
private void releaseMessageAfterDelay(final Message<?> message, long delay) {
Message<?> delayedMessage = message;
DelayedMessageWrapper messageWrapper = null;
DelayedMessageWrapper messageWrapper;
if (message.getPayload() instanceof DelayedMessageWrapper) {
messageWrapper = (DelayedMessageWrapper) message.getPayload();
}

View File

@@ -50,6 +50,7 @@ import org.springframework.integration.StaticMessageHeaderAccessor;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
@@ -93,13 +94,16 @@ public class DelayHandlerTests {
@Before
public void setup() {
this.context.registerBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME,
new IntegrationEvaluationContextFactoryBean());
this.context.refresh();
input.setBeanName("input");
output.setBeanName("output");
taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
delayHandler = new DelayHandler(DELAYER_MESSAGE_GROUP_ID, taskScheduler);
delayHandler.setOutputChannel(output);
delayHandler.setBeanFactory(mock(BeanFactory.class));
delayHandler.setBeanFactory(this.context);
input.subscribe(delayHandler);
output.subscribe(resultHandler);
}
@@ -232,6 +236,18 @@ public class DelayHandlerTests {
assertNotSame(Thread.currentThread(), resultHandler.lastThread);
}
@Test
public void delayHeaderIsInstantInTheFutureAndDefaultDelayWouldTimeout() {
this.delayHandler.setDefaultDelay(5000);
this.delayHandler.setDelayExpressionString("T(Instant).now().plusMillis(150)");
startDelayerHandler();
Message<?> message = new GenericMessage<>("test");
this.input.send(message);
waitForLatch(10000);
assertSame(message.getPayload(), this.resultHandler.lastMessage.getPayload());
assertNotSame(Thread.currentThread(), this.resultHandler.lastThread);
}
@Test
public void delayHeaderIsDateInThePastAndDefaultDelayWouldTimeout() {
delayHandler.setDefaultDelay(5000);