diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java index b776b6a6b3..611833713b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationEvaluationContextFactoryBean.java @@ -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 { - 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()); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java index b5a4c08ea0..8db87f77d3 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/handler/DelayHandler.java @@ -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(); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/DelayHandlerTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/DelayHandlerTests.java index c2cf9c3668..9ffd7b4170 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/handler/DelayHandlerTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/DelayHandlerTests.java @@ -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);