Skip to content

Commit f901c4e

Browse files
artembilangaryrussell
authored andcommitted
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`
1 parent 020ea76 commit f901c4e

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2013-2018 the original author or authors.
2+
* Copyright 2013-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import org.springframework.expression.PropertyAccessor;
2727
import org.springframework.expression.TypeLocator;
2828
import org.springframework.expression.spel.support.StandardEvaluationContext;
29+
import org.springframework.expression.spel.support.StandardTypeLocator;
2930
import org.springframework.integration.context.IntegrationContextUtils;
3031

3132
/**
@@ -65,7 +66,7 @@
6566
public class IntegrationEvaluationContextFactoryBean extends AbstractEvaluationContextFactoryBean
6667
implements FactoryBean<StandardEvaluationContext> {
6768

68-
private volatile TypeLocator typeLocator;
69+
private TypeLocator typeLocator;
6970

7071
private BeanResolver beanResolver;
7172

@@ -87,12 +88,21 @@ public void afterPropertiesSet() throws Exception {
8788
}
8889

8990
@Override
90-
public StandardEvaluationContext getObject() throws Exception {
91+
public StandardEvaluationContext getObject() {
9192
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
9293
if (this.typeLocator != null) {
9394
evaluationContext.setTypeLocator(this.typeLocator);
9495
}
9596

97+
TypeLocator typeLocator = evaluationContext.getTypeLocator();
98+
if (typeLocator instanceof StandardTypeLocator) {
99+
/*
100+
* Register the 'java.time' package so its classes don't need a FQCN,
101+
* for example ' T(Instant).now().plusSeconds(5)'.
102+
*/
103+
((StandardTypeLocator) typeLocator).registerImport("java.time");
104+
}
105+
96106
evaluationContext.setBeanResolver(this.beanResolver);
97107
evaluationContext.setTypeConverter(getTypeConverter());
98108

spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.integration.handler;
1818

1919
import java.io.Serializable;
20+
import java.time.Instant;
2021
import java.util.Collection;
2122
import java.util.Collections;
2223
import java.util.Date;
@@ -158,8 +159,9 @@ public void setDefaultDelay(long defaultDelay) {
158159
}
159160

160161
/**
161-
* Specify the {@link Expression} that should be checked for a delay period (in
162-
* milliseconds) or a Date to delay until. If this property is set, the result of the
162+
* Specify the {@link Expression} that should be checked for a delay period
163+
* (in milliseconds) or a {@link Date}, or {@link Instant} to delay until.
164+
* If this property is set, the result of the
163165
* expression evaluation (if not null) will take precedence over this handler's
164166
* default delay.
165167
* @param delayExpression The delay expression.
@@ -169,8 +171,9 @@ public void setDelayExpression(Expression delayExpression) {
169171
}
170172

171173
/**
172-
* Specify the {@code Expression} that should be checked for a delay period (in
173-
* milliseconds) or a Date to delay until. If this property is set, the result of the
174+
* Specify the {@code Expression} that should be checked for a delay period
175+
* (in milliseconds) or a {@link Date}, or {@link Instant} to delay until.
176+
* If this property is set, the result of the
174177
* expression evaluation (if not null) will take precedence over this handler's
175178
* default delay.
176179
* @param delayExpression The delay expression.
@@ -364,6 +367,12 @@ private long determineDelayForMessage(Message<?> message) {
364367
: System.currentTimeMillis();
365368
delay = ((Date) delayValue).getTime() - current;
366369
}
370+
else if (delayValue instanceof Instant) {
371+
long current = delayedMessageWrapper != null
372+
? delayedMessageWrapper.getRequestDate()
373+
: Instant.now().toEpochMilli();
374+
delay = ((Instant) delayValue).minusMillis(current).toEpochMilli();
375+
}
367376
else if (delayValue != null) {
368377
try {
369378
delay = Long.valueOf(delayValue.toString());
@@ -391,8 +400,7 @@ else if (delayValue != null) {
391400

392401
private void releaseMessageAfterDelay(final Message<?> message, long delay) {
393402
Message<?> delayedMessage = message;
394-
395-
DelayedMessageWrapper messageWrapper = null;
403+
DelayedMessageWrapper messageWrapper;
396404
if (message.getPayload() instanceof DelayedMessageWrapper) {
397405
messageWrapper = (DelayedMessageWrapper) message.getPayload();
398406
}

spring-integration-core/src/test/java/org/springframework/integration/handler/DelayHandlerTests.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.springframework.integration.channel.DirectChannel;
5151
import org.springframework.integration.channel.MessagePublishingErrorHandler;
5252
import org.springframework.integration.channel.QueueChannel;
53+
import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean;
5354
import org.springframework.integration.context.IntegrationContextUtils;
5455
import org.springframework.integration.store.MessageGroup;
5556
import org.springframework.integration.store.MessageGroupStore;
@@ -93,13 +94,16 @@ public class DelayHandlerTests {
9394

9495
@Before
9596
public void setup() {
97+
this.context.registerBean(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME,
98+
new IntegrationEvaluationContextFactoryBean());
99+
this.context.refresh();
96100
input.setBeanName("input");
97101
output.setBeanName("output");
98102
taskScheduler = new ThreadPoolTaskScheduler();
99103
taskScheduler.afterPropertiesSet();
100104
delayHandler = new DelayHandler(DELAYER_MESSAGE_GROUP_ID, taskScheduler);
101105
delayHandler.setOutputChannel(output);
102-
delayHandler.setBeanFactory(mock(BeanFactory.class));
106+
delayHandler.setBeanFactory(this.context);
103107
input.subscribe(delayHandler);
104108
output.subscribe(resultHandler);
105109
}
@@ -232,6 +236,18 @@ public void delayHeaderIsDateInTheFutureAndDefaultDelayWouldTimeout() {
232236
assertNotSame(Thread.currentThread(), resultHandler.lastThread);
233237
}
234238

239+
@Test
240+
public void delayHeaderIsInstantInTheFutureAndDefaultDelayWouldTimeout() {
241+
this.delayHandler.setDefaultDelay(5000);
242+
this.delayHandler.setDelayExpressionString("T(Instant).now().plusMillis(150)");
243+
startDelayerHandler();
244+
Message<?> message = new GenericMessage<>("test");
245+
this.input.send(message);
246+
waitForLatch(10000);
247+
assertSame(message.getPayload(), this.resultHandler.lastMessage.getPayload());
248+
assertNotSame(Thread.currentThread(), this.resultHandler.lastThread);
249+
}
250+
235251
@Test
236252
public void delayHeaderIsDateInThePastAndDefaultDelayWouldTimeout() {
237253
delayHandler.setDefaultDelay(5000);

0 commit comments

Comments
 (0)