INT-3551: Idempotent Receiver: Add value-strategy
JIRA: https://jira.spring.io/browse/INT-3551 * Rename `MetadataKeyStrategy` -> `MetadataEntryStrategy` * Add `valueStrategy` to the `MetadataStoreSelector` * Add `value-strategy` and `value-expression` to the `<idempotent-receiver>` INT-3551: Add `@IR` support on service methods Rework `MetadataKeyStrategy` just to the `MessageProcessor` Fix Docs Minor Doc Polishing.
This commit is contained in:
committed by
Gary Russell
parent
1018fcf361
commit
0cc9273a2e
@@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* A {@code @Bean} that has a MessagingAnnotation (@code @ServiceActivator, @Router etc.)
|
||||
* A {@code method} that has a MessagingAnnotation (@code @ServiceActivator, @Router etc.)
|
||||
* that also has this annotation, has an
|
||||
* {@link org.springframework.integration.handler.advice.IdempotentReceiverInterceptor} applied
|
||||
* to the associated {@link org.springframework.messaging.MessageHandler#handleMessage} method.
|
||||
|
||||
@@ -27,6 +27,9 @@ import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
|
||||
import org.springframework.aop.support.NameMatchMethodPointcut;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -38,6 +41,7 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.integration.annotation.IdempotentReceiver;
|
||||
import org.springframework.integration.annotation.Poller;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.IntegrationConfigUtils;
|
||||
@@ -131,6 +135,28 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
handler = (MessageHandler) this.beanFactory.initializeBean(handler, handlerBeanName);
|
||||
}
|
||||
|
||||
if (AnnotatedElementUtils.isAnnotated(method, IdempotentReceiver.class.getName())
|
||||
&& !AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
|
||||
String[] interceptors = AnnotationUtils.getAnnotation(method, IdempotentReceiver.class).value();
|
||||
for (String interceptor : interceptors) {
|
||||
DefaultBeanFactoryPointcutAdvisor advisor = new DefaultBeanFactoryPointcutAdvisor();
|
||||
advisor.setAdviceBeanName(interceptor);
|
||||
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
|
||||
pointcut.setMappedName("handleMessage");
|
||||
advisor.setPointcut(pointcut);
|
||||
advisor.setBeanFactory(this.beanFactory);
|
||||
|
||||
if (handler instanceof Advised) {
|
||||
((Advised) handler).addAdvisor(advisor);
|
||||
}
|
||||
else {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
proxyFactory.addAdvisor(advisor);
|
||||
handler = (MessageHandler) proxyFactory.getProxy(this.beanFactory.getBeanClassLoader());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AbstractEndpoint endpoint = createEndpoint(handler, method, annotations);
|
||||
if (endpoint != null) {
|
||||
return endpoint;
|
||||
|
||||
@@ -22,11 +22,14 @@ import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.IdempotentReceiverAutoProxyCreatorInitializer;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.advice.IdempotentReceiverInterceptor;
|
||||
import org.springframework.integration.metadata.ExpressionMetadataKeyStrategy;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.selector.MetadataStoreSelector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -46,28 +49,38 @@ public class IdempotentReceiverInterceptorParser extends AbstractBeanDefinitionP
|
||||
boolean hasSelector = StringUtils.hasText(selector);
|
||||
String store = element.getAttribute("metadata-store");
|
||||
boolean hasStore = StringUtils.hasText(store);
|
||||
String strategy = element.getAttribute("key-strategy");
|
||||
boolean hasStrategy = StringUtils.hasText(strategy);
|
||||
String expression = element.getAttribute("key-expression");
|
||||
boolean hasExpression = StringUtils.hasText(expression);
|
||||
String keyStrategy = element.getAttribute("key-strategy");
|
||||
boolean hasKeyStrategy = StringUtils.hasText(keyStrategy);
|
||||
String keyExpression = element.getAttribute("key-expression");
|
||||
boolean hasKeyExpression = StringUtils.hasText(keyExpression);
|
||||
String valueStrategy = element.getAttribute("value-strategy");
|
||||
boolean hasValueStrategy = StringUtils.hasText(valueStrategy);
|
||||
String valueExpression = element.getAttribute("value-expression");
|
||||
boolean hasValueExpression = StringUtils.hasText(valueExpression);
|
||||
|
||||
String endpoints = element.getAttribute("endpoint");
|
||||
|
||||
if (!hasSelector & !(hasStrategy | hasExpression)) {
|
||||
if (!hasSelector & !(hasKeyStrategy | hasKeyExpression)) {
|
||||
parserContext.getReaderContext().error("One of the 'selector', 'key-strategy' or 'key-expression' " +
|
||||
"attributes must be provided", source);
|
||||
}
|
||||
|
||||
if (hasSelector & (hasStore | hasStrategy | hasExpression)) {
|
||||
if (hasSelector & (hasStore | hasKeyStrategy | hasKeyExpression | hasValueStrategy | hasValueExpression)) {
|
||||
parserContext.getReaderContext().error("The 'selector' attribute is mutually exclusive with " +
|
||||
"'metadata-store', 'key-strategy' or 'key-expression'", source);
|
||||
"'metadata-store', 'key-strategy', 'key-expression', 'value-strategy' " +
|
||||
"or 'value-expression'", source);
|
||||
}
|
||||
|
||||
if (hasStrategy & hasExpression) {
|
||||
if (hasKeyStrategy & hasKeyExpression) {
|
||||
parserContext.getReaderContext().error("The 'key-strategy' and 'key-expression' attributes " +
|
||||
"are mutually exclusive", source);
|
||||
}
|
||||
|
||||
if (hasValueStrategy & hasValueExpression) {
|
||||
parserContext.getReaderContext().error("The 'value-strategy' and 'value-expression' attributes " +
|
||||
"are mutually exclusive", source);
|
||||
}
|
||||
|
||||
if (!StringUtils.hasText(endpoints)) {
|
||||
parserContext.getReaderContext().error("The 'endpoint' attribute is required", source);
|
||||
}
|
||||
@@ -79,20 +92,44 @@ public class IdempotentReceiverInterceptorParser extends AbstractBeanDefinitionP
|
||||
else {
|
||||
BeanDefinitionBuilder selectorBuilder =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(MetadataStoreSelector.class);
|
||||
BeanMetadataElement strategyBeanDefinition = null;
|
||||
if (hasStrategy) {
|
||||
strategyBeanDefinition = new RuntimeBeanReference(strategy);
|
||||
BeanMetadataElement keyStrategyBeanDefinition = null;
|
||||
if (hasKeyStrategy) {
|
||||
keyStrategyBeanDefinition = new RuntimeBeanReference(keyStrategy);
|
||||
}
|
||||
else {
|
||||
strategyBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionMetadataKeyStrategy.class)
|
||||
.addConstructorArgValue(expression)
|
||||
keyStrategyBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageProcessor.class)
|
||||
.addConstructorArgValue(
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class).
|
||||
addConstructorArgValue(keyExpression)
|
||||
.getBeanDefinition()
|
||||
)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
selectorBuilder.addConstructorArgValue(strategyBeanDefinition);
|
||||
selectorBuilder.addConstructorArgValue(keyStrategyBeanDefinition);
|
||||
|
||||
BeanMetadataElement valueStrategyBeanDefinition = null;
|
||||
if (hasValueStrategy) {
|
||||
valueStrategyBeanDefinition = new RuntimeBeanReference(valueStrategy);
|
||||
}
|
||||
else if (hasValueExpression) {
|
||||
valueStrategyBeanDefinition =
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionEvaluatingMessageProcessor.class)
|
||||
.addConstructorArgValue(
|
||||
BeanDefinitionBuilder.genericBeanDefinition(ExpressionFactoryBean.class).
|
||||
addConstructorArgValue(valueExpression)
|
||||
.getBeanDefinition()
|
||||
)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
selectorBuilder.addConstructorArgValue(valueStrategyBeanDefinition);
|
||||
|
||||
if (hasStore) {
|
||||
selectorBuilder.addConstructorArgReference(store);
|
||||
}
|
||||
else {
|
||||
selectorBuilder.addConstructorArgValue(new RootBeanDefinition(SimpleMetadataStore.class));
|
||||
}
|
||||
selectorBeanDefinition = selectorBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.util.Assert;
|
||||
* with the Message itself as the root object within the evaluation context.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ExpressionEvaluatingMessageProcessor<T> extends AbstractMessageProcessor<T> {
|
||||
@@ -37,18 +38,15 @@ public class ExpressionEvaluatingMessageProcessor<T> extends AbstractMessageProc
|
||||
|
||||
/**
|
||||
* Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression.
|
||||
*
|
||||
* @param expression The expression.
|
||||
*/
|
||||
public ExpressionEvaluatingMessageProcessor(Expression expression) {
|
||||
this(expression, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression
|
||||
* and expected type for its evaluation result.
|
||||
*
|
||||
* @param expression The expression.
|
||||
* @param expectedType The expected type.
|
||||
*/
|
||||
@@ -63,11 +61,9 @@ public class ExpressionEvaluatingMessageProcessor<T> extends AbstractMessageProc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes the Message by evaluating the expression with that Message as the
|
||||
* root object. The expression evaluation result Object will be returned.
|
||||
*
|
||||
* @param message The message.
|
||||
* @return The result of processing the message.
|
||||
*/
|
||||
@@ -76,4 +72,9 @@ public class ExpressionEvaluatingMessageProcessor<T> extends AbstractMessageProc
|
||||
return this.evaluateExpression(this.expression, message, this.expectedType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExpressionEvaluatingMessageProcessor for: [" + this.expression.getExpressionString() + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 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.metadata;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* The expression based {@link MetadataKeyStrategy} implementation.
|
||||
* The provided {@link Message} is used as the evaluation context root object.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 4.1
|
||||
*/
|
||||
public class ExpressionMetadataKeyStrategy implements MetadataKeyStrategy, BeanFactoryAware {
|
||||
|
||||
private static final ExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
private final MessageProcessor<String> processor;
|
||||
|
||||
private final String expressionString;
|
||||
|
||||
public ExpressionMetadataKeyStrategy(String expressionString) {
|
||||
this.processor = new ExpressionEvaluatingMessageProcessor<String>(PARSER.parseExpression(expressionString));
|
||||
this.expressionString = expressionString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
((BeanFactoryAware) this.processor).setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKey(Message<?> message) {
|
||||
return this.processor.processMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExpressionEvaluatingSelector for: [" + this.expressionString + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 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.metadata;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* The strategy to extract a {@code key} for the {@code MetadataStore}
|
||||
* from the provided {@link Message}.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface MetadataKeyStrategy {
|
||||
|
||||
String getKey(Message<?> message);
|
||||
|
||||
}
|
||||
@@ -17,19 +17,19 @@
|
||||
package org.springframework.integration.selector;
|
||||
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.metadata.ConcurrentMetadataStore;
|
||||
import org.springframework.integration.metadata.MetadataKeyStrategy;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* The {@link MessageSelector} implementation using a {@link ConcurrentMetadataStore}
|
||||
* and {@link MetadataKeyStrategy}.
|
||||
* and {@link MessageProcessor<String>}.
|
||||
* <p>
|
||||
* The {@link #accept} method extracts {@code metadataKey} from the provided {@code message}
|
||||
* using {@link MetadataKeyStrategy} and uses the {@code timestamp} header as the {@code value}
|
||||
* (hex).
|
||||
* using {@link MessageProcessor<String>} and uses the {@code timestamp} header as the {@code value}
|
||||
* (hex) by default. The {@link #valueStrategy} can be provided to override the default behaviour.
|
||||
* <p>
|
||||
* The successful result of the {@link #accept} method is based on the
|
||||
* {@link ConcurrentMetadataStore#putIfAbsent} return value. {@code true} is returned
|
||||
@@ -52,23 +52,38 @@ public class MetadataStoreSelector implements MessageSelector {
|
||||
|
||||
private final ConcurrentMetadataStore metadataStore;
|
||||
|
||||
private final MetadataKeyStrategy keyStrategy;
|
||||
private final MessageProcessor<String> keyStrategy;
|
||||
|
||||
public MetadataStoreSelector(MetadataKeyStrategy keyStrategy) {
|
||||
this(keyStrategy, new SimpleMetadataStore());
|
||||
private final MessageProcessor<String> valueStrategy;
|
||||
|
||||
public MetadataStoreSelector(MessageProcessor<String> keyStrategy) {
|
||||
this(keyStrategy, (MessageProcessor<String>) null);
|
||||
}
|
||||
|
||||
public MetadataStoreSelector(MetadataKeyStrategy keyStrategy, ConcurrentMetadataStore metadataStore) {
|
||||
Assert.notNull(metadataStore);
|
||||
public MetadataStoreSelector(MessageProcessor<String> keyStrategy, MessageProcessor<String> valueStrategy) {
|
||||
this(keyStrategy, valueStrategy, new SimpleMetadataStore());
|
||||
}
|
||||
|
||||
public MetadataStoreSelector(MessageProcessor<String> keyStrategy, ConcurrentMetadataStore metadataStore) {
|
||||
this(keyStrategy, null, metadataStore);
|
||||
}
|
||||
|
||||
public MetadataStoreSelector(MessageProcessor<String> keyStrategy, MessageProcessor<String> valueStrategy,
|
||||
ConcurrentMetadataStore metadataStore) {
|
||||
Assert.notNull(keyStrategy);
|
||||
Assert.notNull(metadataStore);
|
||||
this.metadataStore = metadataStore;
|
||||
this.keyStrategy = keyStrategy;
|
||||
this.valueStrategy = valueStrategy;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean accept(Message<?> message) {
|
||||
String key = this.keyStrategy.getKey(message);
|
||||
String value = Long.toString(message.getHeaders().getTimestamp());
|
||||
String key = this.keyStrategy.processMessage(message);
|
||||
String value = (this.valueStrategy != null)
|
||||
? this.valueStrategy.processMessage(message)
|
||||
: Long.toString(message.getHeaders().getTimestamp());
|
||||
|
||||
return this.metadataStore.putIfAbsent(key, value) == null;
|
||||
}
|
||||
|
||||
@@ -4364,11 +4364,11 @@ The list of component name patterns you want to track (e.g., tracked-components
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.metadata.MetadataKeyStrategy" />
|
||||
<tool:expected-type type="org.springframework.integration.handler.MessageProcessor" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
The 'MetadataKeyStrategy' reference. Used by the underlying
|
||||
A 'MessageProcessor' reference. Used by the underlying
|
||||
'org.springframework.integration.selector.MetadataStoreSelector'.
|
||||
Evaluates an 'idempotentKey' from the request Message.
|
||||
Mutually exclusive with 'selector' and 'key-expression'.
|
||||
@@ -4378,13 +4378,41 @@ The list of component name patterns you want to track (e.g., tracked-components
|
||||
<xsd:attribute name="key-expression">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Expression to populate an 'org.springframework.integration.metadata.ExpressionMetadataKeyStrategy'.
|
||||
Expression to populate an 'ExpressionEvaluatingMessageProcessor'.
|
||||
Used by the underlying 'org.springframework.integration.selector.MetadataStoreSelector'.
|
||||
Evaluates an 'idempotentKey' using the request Message as the evaluation context root object.
|
||||
Mutually exclusive with 'selector' and 'key-strategy'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="value-strategy" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.handler.MessageProcessor" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
<xsd:documentation>
|
||||
A 'MessageProcessor' reference. Used by the underlying
|
||||
'org.springframework.integration.selector.MetadataStoreSelector'.
|
||||
Evaluates a 'value' for the 'idempotentKey' from the request Message.
|
||||
Mutually exclusive with 'selector' and 'value-expression'.
|
||||
By default, the 'MetadataStoreSelector' uses the 'timestamp' message header as the Metadata 'value'.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="value-expression">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Expression to populate an 'ExpressionEvaluatingMessageProcessor'.
|
||||
Used by the underlying 'org.springframework.integration.selector.MetadataStoreSelector'.
|
||||
Evaluates a 'value' for the 'idempotentKey' using the request Message as the evaluation context
|
||||
root object.
|
||||
Mutually exclusive with 'selector' and 'value-strategy'.
|
||||
By default, the 'MetadataStoreSelector' uses the 'timestamp' message header as the Metadata 'value'.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="discard-channel" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
|
||||
@@ -16,11 +16,19 @@
|
||||
<idempotent-receiver id="selectorInterceptor" endpoint="foo" selector="selector"/>
|
||||
|
||||
<beans:bean id="keyStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.springframework.integration.metadata.MetadataKeyStrategy"/>
|
||||
<beans:constructor-arg value="org.springframework.integration.handler.MessageProcessor"/>
|
||||
</beans:bean>
|
||||
|
||||
<idempotent-receiver id="strategyInterceptor" endpoint="foo" key-strategy="keyStrategy"
|
||||
discard-channel="nullChannel" throw-exception-on-rejection="true"/>
|
||||
<beans:bean id="valueStrategy" class="org.mockito.Mockito" factory-method="mock">
|
||||
<beans:constructor-arg value="org.springframework.integration.handler.MessageProcessor"/>
|
||||
</beans:bean>
|
||||
|
||||
<idempotent-receiver id="strategyInterceptor"
|
||||
endpoint="foo"
|
||||
key-strategy="keyStrategy"
|
||||
value-strategy="valueStrategy"
|
||||
discard-channel="nullChannel"
|
||||
throw-exception-on-rejection="true"/>
|
||||
|
||||
<beans:bean id="store" class="org.springframework.integration.metadata.SimpleMetadataStore"/>
|
||||
|
||||
@@ -31,7 +39,7 @@
|
||||
<context:property-placeholder properties-ref="properties"/>
|
||||
|
||||
<idempotent-receiver id="expressionInterceptor" endpoint="foo, ${bar}"
|
||||
metadata-store="store"
|
||||
key-expression="headers.foo"/>
|
||||
metadata-store="store"
|
||||
key-expression="headers.foo"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -46,9 +46,9 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.handler.advice.IdempotentReceiverInterceptor;
|
||||
import org.springframework.integration.metadata.ExpressionMetadataKeyStrategy;
|
||||
import org.springframework.integration.metadata.MetadataKeyStrategy;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.selector.MetadataStoreSelector;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -79,7 +79,10 @@ public class IdempotentReceiverParserTests {
|
||||
private IdempotentReceiverInterceptor strategyInterceptor;
|
||||
|
||||
@Autowired
|
||||
private MetadataKeyStrategy keyStrategy;
|
||||
private MessageProcessor<String> keyStrategy;
|
||||
|
||||
@Autowired
|
||||
private MessageProcessor<String> valueStrategy;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("nullChannel")
|
||||
@@ -113,6 +116,7 @@ public class IdempotentReceiverParserTests {
|
||||
Object messageSelector = getPropertyValue(this.strategyInterceptor, "messageSelector");
|
||||
assertThat(messageSelector, instanceOf(MetadataStoreSelector.class));
|
||||
assertSame(this.keyStrategy, getPropertyValue(messageSelector, "keyStrategy"));
|
||||
assertSame(this.valueStrategy, getPropertyValue(messageSelector, "valueStrategy"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> idempotentEndpoints =
|
||||
(Map<String, List<String>>) getPropertyValue(this.idempotentReceiverAutoProxyCreator,
|
||||
@@ -129,7 +133,7 @@ public class IdempotentReceiverParserTests {
|
||||
assertThat(messageSelector, instanceOf(MetadataStoreSelector.class));
|
||||
assertSame(this.store, getPropertyValue(messageSelector, "metadataStore"));
|
||||
Object keyStrategy = getPropertyValue(messageSelector, "keyStrategy");
|
||||
assertThat(keyStrategy, instanceOf(ExpressionMetadataKeyStrategy.class));
|
||||
assertThat(keyStrategy, instanceOf(ExpressionEvaluatingMessageProcessor.class));
|
||||
assertThat(keyStrategy.toString(), containsString("headers.foo"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> idempotentEndpoints =
|
||||
@@ -176,40 +180,66 @@ public class IdempotentReceiverParserTests {
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'selector' attribute is mutually exclusive with 'metadata-store', " +
|
||||
"'key-strategy' or 'key-expression'"));
|
||||
"'key-strategy', 'key-expression', 'value-strategy' or 'value-expression'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectorAndStrategy() throws Exception {
|
||||
public void testSelectorAndKeyStrategy() throws Exception {
|
||||
try {
|
||||
bootStrap("selector-and-strategy");
|
||||
bootStrap("selector-and-key-strategy");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'selector' attribute is mutually exclusive with 'metadata-store', " +
|
||||
"'key-strategy' or 'key-expression'"));
|
||||
"'key-strategy', 'key-expression', 'value-strategy' or 'value-expression'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectorAndExpression() throws Exception {
|
||||
public void testSelectorAndKeyExpression() throws Exception {
|
||||
try {
|
||||
bootStrap("selector-and-expression");
|
||||
bootStrap("selector-and-key-expression");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'selector' attribute is mutually exclusive with 'metadata-store', " +
|
||||
"'key-strategy' or 'key-expression'"));
|
||||
"'key-strategy', 'key-expression', 'value-strategy' or 'value-expression'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrategyAndExpression() throws Exception {
|
||||
public void testSelectorAndValueStrategy() throws Exception {
|
||||
try {
|
||||
bootStrap("strategy-and-expression");
|
||||
bootStrap("selector-and-value-strategy");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'selector' attribute is mutually exclusive with 'metadata-store', " +
|
||||
"'key-strategy', 'key-expression', 'value-strategy' or 'value-expression'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectorAndValueExpression() throws Exception {
|
||||
try {
|
||||
bootStrap("selector-and-value-expression");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'selector' attribute is mutually exclusive with 'metadata-store', " +
|
||||
"'key-strategy', 'key-expression', 'value-strategy' or 'value-expression'"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyStrategyAndKeyExpression() throws Exception {
|
||||
try {
|
||||
bootStrap("key-strategy-and-key-expression");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
@@ -218,6 +248,18 @@ public class IdempotentReceiverParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValueStrategyAndValueExpression() throws Exception {
|
||||
try {
|
||||
bootStrap("value-strategy-and-value-expression");
|
||||
fail("BeanDefinitionParsingException expected");
|
||||
}
|
||||
catch (BeanDefinitionParsingException e) {
|
||||
assertThat(e.getMessage(),
|
||||
containsString("The 'value-strategy' and 'value-expression' attributes are mutually exclusive"));
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationContext bootStrap(String configProperty) throws Exception {
|
||||
PropertiesFactoryBean pfb = new PropertiesFactoryBean();
|
||||
pfb.setLocation(new ClassPathResource(
|
||||
|
||||
@@ -13,8 +13,17 @@ without-endpoint=<int:idempotent-receiver endpoint="" selector="selector"/>
|
||||
|
||||
selector-and-store=<int:idempotent-receiver endpoint="foo" selector="selector" metadata-store="store"/>
|
||||
|
||||
selector-and-strategy=<int:idempotent-receiver endpoint="foo" selector="selector" key-strategy="strategy"/>
|
||||
selector-and-key-strategy=<int:idempotent-receiver endpoint="foo" selector="selector" key-strategy="strategy"/>
|
||||
|
||||
selector-and-expression=<int:idempotent-receiver endpoint="foo" selector="selector" key-expression="expression"/>
|
||||
selector-and-key-expression=<int:idempotent-receiver endpoint="foo" selector="selector" key-expression="expression"/>
|
||||
|
||||
strategy-and-expression=<int:idempotent-receiver endpoint="foo" key-strategy="strategy" key-expression="expression"/>
|
||||
selector-and-value-strategy=<int:idempotent-receiver endpoint="foo" selector="selector" value-strategy="strategy"/>
|
||||
|
||||
selector-and-value-expression=<int:idempotent-receiver endpoint="foo" selector="selector" \
|
||||
value-expression="expression"/>
|
||||
|
||||
key-strategy-and-key-expression=<int:idempotent-receiver endpoint="foo" key-strategy="strategy" \
|
||||
key-expression="expression"/>
|
||||
|
||||
value-strategy-and-value-expression=<int:idempotent-receiver endpoint="foo" key-strategy="strategy" \
|
||||
value-strategy="strategy" value-expression="expression"/>
|
||||
|
||||
@@ -33,16 +33,14 @@ import org.mockito.Mockito;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
import org.springframework.integration.MessageRejectedException;
|
||||
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
|
||||
import org.springframework.integration.metadata.ConcurrentMetadataStore;
|
||||
import org.springframework.integration.metadata.ExpressionMetadataKeyStrategy;
|
||||
import org.springframework.integration.metadata.MetadataStore;
|
||||
import org.springframework.integration.metadata.SimpleMetadataStore;
|
||||
import org.springframework.integration.selector.MetadataStoreSelector;
|
||||
import org.springframework.integration.support.DefaultMessageBuilderFactory;
|
||||
import org.springframework.integration.support.MessageBuilderFactory;
|
||||
import org.springframework.integration.support.utils.IntegrationUtils;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -86,7 +84,8 @@ public class IdempotentReceiverTests {
|
||||
@Test
|
||||
public void testIdempotentReceiverInterceptor() {
|
||||
ConcurrentMetadataStore store = new SimpleMetadataStore();
|
||||
ExpressionMetadataKeyStrategy idempotentKeyStrategy = new ExpressionMetadataKeyStrategy("payload");
|
||||
ExpressionEvaluatingMessageProcessor<String> idempotentKeyStrategy =
|
||||
new ExpressionEvaluatingMessageProcessor<>(new SpelExpressionParser().parseExpression("payload"));
|
||||
BeanFactory beanFactory = Mockito.mock(BeanFactory.class);
|
||||
idempotentKeyStrategy.setBeanFactory(beanFactory);
|
||||
IdempotentReceiverInterceptor idempotentReceiverInterceptor =
|
||||
|
||||
Reference in New Issue
Block a user