INT-2243: Delayer: Add 'expression' Support

Previously, the `<delayer>` provided a `delay-header-name` attribute.
In complex cases there was need to precede it with `<header-enricher>`.

* Add support for an 'expression' attribute and sub-element
* Deprecate `delay-header-name`
* Make `DelayHandler.DelayedMessageWrapper` *public* to allow access for Messages in the Store
* Add tests
* Add 'What's new' section
* Polishing Delayer's doc regarding new abilities

JIRA: https://jira.springsource.org/browse/INT-2243, https://jira.springsource.org/browse/INT-3049

INT-2243: ban delay-header-name with expression

INT-2243 Polishing

INT-2243: fall-back to default on Eval Exception

INT-2243: DelayedMessageWrapper refactoring

* Make `DelayedMessageWrapper` Spring Data Mongo mapping compatible.
In terms of Spring Data - add Persistence Constructor

INT-2243 add 'ignore-expression-failures' support

* Add `ignore-expression-failures` to the `<delayer>`
* Add tests for `ignore-expression-failures`
* Add a note to the RM
* Describe SpEL side-effects for `DelayHandler`

Doc Polishing
This commit is contained in:
Artem Bilan
2013-06-15 17:51:58 +03:00
committed by Gary Russell
parent 67fd4a5a60
commit afe56ca6d5
12 changed files with 329 additions and 82 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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,8 @@ package org.springframework.integration.config.xml;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.ExpressionFactoryBean;
import org.springframework.integration.expression.DynamicExpression;
import org.springframework.integration.handler.DelayHandler;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
@@ -43,13 +45,22 @@ public class DelayerParser extends AbstractConsumerEndpointParser {
String defaultDelay = element.getAttribute("default-delay");
String delayHeaderName = element.getAttribute("delay-header-name");
String expression = element.getAttribute(EXPRESSION_ATTRIBUTE);
Element expressionElement = DomUtils.getChildElementByTagName(element, "expression");
boolean hasDefaultDelay = StringUtils.hasText(defaultDelay);
boolean hasDelayHeaderName = StringUtils.hasText(delayHeaderName);
boolean hasExpression = StringUtils.hasText(expression);
boolean hasExpressionElement = expressionElement != null;
if (!(hasDefaultDelay | hasDelayHeaderName)) {
if (!(hasDefaultDelay | hasDelayHeaderName | hasExpression | hasExpressionElement)) {
parserContext.getReaderContext()
.error("The 'default-delay' or 'delay-header-name' attributes should be provided.", element);
.error("The 'default-delay' or 'delay-header-name', or 'expression' attributes, or 'expression' sub-element should be provided.", element);
}
if ((hasDelayHeaderName & (hasExpression | hasExpressionElement)) | (hasExpression & hasExpressionElement)) {
parserContext.getReaderContext()
.error("'delay-header-name', 'expression' attribute and 'expression' sub-element are mutually exclusive.", element);
}
builder.addConstructorArgValue(id + ".messageGroupId");
@@ -62,12 +73,32 @@ public class DelayerParser extends AbstractConsumerEndpointParser {
if (hasDefaultDelay) {
builder.addPropertyValue("defaultDelay", defaultDelay);
}
BeanDefinitionBuilder expressionBuilder = null;
if (hasExpression) {
expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class);
expressionBuilder.addConstructorArgValue(expression);
}
else if(expressionElement != null) {
expressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
DynamicExpression.class);
String key = expressionElement.getAttribute("key");
String expressionSourceReference = expressionElement.getAttribute("source");
expressionBuilder.addConstructorArgValue(key);
expressionBuilder.addConstructorArgReference(expressionSourceReference);
}
if (expressionBuilder != null) {
builder.addPropertyValue("delayExpression", expressionBuilder.getBeanDefinition());
}
if (hasDelayHeaderName) {
builder.addPropertyValue("delayHeaderName", delayHeaderName);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "message-store");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "ignore-expression-failures");
Element txElement = DomUtils.getChildElementByTagName(element, "transactional");
Element adviceChainElement = DomUtils.getChildElementByTagName(element, "advice-chain");

View File

@@ -22,13 +22,22 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.store.MessageGroup;
import org.springframework.integration.store.MessageGroupStore;
import org.springframework.integration.store.MessageStore;
@@ -43,7 +52,7 @@ import org.springframework.util.CollectionUtils;
/**
* A {@link MessageHandler} that is capable of delaying the continuation of a
* Message flow based on the presence of a delay header on an inbound Message
* Message flow based on the result of evaluation {@code delayExpression} on an inbound {@link Message}
* or a default delay value configured on this handler. Note that the
* continuation of the flow is delegated to a {@link TaskScheduler}, and
* therefore, the calling thread does not block. The advantage of this approach
@@ -55,15 +64,15 @@ import org.springframework.util.CollectionUtils;
* is a side-effect of passing the Message to the output channel after the
* delay with a different Thread in control.
* <p>
* When this handler's 'delayHeaderName' property is configured, that value, if
* present on a Message, will take precedence over the handler's 'defaultDelay'
* value. The actual header value may be a long, a String that can be parsed
* When this handler's {@code delayExpression} property is configured, that evaluation result value
* will take precedence over the handler's {@code defaultDelay} value.
* The actual evaluation result value may be a long, a String that can be parsed
* as a long, or a Date. If it is a long, it will be interpreted as the length
* of time to delay in milliseconds counting from the current time (e.g. a
* value of 5000 indicates that the Message can be released as soon as five
* seconds from the current time). If the value is a Date, it will be
* delayed at least until that Date occurs (i.e. the delay in that case is
* equivalent to <code>headerDate.getTime() - new Date().getTime()</code>).
* equivalent to {@code headerDate.getTime() - new Date().getTime()}).
*
* @author Mark Fisher
* @author Artem Bilan
@@ -71,12 +80,19 @@ import org.springframework.util.CollectionUtils;
*/
@ManagedResource
public class DelayHandler extends AbstractReplyProducingMessageHandler implements DelayHandlerManagement, ApplicationListener<ContextRefreshedEvent> {
public class DelayHandler extends AbstractReplyProducingMessageHandler implements DelayHandlerManagement,
ApplicationListener<ContextRefreshedEvent> {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final String messageGroupId;
private volatile long defaultDelay;
private Expression delayExpression;
private volatile boolean ignoreExpressionFailures = true;
private volatile String delayHeaderName;
private volatile MessageGroupStore messageStore;
@@ -87,6 +103,8 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
private volatile MessageHandler releaseHandler = new ReleaseMessageHandler();
private EvaluationContext evaluationContext;
/**
* Create a DelayHandler with the given 'messageGroupId' that is used as 'key' for {@link MessageGroup}
* to store delayed Messages in the {@link MessageGroupStore}. The sending of Messages after
@@ -109,10 +127,10 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
}
/**
* Set the default delay in milliseconds. If no 'delayHeaderName' property
* Set the default delay in milliseconds. If no {@code delayExpression} property
* has been provided, the default delay will be applied to all Messages. If
* a delay should <em>only</em> be applied to Messages with a
* header, then set this value to 0.
* a delay should <em>only</em> be applied to Messages with evaluation result from
* @code delayExpression}, then set this value to 0.
*/
public void setDefaultDelay(long defaultDelay) {
this.defaultDelay = defaultDelay;
@@ -122,11 +140,36 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
* Specify the name of the header that should be checked for a delay period
* (in milliseconds) or a Date to delay until. If this property is set, any
* such header value will take precedence over this handler's default delay.
* @deprecated in favor of {@link #delayExpression}
*/
@Deprecated
public void setDelayHeaderName(String delayHeaderName) {
this.delayHeaderName = delayHeaderName;
}
/**
* 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 expression evaluation will take precedence over this handler's default delay.
*/
public void setDelayExpression(Expression delayExpression) {
this.delayExpression = delayExpression;
}
/**
* Specify whether {@code Exceptions} thrown by {@link #delayExpression} evaluation should be
* ignored (only logged). In this case case the delayer will fall back to the
* to the {@link #defaultDelay}.
* If this property is specified as {@code false}, any {@link #delayExpression} evaluation
* {@code Exception} will be thrown to the caller without falling back to the to the {@link #defaultDelay}.
* Default is {@code true}.
*
* @see #determineDelayForMessage
*/
public void setIgnoreExpressionFailures(boolean ignoreExpressionFailures) {
this.ignoreExpressionFailures = ignoreExpressionFailures;
}
/**
* Specify the {@link MessageGroupStore} that should be used to store Messages
* while awaiting the delay.
@@ -161,7 +204,18 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
else {
Assert.isInstanceOf(MessageStore.class, this.messageStore);
}
if (this.delayHeaderName != null) {
logger.warn("'delayHeaderName' is deprecated in favor of 'delayExpression'");
if (this.delayExpression == null) {
this.delayExpression = expressionParser.parseExpression("headers['" + this.delayHeaderName + "']");
}
}
if (this.getBeanFactory() != null) {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
}
else {
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext();
}
this.releaseHandler = this.createReleaseMessageTask();
}
@@ -185,7 +239,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
* and if {@code delay > 0} schedules 'releaseMessage' task after 'delay'.
*
* @param requestMessage - the Message which may be delayed.
* @return - <code>null</code> if 'requestMessage' is delayed,
* @return - {@code null} if 'requestMessage' is delayed,
* otherwise - 'payload' from 'requestMessage'.
*
* @see #releaseMessage
@@ -210,21 +264,37 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
private long determineDelayForMessage(Message<?> message) {
long delay = this.defaultDelay;
if (this.delayHeaderName != null) {
Object headerValue = message.getHeaders().get(this.delayHeaderName);
if (headerValue instanceof Date) {
delay = ((Date) headerValue).getTime() - new Date().getTime();
if (this.delayExpression != null) {
Exception delayValueException = null;
Object delayValue = null;
try {
delayValue = this.delayExpression.getValue(this.evaluationContext, message);
}
else if (headerValue != null) {
catch (EvaluationException e) {
delayValueException = e;
}
if (delayValue instanceof Date) {
delay = ((Date) delayValue).getTime() - new Date().getTime();
}
else if (delayValue != null) {
try {
delay = Long.valueOf(headerValue.toString());
delay = Long.valueOf(delayValue.toString());
}
catch (NumberFormatException e) {
delayValueException = e;
}
}
if (delayValueException != null) {
if (this.ignoreExpressionFailures) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to parse delay from header value '" + headerValue.toString() +
"', will fall back to default delay: " + this.defaultDelay);
logger.debug("Failed to get delay value from 'delayExpression': " + delayValueException.getMessage() +
". Will fall back to default delay: " + this.defaultDelay);
}
}
else {
throw new MessageHandlingException(message, "Error occurred during 'delay' value determination", delayValueException);
}
}
}
return delay;
@@ -238,7 +308,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
messageWrapper = (DelayedMessageWrapper) message.getPayload();
}
else {
messageWrapper = new DelayedMessageWrapper(message);
messageWrapper = new DelayedMessageWrapper(message, System.currentTimeMillis());
delayedMessage = MessageBuilder.withPayload(messageWrapper).copyHeaders(message.getHeaders()).build();
this.messageStore.addMessageToGroup(this.messageGroupId, delayedMessage);
}
@@ -277,7 +347,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
/**
* Used for reading persisted Messages in the 'messageStore'
* to reschedule them e.g. upon application restart.
* The logic is based on iteration over 'messageGroup.getMessages()'
* The logic is based on iteration over {@code messageGroup.getMessages()}
* and schedules task about 'delay' logic.
* This behavior is dictated by the avoidance of invocation thread overload.
*/
@@ -333,16 +403,17 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
}
private static final class DelayedMessageWrapper implements Serializable {
public static final class DelayedMessageWrapper implements Serializable {
private static final long serialVersionUID = -4739802369074947045L;
private final long requestDate = System.currentTimeMillis();
private final long requestDate;
private final Message<?> original;
public DelayedMessageWrapper(Message<?> original) {
DelayedMessageWrapper(Message<?> original, long requestDate) {
this.original = original;
this.requestDate = requestDate;
}
public long getRequestDate() {
@@ -355,8 +426,12 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DelayedMessageWrapper that = (DelayedMessageWrapper) o;

View File

@@ -1359,8 +1359,8 @@
<xsd:documentation>
Defines an endpoint that passes a Message to the output-channel after a
delay. The delay may
be
retrieved from a Message header or else fallback to the
be dynamically determined by evaluating an expression
(such as a Message header) or fallback to the
'default-delay' of this endpoint.
</xsd:documentation>
</xsd:annotation>
@@ -1384,6 +1384,14 @@
to proxy DelayHandler's 'release Message task'.
</xsd:documentation>
</xsd:annotation>
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1" >
<xsd:annotation>
<xsd:documentation>
Specify the SpEL expression that evaluates to the delay value in milliseconds,
or a java.util.Date.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="transactional" type="transactionalType" minOccurs="0" maxOccurs="1" />
<xsd:element name="advice-chain" type="adviceChainType" minOccurs="0" maxOccurs="1" />
</xsd:choice>
@@ -1393,22 +1401,40 @@
Specify the default delay in milliseconds. This value can be set to 0
if the only Messages
that
should be delayed are those with a particular header (in that
case, be sure to provide
a value for the
'delay-header-name' attribute).
should be delayed are those with a particular expression evaluation result.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify the SpEL expression that evaluates to the delay value in milliseconds,
or a java.util.Date.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="ignore-expression-failures" type="xsd:string" default="true">
<xsd:annotation>
<xsd:documentation>
Specify whether Exceptions thrown by 'expression' evaluation should be
ignored (only logged). In this case case the delayer will fall back to the
to the 'default-delay'. Default behaviour.
If this attribute is specified as 'false', any
'expression' evaluation Exception will be thrown to the caller without
falling back to the to the 'default-delay'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="delay-header-name" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
Specify the name of the header that should contain the delay value.
[DEPRECATED]Specify the name of the header that should contain the delay value.
This value can either
represent the number of milliseconds to delay counting from the current
time or it can be an
absolute Date until
which the Message should be delayed.
This attribute is deprecated in favor of an 'expression' attribute or sub-element.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>

View File

@@ -57,6 +57,22 @@
</advice-chain>
</delayer>
<delayer id="delayerWithExpression"
input-channel="input"
output-channel="output"
expression="100"
ignore-expression-failures="false"/>
<beans:bean id="delayerSource"
class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource"
p:basename="org/springframework/integration/config/xml/delayer-expression"/>
<delayer id="delayerWithExpressionSubElement"
input-channel="input"
output-channel="output">
<expression key="delay" source="delayerSource"/>
</delayer>
<beans:bean id="testScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"
p:poolSize="7"
p:waitForTasksToCompleteOnShutdown="true"/>

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.expression.Expression;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.DelayHandler;
import org.springframework.integration.test.util.TestUtils;
@@ -58,19 +59,16 @@ public class DelayerParserTests {
@Test
public void defaultScheduler() {
Object endpoint = context.getBean("delayerWithDefaultScheduler");
assertEquals(EventDrivenConsumer.class, endpoint.getClass());
Object handler = TestUtils.getPropertyValue(endpoint, "handler");
assertEquals(DelayHandler.class, handler.getClass());
DelayHandler delayHandler = (DelayHandler) handler;
DelayHandler delayHandler = context.getBean("delayerWithDefaultScheduler.handler", DelayHandler.class);
assertEquals(99, delayHandler.getOrder());
DirectFieldAccessor accessor = new DirectFieldAccessor(delayHandler);
assertEquals(context.getBean("output"), accessor.getPropertyValue("outputChannel"));
assertEquals(new Long(1234), accessor.getPropertyValue("defaultDelay"));
assertEquals("foo", accessor.getPropertyValue("delayHeaderName"));
assertEquals(new Long(987), new DirectFieldAccessor(
accessor.getPropertyValue("messagingTemplate")).getPropertyValue("sendTimeout"));
assertNull(accessor.getPropertyValue("taskScheduler"));
assertEquals(context.getBean("output"), TestUtils.getPropertyValue(delayHandler, "outputChannel"));
assertEquals(new Long(1234), TestUtils.getPropertyValue(delayHandler, "defaultDelay", Long.class));
assertEquals("foo", TestUtils.getPropertyValue(delayHandler, "delayHeaderName"));
//INT-2243
assertNotNull(TestUtils.getPropertyValue(delayHandler, "delayExpression"));
assertEquals("headers['foo']", TestUtils.getPropertyValue(delayHandler, "delayExpression", Expression.class).getExpressionString());
assertEquals(new Long(987), TestUtils.getPropertyValue(delayHandler, "messagingTemplate.sendTimeout", Long.class));
assertNull(TestUtils.getPropertyValue(delayHandler, "taskScheduler"));
}
@Test
@@ -134,4 +132,19 @@ public class DelayerParserTests {
assertEquals("{*=PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,readOnly}", nameMap.toString());
}
@Test
public void testInt2243Expression() {
DelayHandler delayHandler = context.getBean("delayerWithExpression.handler", DelayHandler.class);
assertNull(TestUtils.getPropertyValue(delayHandler, "delayHeaderName"));
assertEquals("100", TestUtils.getPropertyValue(delayHandler, "delayExpression", Expression.class).getExpressionString());
assertFalse(TestUtils.getPropertyValue(delayHandler, "ignoreExpressionFailures", Boolean.class));
}
@Test
public void testInt2243ExpressionSubElement() {
DelayHandler delayHandler = context.getBean("delayerWithExpressionSubElement.handler", DelayHandler.class);
assertNull(TestUtils.getPropertyValue(delayHandler, "delayHeaderName"));
assertEquals("headers.timestamp + 1000", TestUtils.getPropertyValue(delayHandler, "delayExpression", Expression.class).getExpressionString());
}
}

View File

@@ -55,4 +55,15 @@
<beans:bean id="sampleHandler" class="org.springframework.integration.config.xml.DelayerUsageTests$SampleService"/>
<channel id="inputC"/>
<channel id="outputC">
<queue/>
</channel>
<beans:bean id="time" class="java.util.Calendar" factory-method="getInstance"/>
<delayer id="delayerExpression" input-channel="inputC" output-channel="outputC" expression="new java.util.Date(@time.timeInMillis + 5000)"/>
</beans:beans>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -56,6 +56,13 @@ public class DelayerUsageTests {
@Autowired @Qualifier("outputB1")
private PollableChannel outputB1;
@Autowired
private MessageChannel inputC;
@Autowired
private PollableChannel outputC;
@Test
public void testDelayWithDefaultScheduler(){
long start = System.currentTimeMillis();
@@ -109,6 +116,16 @@ public class DelayerUsageTests {
assertEquals("hello", message.getPayload());
}
@Test
public void testInt2243DelayerExpression() {
long start = System.currentTimeMillis();
this.inputC.send(new GenericMessage<String>("test"));
Message<?> message = this.outputC.receive(10000);
assertNotNull(message);
assertTrue((System.currentTimeMillis() - start) >= 1000);
assertEquals("test", message.getPayload());
}
public static class SampleService{
public String processMessage(String message) throws Exception {
Thread.sleep(500);

View File

@@ -30,10 +30,14 @@ import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.context.IntegrationContextUtils;
@@ -50,6 +54,7 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
* @author Mark Fisher
* @author Artem Bilan
* @author Gunnar Hillert
* @author Gary Russell
* @since 1.0.3
*/
public class DelayHandlerTests {
@@ -80,6 +85,11 @@ public class DelayHandlerTests {
output.subscribe(resultHandler);
}
private void setDelayExpression() {
Expression expression = new SpelExpressionParser().parseExpression("headers.delay");
this.delayHandler.setDelayExpression(expression);
}
private void startDelayerHandler() {
delayHandler.afterPropertiesSet();
delayHandler.onApplicationEvent(new ContextRefreshedEvent(TestUtils.createTestApplicationContext()));
@@ -108,7 +118,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderAndDefaultDelayWouldTimeout() throws Exception {
delayHandler.setDefaultDelay(5000);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", 100).build();
@@ -121,7 +131,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsNegativeAndDefaultDelayWouldTimeout() throws Exception {
delayHandler.setDefaultDelay(5000);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", -7000).build();
@@ -134,7 +144,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsInvalidFallsBackToDefaultDelay() throws Exception {
delayHandler.setDefaultDelay(5);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", "not a number").build();
@@ -147,7 +157,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsDateInTheFutureAndDefaultDelayWouldTimeout() throws Exception {
delayHandler.setDefaultDelay(5000);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", new Date(new Date().getTime() + 150)).build();
@@ -160,7 +170,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsDateInThePastAndDefaultDelayWouldTimeout() throws Exception {
delayHandler.setDefaultDelay(5000);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", new Date(new Date().getTime() - 60 * 1000)).build();
@@ -172,7 +182,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsNullDateAndDefaultDelayIsZero() throws Exception {
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Date nullDate = null;
Message<?> message = MessageBuilder.withPayload("test")
@@ -185,7 +195,7 @@ public class DelayHandlerTests {
@Test(expected = TestTimedOutException.class)
public void delayHeaderIsFutureDateAndTimesOut() throws Exception {
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Date future = new Date(new Date().getTime() + 60 * 1000);
Message<?> message = MessageBuilder.withPayload("test")
@@ -199,7 +209,7 @@ public class DelayHandlerTests {
@Test
public void delayHeaderIsValidStringAndDefaultDelayWouldTimeout() throws Exception {
delayHandler.setDefaultDelay(5000);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
Message<?> message = MessageBuilder.withPayload("test")
.setHeader("delay", "20").build();
@@ -275,7 +285,7 @@ public class DelayHandlerTests {
MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
errorHandler.setDefaultErrorChannel(errorChannel);
taskScheduler.setErrorHandler(errorHandler);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
output.unsubscribe(resultHandler);
errorChannel.subscribe(resultHandler);
@@ -308,7 +318,7 @@ public class DelayHandlerTests {
MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
errorHandler.setBeanFactory(context);
taskScheduler.setErrorHandler(errorHandler);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
output.unsubscribe(resultHandler);
customErrorChannel.subscribe(resultHandler);
@@ -339,7 +349,7 @@ public class DelayHandlerTests {
MessagePublishingErrorHandler errorHandler = new MessagePublishingErrorHandler();
errorHandler.setBeanFactory(context);
taskScheduler.setErrorHandler(errorHandler);
delayHandler.setDelayHeaderName("delay");
this.setDelayExpression();
this.startDelayerHandler();
output.unsubscribe(resultHandler);
defaultErrorChannel.subscribe(resultHandler);
@@ -360,7 +370,6 @@ public class DelayHandlerTests {
assertNotSame(Thread.currentThread(), resultHandler.lastThread);
}
@Test //INT-1132
public void testReschedulePersistedMessagesOnStartup() throws Exception {
MessageGroupStore messageGroupStore = new SimpleMessageStore();
@@ -416,6 +425,14 @@ public class DelayHandlerTests {
Mockito.verify(this.delayHandler, Mockito.times(1)).reschedulePersistedMessages();
}
@Test(expected = MessageHandlingException.class)
public void testInt2243IgnoreExpressionFailuresAsFalse() throws Exception {
this.setDelayExpression();
this.delayHandler.setIgnoreExpressionFailures(false);
this.startDelayerHandler();
this.delayHandler.handleMessage(new GenericMessage<String>("test"));
}
private void waitForLatch(long timeout) {
try {
this.latch.await(timeout, TimeUnit.MILLISECONDS);