Merge remote-tracking branch 'upstream/master' into 4.0.0-WIP
Conflicts: spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests.java spring-integration-core/src/test/java/org/springframework/integration/router/config/RouterWithMappingTests.java spring-integration-groovy/src/main/java/org/springframework/integration/groovy/GroovyScriptExecutingMessageProcessor.java spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/store/channel/AbstractTxTimeoutMessageStoreTests.java spring-integration-jmx/src/main/java/org/springframework/integration/jmx/OperationInvokingMessageHandler.java Resolved.
This commit is contained in:
@@ -82,8 +82,7 @@ public class AmqpOutboundEndpoint extends AbstractReplyProducingMessageHandler
|
||||
private volatile MessageChannel returnChannel;
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
Assert.state(exchangeNameExpression == null || exchangeName == null,
|
||||
"Either an exchangeName or an exchangeNameExpression can be provided, but not both");
|
||||
Assert.state(this.confirmCorrelationExpression == null || !this.expectReply,
|
||||
@@ -116,7 +115,7 @@ public class AmqpOutboundEndpoint extends AbstractReplyProducingMessageHandler
|
||||
}
|
||||
if (this.returnChannel != null) {
|
||||
Assert.isTrue(amqpTemplate instanceof RabbitTemplate, "RabbitTemplate implementation is required for publisher returns");
|
||||
( (RabbitTemplate) this.amqpTemplate).setReturnCallback(this);
|
||||
((RabbitTemplate) this.amqpTemplate).setReturnCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +288,7 @@ public class AmqpOutboundEndpoint extends AbstractReplyProducingMessageHandler
|
||||
|
||||
public void returnedMessage(org.springframework.amqp.core.Message message, int replyCode, String replyText,
|
||||
String exchange, String routingKey) {
|
||||
// safe to cast; we asserted we have a RabbitTemplate in onInit()
|
||||
// safe to cast; we asserted we have a RabbitTemplate in doInit()
|
||||
MessageConverter converter = ((RabbitTemplate) this.amqpTemplate).getMessageConverter();
|
||||
Object returnedObject = converter.fromMessage(message);
|
||||
MessageBuilder<?> builder = (returnedObject instanceof Message)
|
||||
|
||||
@@ -77,10 +77,10 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM
|
||||
if (this.targetObject != null) {
|
||||
Assert.state(this.expression == null,
|
||||
"The 'targetObject' and 'expression' properties are mutually exclusive.");
|
||||
boolean targetIsDirectReplyProducingHandler = this.extractTypeIfPossible(targetObject,
|
||||
AbstractReplyProducingMessageHandler.class) != null
|
||||
&& this.canBeUsedDirect(
|
||||
(AbstractReplyProducingMessageHandler) targetObject) // give subclasses a say
|
||||
AbstractReplyProducingMessageHandler actualHandler = this.extractTypeIfPossible(targetObject,
|
||||
AbstractReplyProducingMessageHandler.class);
|
||||
boolean targetIsDirectReplyProducingHandler = actualHandler != null
|
||||
&& this.canBeUsedDirect(actualHandler) // give subclasses a say
|
||||
&& this.methodIsHandleMessageOrEmpty(this.targetMethodName);
|
||||
if (this.targetObject instanceof MessageProcessor<?>) {
|
||||
handler = this.createMessageProcessingHandler((MessageProcessor<?>) this.targetObject);
|
||||
@@ -89,9 +89,9 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Wiring handler (" + beanName + ") directly into endpoint");
|
||||
}
|
||||
this.checkReuse(actualHandler);
|
||||
this.postProcessReplyProducer(actualHandler);
|
||||
handler = (MessageHandler) targetObject;
|
||||
this.checkReuse((AbstractReplyProducingMessageHandler) handler);
|
||||
this.postProcessReplyProducer((AbstractReplyProducingMessageHandler) handler);
|
||||
}
|
||||
else {
|
||||
handler = this.createMethodInvokingHandler(this.targetObject, this.targetMethodName);
|
||||
@@ -120,7 +120,7 @@ abstract class AbstractStandardMessageHandlerFactoryBean extends AbstractSimpleM
|
||||
}
|
||||
|
||||
private void checkReuse(AbstractReplyProducingMessageHandler replyHandler) {
|
||||
Assert.isTrue(!referencedReplyProducers.contains(targetObject),
|
||||
Assert.isTrue(!referencedReplyProducers.contains(replyHandler),
|
||||
"An AbstractReplyProducingMessageHandler may only be referenced once (" +
|
||||
replyHandler.getComponentName() + ") - use scope=\"prototype\"");
|
||||
referencedReplyProducers.add(replyHandler);
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
|
||||
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -43,7 +44,7 @@ import org.springframework.util.xml.DomUtils;
|
||||
public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
|
||||
private static String[] referenceAttributes = new String[] {
|
||||
"default-request-channel", "default-reply-channel", "error-channel", "message-mapper", "async-executor"
|
||||
"default-request-channel", "default-reply-channel", "error-channel", "message-mapper", "async-executor", "mapper"
|
||||
};
|
||||
|
||||
private static String[] innerAttributes = new String[] {
|
||||
@@ -93,9 +94,16 @@ public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, attributeName);
|
||||
}
|
||||
|
||||
boolean hasMapper = StringUtils.hasText(element.getAttribute("mapper"));
|
||||
boolean hasDefaultPayloadExpression = StringUtils.hasText(element.getAttribute("default-payload-expression"));
|
||||
Assert.state(hasMapper ? !hasDefaultPayloadExpression : true, "'default-payload-expression' is not allowed when a 'mapper' is provided");
|
||||
|
||||
List<Element> invocationHeaders = DomUtils.getChildElementsByTagName(element, "default-header");
|
||||
if (!CollectionUtils.isEmpty(invocationHeaders)
|
||||
|| StringUtils.hasText(element.getAttribute("default-payload-expression"))) {
|
||||
boolean hasDefaultHeaders = !CollectionUtils.isEmpty(invocationHeaders);
|
||||
|
||||
Assert.state(hasMapper ? !hasDefaultHeaders : true, "default-header elements are not allowed when a 'mapper' is provided");
|
||||
|
||||
if (hasDefaultHeaders || hasDefaultPayloadExpression) {
|
||||
BeanDefinitionBuilder methodMetadataBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
"org.springframework.integration.gateway.GatewayMethodMetadata");
|
||||
this.setMethodInvocationHeaders(methodMetadataBuilder, invocationHeaders);
|
||||
@@ -118,8 +126,11 @@ public class GatewayParser extends AbstractSimpleBeanDefinitionParser {
|
||||
methodMetadataBuilder.addPropertyValue("requestTimeout", methodElement.getAttribute("request-timeout"));
|
||||
methodMetadataBuilder.addPropertyValue("replyTimeout", methodElement.getAttribute("reply-timeout"));
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(methodMetadataBuilder, methodElement, "payload-expression");
|
||||
Assert.state(hasMapper ? !StringUtils.hasText(element.getAttribute("payload-expression")) : true,
|
||||
"'payload-expression' is not allowed when a 'mapper' is provided");
|
||||
invocationHeaders = DomUtils.getChildElementsByTagName(methodElement, "header");
|
||||
if (!CollectionUtils.isEmpty(invocationHeaders)) {
|
||||
Assert.state(!hasMapper, "header elements are not allowed when a 'mapper' is provided");
|
||||
this.setMethodInvocationHeaders(methodMetadataBuilder, invocationHeaders);
|
||||
}
|
||||
methodMetadataMap.put(methodName, methodMetadataBuilder.getBeanDefinition());
|
||||
|
||||
@@ -20,12 +20,14 @@ import java.util.List;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
@@ -33,14 +35,13 @@ import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.core.Conventions;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.ExpressionFactoryBean;
|
||||
import org.springframework.integration.config.SpelFunctionFactoryBean;
|
||||
import org.springframework.integration.endpoint.AbstractPollingEndpoint;
|
||||
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
|
||||
import org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
@@ -462,6 +463,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
}
|
||||
return expressionDef;
|
||||
}
|
||||
|
||||
public static void registerSpelFunctionBean(BeanDefinitionRegistry registry, String functionId, String className,
|
||||
String methodSignature) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(SpelFunctionFactoryBean.class)
|
||||
@@ -469,6 +471,7 @@ public abstract class IntegrationNamespaceUtils {
|
||||
.addConstructorArgValue(methodSignature);
|
||||
registry.registerBeanDefinition(functionId, builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
public static BeanDefinition createExpressionDefIfAttributeDefined(String expressionElementName, Element element) {
|
||||
|
||||
Assert.hasText(expressionElementName, "'expressionElementName' must no be empty");
|
||||
|
||||
@@ -98,8 +98,7 @@ public class MessageFilter extends AbstractReplyProducingPostProcessingMessageHa
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
if (this.selector instanceof AbstractMessageProcessingSelector) {
|
||||
((AbstractMessageProcessingSelector) this.selector).setConversionService(this.getConversionService());
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.integration.annotation.Headers;
|
||||
import org.springframework.integration.annotation.Payload;
|
||||
import org.springframework.integration.expression.ExpressionUtils;
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
import org.springframework.integration.mapping.MessageMappingException;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -85,6 +86,8 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
|
||||
|
||||
private final List<MethodParameter> parameterList;
|
||||
|
||||
private final MethodArgsMessageMapper argsMapper;
|
||||
|
||||
private volatile Expression payloadExpression;
|
||||
|
||||
private final Map<String, Expression> parameterPayloadExpressions = new HashMap<String, Expression>();
|
||||
@@ -93,23 +96,28 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
|
||||
|
||||
public GatewayMethodInboundMessageMapper(Method method) {
|
||||
this(method, null);
|
||||
}
|
||||
|
||||
public GatewayMethodInboundMessageMapper(Method method, Map<String, Expression> headerExpressions) {
|
||||
this(method, headerExpressions, null);
|
||||
this(method, headerExpressions, null, null);
|
||||
}
|
||||
|
||||
public GatewayMethodInboundMessageMapper(Method method, Map<String, Expression> headerExpressions,
|
||||
Map<String, Expression> globalHeaderExpressions) {
|
||||
Map<String, Expression> globalHeaderExpressions, MethodArgsMessageMapper mapper) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
this.method = method;
|
||||
this.headerExpressions = headerExpressions;
|
||||
this.globalHeaderExpressions = globalHeaderExpressions;
|
||||
this.parameterList = getMethodParameterList(method);
|
||||
this.payloadExpression = parsePayloadExpression(method);
|
||||
if (mapper == null) {
|
||||
this.argsMapper = new DefaultMethodArgsMessageMapper();
|
||||
}
|
||||
else {
|
||||
this.argsMapper = mapper;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,85 +143,17 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
|
||||
}
|
||||
|
||||
private Message<?> mapArgumentsToMessage(Object[] arguments) {
|
||||
Object messageOrPayload = null;
|
||||
boolean foundPayloadAnnotation = false;
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
EvaluationContext methodInvocationEvaluationContext = createMethodInvocationEvaluationContext(arguments);
|
||||
if (this.payloadExpression != null) {
|
||||
messageOrPayload = this.payloadExpression.getValue(methodInvocationEvaluationContext);
|
||||
try {
|
||||
return this.argsMapper.toMessage(new MethodArgsHolder(this.method, arguments));
|
||||
}
|
||||
for (int i = 0; i < this.parameterList.size(); i++) {
|
||||
Object argumentValue = arguments[i];
|
||||
MethodParameter methodParameter = this.parameterList.get(i);
|
||||
Annotation annotation = this.findMappingAnnotation(methodParameter.getParameterAnnotations());
|
||||
if (annotation != null) {
|
||||
if (annotation.annotationType().equals(Payload.class)) {
|
||||
if (messageOrPayload != null) {
|
||||
this.throwExceptionForMultipleMessageOrPayloadParameters(methodParameter);
|
||||
}
|
||||
String expression = ((Payload) annotation).value();
|
||||
if (!StringUtils.hasText(expression)) {
|
||||
messageOrPayload = argumentValue;
|
||||
}
|
||||
else {
|
||||
messageOrPayload = this.evaluatePayloadExpression(expression, argumentValue);
|
||||
}
|
||||
foundPayloadAnnotation = true;
|
||||
}
|
||||
else if (annotation.annotationType().equals(Header.class)) {
|
||||
Header headerAnnotation = (Header) annotation;
|
||||
String headerName = this.determineHeaderName(headerAnnotation, methodParameter);
|
||||
if (headerAnnotation.required() && argumentValue == null) {
|
||||
throw new IllegalArgumentException("Received null argument value for required header: '" + headerName + "'");
|
||||
}
|
||||
headers.put(headerName, argumentValue);
|
||||
}
|
||||
else if (annotation.annotationType().equals(Headers.class)) {
|
||||
if (argumentValue != null) {
|
||||
if (!(argumentValue instanceof Map)) {
|
||||
throw new IllegalArgumentException("@Headers annotation is only valid for Map-typed parameters");
|
||||
}
|
||||
for (Object key : ((Map<?, ?>) argumentValue).keySet()) {
|
||||
Assert.isInstanceOf(String.class, key, "Invalid header name [" + key +
|
||||
"], name type must be String.");
|
||||
Object value = ((Map<?, ?>) argumentValue).get(key);
|
||||
headers.put((String) key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (e instanceof MessagingException) {
|
||||
throw (MessagingException) e;
|
||||
}
|
||||
else if (messageOrPayload == null) {
|
||||
messageOrPayload = argumentValue;
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(methodParameter.getParameterType())) {
|
||||
if (messageOrPayload instanceof Map && !foundPayloadAnnotation) {
|
||||
if (payloadExpression == null){
|
||||
throw new MessagingException("Ambiguous method parameters; found more than one " +
|
||||
"Map-typed parameter and neither one contains a @Payload annotation");
|
||||
}
|
||||
}
|
||||
this.copyHeaders((Map<?, ?>) argumentValue, headers);
|
||||
}
|
||||
else if (this.payloadExpression == null) {
|
||||
this.throwExceptionForMultipleMessageOrPayloadParameters(methodParameter);
|
||||
else {
|
||||
throw new MessageMappingException("Failed to map arguments", e);
|
||||
}
|
||||
}
|
||||
Assert.isTrue(messageOrPayload != null, "unable to determine a Message or payload parameter on method [" + method + "]");
|
||||
MessageBuilder<?> builder = (messageOrPayload instanceof Message)
|
||||
? MessageBuilder.fromMessage((Message<?>) messageOrPayload)
|
||||
: MessageBuilder.withPayload(messageOrPayload);
|
||||
builder.copyHeadersIfAbsent(headers);
|
||||
// Explicit headers in XML override any @Header annotations...
|
||||
if (!CollectionUtils.isEmpty(this.headerExpressions)) {
|
||||
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext, this.headerExpressions);
|
||||
builder.copyHeaders(evaluatedHeaders);
|
||||
}
|
||||
// ...whereas global (default) headers do not...
|
||||
if (!CollectionUtils.isEmpty(this.globalHeaderExpressions)) {
|
||||
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext, this.globalHeaderExpressions);
|
||||
builder.copyHeadersIfAbsent(evaluatedHeaders);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Map<String, Object> evaluateHeaders(EvaluationContext methodInvocationEvaluationContext, Map<String, Expression> headerExpressions) {
|
||||
@@ -318,4 +258,94 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
|
||||
return expression;
|
||||
}
|
||||
|
||||
public class DefaultMethodArgsMessageMapper implements MethodArgsMessageMapper {
|
||||
|
||||
@Override
|
||||
public Message<?> toMessage(MethodArgsHolder holder) throws Exception {
|
||||
Object messageOrPayload = null;
|
||||
boolean foundPayloadAnnotation = false;
|
||||
Object[] arguments = holder.getArgs();
|
||||
EvaluationContext methodInvocationEvaluationContext = createMethodInvocationEvaluationContext(arguments);
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
if (GatewayMethodInboundMessageMapper.this.payloadExpression != null) {
|
||||
messageOrPayload = GatewayMethodInboundMessageMapper.this.payloadExpression.getValue(methodInvocationEvaluationContext);
|
||||
}
|
||||
for (int i = 0; i < GatewayMethodInboundMessageMapper.this.parameterList.size(); i++) {
|
||||
Object argumentValue = arguments[i];
|
||||
MethodParameter methodParameter = GatewayMethodInboundMessageMapper.this.parameterList.get(i);
|
||||
Annotation annotation = GatewayMethodInboundMessageMapper.this.findMappingAnnotation(methodParameter.getParameterAnnotations());
|
||||
if (annotation != null) {
|
||||
if (annotation.annotationType().equals(Payload.class)) {
|
||||
if (messageOrPayload != null) {
|
||||
GatewayMethodInboundMessageMapper.this.throwExceptionForMultipleMessageOrPayloadParameters(methodParameter);
|
||||
}
|
||||
String expression = ((Payload) annotation).value();
|
||||
if (!StringUtils.hasText(expression)) {
|
||||
messageOrPayload = argumentValue;
|
||||
}
|
||||
else {
|
||||
messageOrPayload = GatewayMethodInboundMessageMapper.this.evaluatePayloadExpression(expression, argumentValue);
|
||||
}
|
||||
foundPayloadAnnotation = true;
|
||||
}
|
||||
else if (annotation.annotationType().equals(Header.class)) {
|
||||
Header headerAnnotation = (Header) annotation;
|
||||
String headerName = GatewayMethodInboundMessageMapper.this.determineHeaderName(headerAnnotation, methodParameter);
|
||||
if (headerAnnotation.required() && argumentValue == null) {
|
||||
throw new IllegalArgumentException("Received null argument value for required header: '" + headerName + "'");
|
||||
}
|
||||
headers.put(headerName, argumentValue);
|
||||
}
|
||||
else if (annotation.annotationType().equals(Headers.class)) {
|
||||
if (argumentValue != null) {
|
||||
if (!(argumentValue instanceof Map)) {
|
||||
throw new IllegalArgumentException("@Headers annotation is only valid for Map-typed parameters");
|
||||
}
|
||||
for (Object key : ((Map<?, ?>) argumentValue).keySet()) {
|
||||
Assert.isInstanceOf(String.class, key, "Invalid header name [" + key +
|
||||
"], name type must be String.");
|
||||
Object value = ((Map<?, ?>) argumentValue).get(key);
|
||||
headers.put((String) key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (messageOrPayload == null) {
|
||||
messageOrPayload = argumentValue;
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(methodParameter.getParameterType())) {
|
||||
if (messageOrPayload instanceof Map && !foundPayloadAnnotation) {
|
||||
if (payloadExpression == null){
|
||||
throw new MessagingException("Ambiguous method parameters; found more than one " +
|
||||
"Map-typed parameter and neither one contains a @Payload annotation");
|
||||
}
|
||||
}
|
||||
GatewayMethodInboundMessageMapper.this.copyHeaders((Map<?, ?>) argumentValue, headers);
|
||||
}
|
||||
else if (GatewayMethodInboundMessageMapper.this.payloadExpression == null) {
|
||||
GatewayMethodInboundMessageMapper.this.throwExceptionForMultipleMessageOrPayloadParameters(methodParameter);
|
||||
}
|
||||
}
|
||||
Assert.isTrue(messageOrPayload != null, "unable to determine a Message or payload parameter on method [" + method + "]");
|
||||
MessageBuilder<?> builder = (messageOrPayload instanceof Message)
|
||||
? MessageBuilder.fromMessage((Message<?>) messageOrPayload)
|
||||
: MessageBuilder.withPayload(messageOrPayload);
|
||||
builder.copyHeadersIfAbsent(headers);
|
||||
// Explicit headers in XML override any @Header annotations...
|
||||
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.headerExpressions)) {
|
||||
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
|
||||
GatewayMethodInboundMessageMapper.this.headerExpressions);
|
||||
builder.copyHeaders(evaluatedHeaders);
|
||||
}
|
||||
// ...whereas global (default) headers do not...
|
||||
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.globalHeaderExpressions)) {
|
||||
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
|
||||
GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
|
||||
builder.copyHeadersIfAbsent(evaluatedHeaders);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint implements Trackab
|
||||
|
||||
private volatile GatewayMethodMetadata globalMethodMetadata;
|
||||
|
||||
private volatile MethodArgsMessageMapper argsMapper;
|
||||
|
||||
/**
|
||||
* Create a Factory whose service interface type can be configured by setter injection.
|
||||
* If none is set, it will fall back to the default service interface type,
|
||||
@@ -214,6 +216,15 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint implements Trackab
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom {@link MethodArgsMessageMapper} to map from a {@link MethodArgsHolder}
|
||||
* to a {@link Message}.
|
||||
* @param mapper the mapper.
|
||||
*/
|
||||
public final void setMapper(MethodArgsMessageMapper mapper) {
|
||||
this.argsMapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
@@ -395,7 +406,8 @@ public class GatewayProxyFactoryBean extends AbstractEndpoint implements Trackab
|
||||
}
|
||||
}
|
||||
GatewayMethodInboundMessageMapper messageMapper = new GatewayMethodInboundMessageMapper(method, headerExpressions,
|
||||
this.globalMethodMetadata != null ? this.globalMethodMetadata.getHeaderExpressions() : null);
|
||||
this.globalMethodMetadata != null ? this.globalMethodMetadata.getHeaderExpressions() : null,
|
||||
this.argsMapper);
|
||||
if (StringUtils.hasText(payloadExpression)) {
|
||||
messageMapper.setPayloadExpression(payloadExpression);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.gateway;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Simple wrapper class containing a {@link Method} and an object
|
||||
* array containing the arguments for an invocation of that method.
|
||||
* For example used by a {@link MethodArgsMessageMapper} with this generic
|
||||
* type to provide custom argument mapping when creating a message
|
||||
* in a {@code GatewayProxyFactoryBean}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public final class MethodArgsHolder {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
public MethodArgsHolder(Method method, Object[] args) {
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public final Method getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public final Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.gateway;
|
||||
|
||||
import org.springframework.integration.mapping.InboundMessageMapper;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are {@link InboundMessageMapper}s
|
||||
* that map a {@link MethodArgsHolder} to a {@link org.springframework.integration.Message}.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @since 3.0
|
||||
*
|
||||
*/
|
||||
public interface MethodArgsMessageMapper extends InboundMessageMapper<MethodArgsHolder> {
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.handler;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.integration.core.MessageProducer;
|
||||
@@ -114,7 +115,7 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
|
||||
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
protected final void onInit() {
|
||||
if (this.getBeanFactory() != null) {
|
||||
this.messagingTemplate.setBeanFactory(getBeanFactory());
|
||||
}
|
||||
@@ -125,6 +126,10 @@ public abstract class AbstractReplyProducingMessageHandler extends AbstractMessa
|
||||
}
|
||||
this.advisedRequestHandler = (RequestHandler) proxyFactory.getProxy(this.beanClassLoader);
|
||||
}
|
||||
this.doInit();
|
||||
}
|
||||
|
||||
protected void doInit() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -196,8 +196,7 @@ public class DelayHandler extends AbstractReplyProducingMessageHandler implement
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
if (this.messageStore == null) {
|
||||
this.messageStore = new SimpleMessageStore();
|
||||
}
|
||||
|
||||
@@ -55,8 +55,7 @@ public class ServiceActivatingHandler extends AbstractReplyProducingMessageHandl
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
if (processor instanceof AbstractMessageProcessor) {
|
||||
((AbstractMessageProcessor<?>) this.processor).setConversionService(this.getConversionService());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.
|
||||
@@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Base class for Message Splitter implementations that delegate to a
|
||||
* {@link MessageProcessor} instance.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 2.0
|
||||
*/
|
||||
@@ -43,8 +43,7 @@ abstract class AbstractMessageProcessingSplitter extends AbstractMessageSplitter
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
ConversionService conversionService = this.getConversionService();
|
||||
if (conversionService != null && this.messageProcessor instanceof AbstractMessageProcessor) {
|
||||
((AbstractMessageProcessor<?>) this.messageProcessor).setConversionService(conversionService);
|
||||
|
||||
@@ -194,8 +194,7 @@ public class ContentEnricher extends AbstractReplyProducingMessageHandler implem
|
||||
* the requestChannel is set.
|
||||
*/
|
||||
@Override
|
||||
public void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
if (this.replyChannel != null) {
|
||||
Assert.notNull(this.requestChannel, "If the replyChannel is set, then the requestChannel must not be null");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.
|
||||
@@ -54,8 +54,7 @@ public class MessageTransformingHandler extends AbstractReplyProducingMessageHan
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
if (this.getBeanFactory() != null && this.transformer instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) this.transformer).setBeanFactory(this.getBeanFactory());
|
||||
}
|
||||
|
||||
@@ -717,6 +717,22 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mapper" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An MethodArgsMessageMapper to map the method arguments to a Message. When this
|
||||
is provided, no payload-expressions or headers are allowed; the custom mapper is
|
||||
responsible for creating the message.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.integration.gateway.MethodArgsMessageMapper" />
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
@@ -23,16 +23,20 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
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.Message;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.DelayHandler;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
@@ -48,6 +52,7 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @since 1.0.3
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -100,7 +105,7 @@ public class DelayerParserTests {
|
||||
}
|
||||
|
||||
@Test //INT-2649
|
||||
public void transactionalSubElement() {
|
||||
public void transactionalSubElement() throws Exception {
|
||||
Object endpoint = context.getBean("delayerWithTransactional");
|
||||
DelayHandler delayHandler = TestUtils.getPropertyValue(endpoint, "handler", DelayHandler.class);
|
||||
List<?> adviceChain = TestUtils.getPropertyValue(delayHandler, "delayedAdviceChain", List.class);
|
||||
@@ -109,7 +114,8 @@ public class DelayerParserTests {
|
||||
assertTrue(advice instanceof TransactionInterceptor);
|
||||
TransactionAttributeSource transactionAttributeSource = ((TransactionInterceptor) advice).getTransactionAttributeSource();
|
||||
assertTrue(transactionAttributeSource instanceof MatchAlwaysTransactionAttributeSource);
|
||||
TransactionDefinition definition = transactionAttributeSource.getTransactionAttribute(null, null);
|
||||
Method method = MessageHandler.class.getMethod("handleMessage", Message.class);
|
||||
TransactionDefinition definition = transactionAttributeSource.getTransactionAttribute(method, null);
|
||||
assertEquals(TransactionDefinition.PROPAGATION_REQUIRED, definition.getPropagationBehavior());
|
||||
assertEquals(TransactionDefinition.ISOLATION_DEFAULT, definition.getIsolationLevel());
|
||||
assertEquals(TransactionDefinition.TIMEOUT_DEFAULT, definition.getTimeout());
|
||||
|
||||
@@ -39,12 +39,12 @@ import org.springframework.util.StopWatch;
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
*/
|
||||
|
||||
public class MessageIdGenerationTests {
|
||||
|
||||
@Test
|
||||
public void testCustomIdGenerationWithParentRegistrar() throws Exception{
|
||||
public void testCustomIdGenerationWithParentRegistrar() throws Exception {
|
||||
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext("MessageIdGenerationTests-context-withGenerator.xml", this.getClass());
|
||||
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(new String[]{"MessageIdGenerationTests-context.xml"}, this.getClass(), parent);
|
||||
|
||||
@@ -58,7 +58,7 @@ public class MessageIdGenerationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomIdGenerationWithParentChileIndependentCreation() throws Exception{
|
||||
public void testCustomIdGenerationWithParentChildIndependentCreation() throws Exception {
|
||||
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext("MessageIdGenerationTests-context-withGenerator.xml", this.getClass());
|
||||
GenericXmlApplicationContext child = new GenericXmlApplicationContext();
|
||||
child.load("classpath:/org/springframework/integration/core/MessageIdGenerationTests-context.xml");
|
||||
@@ -89,7 +89,7 @@ public class MessageIdGenerationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomIdGenerationWithChildRegistrar() throws Exception{
|
||||
public void testCustomIdGenerationWithChildRegistrar() throws Exception {
|
||||
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext("MessageIdGenerationTests-context.xml", this.getClass());
|
||||
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(new String[]{"MessageIdGenerationTests-context-withGenerator.xml"}, this.getClass(), parent);
|
||||
|
||||
@@ -98,13 +98,13 @@ public class MessageIdGenerationTests {
|
||||
MessageChannel inputChannel = child.getBean("input", MessageChannel.class);
|
||||
inputChannel.send(new GenericMessage<Integer>(0));
|
||||
verify(idGenerator, atLeastOnce()).generateId();
|
||||
parent.close();
|
||||
child.close();
|
||||
parent.close();
|
||||
this.assertDestroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomIdGenerationWithChildRegistrarClosed() throws Exception{
|
||||
public void testCustomIdGenerationWithChildRegistrarClosed() throws Exception {
|
||||
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext("MessageIdGenerationTests-context.xml", this.getClass());
|
||||
ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext(new String[]{"MessageIdGenerationTests-context-withGenerator.xml"}, this.getClass(), parent);
|
||||
|
||||
@@ -120,7 +120,7 @@ public class MessageIdGenerationTests {
|
||||
|
||||
// similar to the last test, but should not fail because child AC is closed before second child AC is started
|
||||
@Test
|
||||
public void testCustomIdGenerationWithParentChildIndependentCreationChildrenRegistrarsOneAtTheTime() throws Exception{
|
||||
public void testCustomIdGenerationWithParentChildIndependentCreationChildrenRegistrarsOneAtTheTime() throws Exception {
|
||||
ClassPathXmlApplicationContext parent = new ClassPathXmlApplicationContext("MessageIdGenerationTests-context.xml", this.getClass());
|
||||
|
||||
GenericXmlApplicationContext childA = new GenericXmlApplicationContext();
|
||||
@@ -142,7 +142,7 @@ public class MessageIdGenerationTests {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void performanceTest(){
|
||||
public void performanceTest() {
|
||||
int times = 1000000;
|
||||
StopWatch watch = new StopWatch();
|
||||
watch.start();
|
||||
|
||||
@@ -20,4 +20,10 @@
|
||||
<int:channel id="requestChannelBar"/>
|
||||
<int:channel id="requestChannelBaz"/>
|
||||
|
||||
<int:gateway id="customMappedGateway"
|
||||
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests.Baz"
|
||||
default-request-channel="requestChannelBaz" mapper="mapper"/>
|
||||
|
||||
<bean id="mapper" class="org.springframework.integration.gateway.GatewayInterfaceTests$BazMapper"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -238,6 +239,26 @@ public class GatewayInterfaceTests {
|
||||
new GatewayProxyFactoryBean(NotAnInterface.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithCustomMapper() {
|
||||
ApplicationContext ac = new ClassPathXmlApplicationContext("GatewayInterfaceTests-context.xml", this.getClass());
|
||||
DirectChannel channel = ac.getBean("requestChannelBaz", DirectChannel.class);
|
||||
final AtomicBoolean called = new AtomicBoolean();
|
||||
MessageHandler handler = new MessageHandler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
assertThat((String) message.getPayload(), equalTo("fizbuz"));
|
||||
called.set(true);
|
||||
}
|
||||
};
|
||||
channel.subscribe(handler);
|
||||
Baz baz = ac.getBean(Baz.class);
|
||||
baz.baz("hello");
|
||||
assertTrue(called.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public interface Foo {
|
||||
@Gateway(requestChannel="requestChannelFoo")
|
||||
@@ -256,4 +277,18 @@ public class GatewayInterfaceTests {
|
||||
public static class NotAnInterface {
|
||||
public void fail(String payload){}
|
||||
}
|
||||
|
||||
public interface Baz {
|
||||
|
||||
public void baz(String payload);
|
||||
}
|
||||
|
||||
public static class BazMapper implements MethodArgsMessageMapper {
|
||||
|
||||
@Override
|
||||
public Message<?> toMessage(MethodArgsHolder object) throws Exception {
|
||||
return MessageBuilder.withPayload("fizbuz").build();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.annotation.Headers;
|
||||
import org.springframework.integration.annotation.Payload;
|
||||
import org.springframework.integration.mapping.MessageMappingException;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
|
||||
/**
|
||||
@@ -78,7 +79,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
assertEquals("bar", message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = MessageMappingException.class)
|
||||
public void toMessageWithPayloadAndRequiredHeaderButNullValue() throws Exception {
|
||||
Method method = TestService.class.getMethod(
|
||||
"sendPayloadAndHeader", String.class, String.class);
|
||||
@@ -134,7 +135,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
assertEquals("test", message.getPayload());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = MessageMappingException.class)
|
||||
public void toMessageWithPayloadAndHeadersMapWithNonStringKey() throws Exception {
|
||||
Method method = TestService.class.getMethod(
|
||||
"sendPayloadAndHeadersMap", String.class, Map.class);
|
||||
@@ -166,7 +167,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
assertEquals("bar", message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = MessageMappingException.class)
|
||||
public void toMessageWithMessageParameterAndRequiredHeaderButNullValue() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessageAndHeader", Message.class, String.class);
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
@@ -197,7 +198,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
assertNull(message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = MessageMappingException.class)
|
||||
public void noArgs() throws Exception {
|
||||
Method method = TestService.class.getMethod("noArgs", new Class<?>[] {});
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
@@ -205,7 +206,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
mapper.toMessage(new Object[] {});
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test(expected = MessageMappingException.class)
|
||||
public void onlyHeaders() throws Exception {
|
||||
Method method = TestService.class.getMethod("onlyHeaders", String.class, String.class);
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ServiceActivatorDefaultFrameworkMethodTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotOptimizedReplyingMessageHandler() {
|
||||
public void testOptimizedReplyingMessageHandler() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.optimizedRefReplyingHandlerTestInputChannel.send(message);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -24,18 +24,17 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.integration.router.AbstractMappingMessageRouter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -43,10 +42,10 @@ public class RouterWithMappingTests {
|
||||
|
||||
@Autowired
|
||||
private MessageChannel expressionRouter;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("spelRouter")
|
||||
private ConsumerEndpointFactoryBean spelRouter;
|
||||
@Qualifier("spelRouter.handler")
|
||||
private AbstractMappingMessageRouter spelRouterHandler;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel pojoRouter;
|
||||
@@ -87,8 +86,7 @@ public class RouterWithMappingTests {
|
||||
assertNull(fooChannelForExpression.receive(0));
|
||||
assertNull(barChannelForExpression.receive(0));
|
||||
// validate dynamics
|
||||
AbstractMappingMessageRouter router = (AbstractMappingMessageRouter) TestUtils.getPropertyValue(spelRouter, "handler");
|
||||
router.setChannelMapping("baz", "fooChannelForExpression");
|
||||
spelRouterHandler.setChannelMapping("baz", "fooChannelForExpression");
|
||||
expressionRouter.send(message3);
|
||||
assertNull(defaultChannelForExpression.receive(10));
|
||||
assertNotNull(fooChannelForExpression.receive(10));
|
||||
|
||||
@@ -218,9 +218,7 @@ public class FileWritingMessageHandler extends AbstractReplyProducingMessageHand
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public abstract class AbstractRemoteFileInboundChannelAdapterParser extends Abst
|
||||
// configure the InboundFileSynchronizer properties
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(synchronizerBuilder, element, "remote-directory");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(synchronizerBuilder, element, "delete-remote-files");
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(synchronizerBuilder, element, "preserve-timestamp");
|
||||
|
||||
String remoteFileSeparator = element.getAttribute("remote-file-separator");
|
||||
synchronizerBuilder.addPropertyValue("remoteFileSeparator", remoteFileSeparator);
|
||||
|
||||
@@ -286,8 +286,7 @@ public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReply
|
||||
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
Assert.notNull(this.command, "command must not be null");
|
||||
if (Command.RM.equals(this.command) ||
|
||||
Command.GET.equals(this.command)) {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.integration.file.remote.synchronizer;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -50,6 +49,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileSynchronizer,
|
||||
@@ -89,6 +89,12 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
*/
|
||||
private volatile boolean deleteRemoteFiles;
|
||||
|
||||
/**
|
||||
* Should we <em>transfer</em> the remote file <b>timestamp</b>
|
||||
* to the local file? By default this is false.
|
||||
*/
|
||||
private volatile boolean preserveTimestamp;
|
||||
|
||||
/**
|
||||
* Create a synchronizer with the {@link SessionFactory} used to acquire {@link Session} instances.
|
||||
*/
|
||||
@@ -127,6 +133,10 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
this.deleteRemoteFiles = deleteRemoteFiles;
|
||||
}
|
||||
|
||||
public void setPreserveTimestamp(boolean preserveTimestamp) {
|
||||
this.preserveTimestamp = preserveTimestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntegrationEvaluationContext(EvaluationContext evaluationContext) {
|
||||
this.evaluationContext = evaluationContext;
|
||||
@@ -149,7 +159,7 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
Session<F> session = null;
|
||||
try {
|
||||
session = this.sessionFactory.getSession();
|
||||
Assert.state(session != null, "failed to acquire a Session");
|
||||
Assert.notNull(session, "failed to acquire a Session");
|
||||
F[] files = session.list(this.remoteDirectory);
|
||||
if (!ObjectUtils.isEmpty(files)) {
|
||||
Collection<F> filteredFiles = this.filterFiles(files);
|
||||
@@ -192,7 +202,6 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
if (!localFile.exists()) {
|
||||
String tempFileName = localFile.getAbsolutePath() + this.temporaryFileSuffix;
|
||||
File tempFile = new File(tempFileName);
|
||||
InputStream inputStream = null;
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
|
||||
try {
|
||||
session.read(remoteFilePath, fileOutputStream);
|
||||
@@ -206,13 +215,6 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
catch (Exception ignored1) {
|
||||
}
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
}
|
||||
@@ -228,6 +230,9 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.preserveTimestamp) {
|
||||
localFile.setLastModified(getModified(remoteFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,4 +247,6 @@ public abstract class AbstractInboundFileSynchronizer<F> implements InboundFileS
|
||||
|
||||
protected abstract String getFilename(F file);
|
||||
|
||||
protected abstract long getModified(F file);
|
||||
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
(sessionFactory, "get", "payload");
|
||||
gw.setFilter(new TestPatternFilter(""));
|
||||
try {
|
||||
gw.onInit();
|
||||
gw.afterPropertiesSet();
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
@@ -93,7 +93,7 @@ public class RemoteFileOutboundGatewayTests {
|
||||
(sessionFactory, "rm", "payload");
|
||||
gw.setFilter(new TestPatternFilter(""));
|
||||
try {
|
||||
gw.onInit();
|
||||
gw.afterPropertiesSet();
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
|
||||
1
spring-integration-ftp/.gitignore
vendored
1
spring-integration-ftp/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
local-test-dir/*.test
|
||||
remote-target-dir/*test
|
||||
test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.
|
||||
@@ -28,6 +28,7 @@ import org.springframework.integration.file.remote.synchronizer.AbstractInboundF
|
||||
* @author Iwein Fuld
|
||||
* @author Josh Long
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FtpInboundFileSynchronizer extends AbstractInboundFileSynchronizer<FTPFile> {
|
||||
@@ -50,4 +51,9 @@ public class FtpInboundFileSynchronizer extends AbstractInboundFileSynchronizer<
|
||||
return (file != null ? file.getName() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getModified(FTPFile file) {
|
||||
return file.getTimestamp().getTimeInMillis();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="delete-remote-files" type="xsd:string">
|
||||
<xsd:attribute name="delete-remote-files" type="xsd:string" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether to delete the remote source
|
||||
@@ -245,6 +245,16 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="preserve-timestamp" type="xsd:string" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether to preserve the modified timestamp from the remote source
|
||||
file on the local file after copying.
|
||||
By default, the remote timestamp will NOT be
|
||||
preserved.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ftp="http://www.springframework.org/schema/integration/ftp"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xmlns:ftps="http://www.springframework.org/schema/integration/ftps"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/integration/ftps http://www.springframework.org/schema/integration/ftp/spring-integration-ftps.xsd
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd">
|
||||
|
||||
<bean id="ftpSessionFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
|
||||
<property name="host" value="localhost"/>
|
||||
@@ -19,10 +16,10 @@
|
||||
<property name="fileType" value="2"/>
|
||||
</bean>
|
||||
|
||||
<ftp:inbound-channel-adapter id="adapterFtpDontAutoCreate"
|
||||
<ftp:inbound-channel-adapter id="adapterFtpDontAutoCreate"
|
||||
channel="ftpIn"
|
||||
session-factory="ftpSessionFactory"
|
||||
filter="filter"
|
||||
filter="filter"
|
||||
local-directory="file:target/bar"
|
||||
remote-directory="foo/bar"
|
||||
auto-create-local-directory="false"
|
||||
@@ -31,9 +28,9 @@
|
||||
</ftp:inbound-channel-adapter>
|
||||
|
||||
<bean id="filter" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.integration.file.entries.EntryListFilter"/>
|
||||
<constructor-arg value="org.springframework.integration.file.filters.FileListFilter"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<int:channel id="ftpIn">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
@@ -15,19 +15,28 @@
|
||||
*/
|
||||
package org.springframework.integration.ftp;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.MessagingException;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*
|
||||
*/
|
||||
public class FtpParserInboundTests {
|
||||
@@ -43,10 +52,22 @@ public class FtpParserInboundTests {
|
||||
assertTrue(new File("target/foo").exists());
|
||||
assertTrue(!new File("target/bar").exists());
|
||||
}
|
||||
@Test(expected=BeanCreationException.class)
|
||||
@Test
|
||||
public void testLocalFilesAutoCreationFalse() throws Exception{
|
||||
assertTrue(!new File("target/bar").exists());
|
||||
new ClassPathXmlApplicationContext("FtpParserInboundTests-fail-context.xml", this.getClass());
|
||||
try {
|
||||
new ClassPathXmlApplicationContext("FtpParserInboundTests-fail-context.xml", this.getClass());
|
||||
fail("BeansException expected.");
|
||||
}
|
||||
catch (BeansException e) {
|
||||
Throwable cause = e.getCause();
|
||||
assertThat(cause, Matchers.instanceOf(BeanCreationException.class));
|
||||
cause = cause.getCause();
|
||||
assertThat(cause, Matchers.instanceOf(MessagingException.class));
|
||||
cause = cause.getCause();
|
||||
assertThat(cause, Matchers.instanceOf(FileNotFoundException.class));
|
||||
assertEquals("bar", cause.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -7,20 +7,21 @@
|
||||
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd">
|
||||
|
||||
<bean id="ftpSessionFactory"
|
||||
<bean id="ftpSessionFactory"
|
||||
class="org.springframework.integration.ftp.config.FtpInboundChannelAdapterParserTests.TestSessionFactoryBean"/>
|
||||
|
||||
<bean id="csf" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
|
||||
<constructor-arg ref="ftpSessionFactory"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<int-ftp:inbound-channel-adapter id="ftpInbound"
|
||||
channel="ftpChannel"
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
auto-create-local-directory="true"
|
||||
auto-startup="false"
|
||||
delete-remote-files="true"
|
||||
preserve-timestamp="true"
|
||||
filename-pattern="*.txt"
|
||||
local-directory="."
|
||||
remote-file-separator=""
|
||||
@@ -39,7 +40,7 @@
|
||||
</bean>
|
||||
|
||||
<bean id="acceptAllFilter" class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
|
||||
|
||||
|
||||
<int:transaction-synchronization-factory id="syncFactory">
|
||||
<int:after-commit expression="'foo'" channel="successChannel"/>
|
||||
<int:after-rollback expression="'bar'" channel="failureChannel"/>
|
||||
@@ -48,13 +49,13 @@
|
||||
<int:channel id="successChannel" />
|
||||
|
||||
<int:channel id="failureChannel" />
|
||||
|
||||
|
||||
<bean id="comparator" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="java.util.Comparator"/>
|
||||
</bean>
|
||||
|
||||
<int-ftp:inbound-channel-adapter
|
||||
channel="ftpChannel"
|
||||
channel="ftpChannel"
|
||||
session-factory="ftpSessionFactory"
|
||||
charset="UTF-8"
|
||||
auto-create-local-directory="true"
|
||||
@@ -66,7 +67,7 @@
|
||||
</int-ftp:inbound-channel-adapter>
|
||||
|
||||
<int-ftp:inbound-channel-adapter id="simpleAdapterWithCachedSessions"
|
||||
channel="ftpChannel"
|
||||
channel="ftpChannel"
|
||||
session-factory="csf"
|
||||
local-directory="."
|
||||
remote-directory="foo/bar">
|
||||
@@ -76,7 +77,7 @@
|
||||
<int:channel id="ftpChannel">
|
||||
<int:queue/>
|
||||
</int:channel>
|
||||
|
||||
|
||||
<bean id="entryListFilter" class="org.mockito.Mockito" factory-method="mock">
|
||||
<constructor-arg value="org.springframework.integration.file.filters.FileListFilter"/>
|
||||
</bean>
|
||||
@@ -89,7 +90,7 @@
|
||||
</int-ftp:inbound-channel-adapter>
|
||||
|
||||
<int:bridge input-channel="autoChannel" output-channel="nullChannel" />
|
||||
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -77,6 +77,7 @@ public class FtpInboundChannelAdapterParserTests {
|
||||
FtpInboundFileSynchronizer fisync =
|
||||
(FtpInboundFileSynchronizer) TestUtils.getPropertyValue(inbound, "synchronizer");
|
||||
assertNotNull(TestUtils.getPropertyValue(fisync, "localFilenameGeneratorExpression"));
|
||||
assertTrue(TestUtils.getPropertyValue(fisync, "preserveTimestamp", Boolean.class));
|
||||
assertEquals(".foo", TestUtils.getPropertyValue(fisync, "temporaryFileSuffix", String.class));
|
||||
String remoteFileSeparator = (String) TestUtils.getPropertyValue(fisync, "remoteFileSeparator");
|
||||
assertNotNull(remoteFileSeparator);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -30,10 +31,12 @@ import static org.mockito.Mockito.when;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPFile;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
@@ -51,6 +54,7 @@ import org.springframework.messaging.Message;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FtpInboundRemoteFileSystemSynchronizerTests {
|
||||
@@ -80,6 +84,7 @@ public class FtpInboundRemoteFileSystemSynchronizerTests {
|
||||
ftpSessionFactory.setHost("foo.com");
|
||||
FtpInboundFileSynchronizer synchronizer = spy(new FtpInboundFileSynchronizer(ftpSessionFactory));
|
||||
synchronizer.setDeleteRemoteFiles(true);
|
||||
synchronizer.setPreserveTimestamp(true);
|
||||
synchronizer.setRemoteDirectory("remote-test-dir");
|
||||
synchronizer.setFilter(new FtpRegexPatternFileListFilter(".*\\.test$"));
|
||||
synchronizer.setIntegrationEvaluationContext(ExpressionUtils.createStandardEvaluationContext());
|
||||
@@ -98,9 +103,15 @@ public class FtpInboundRemoteFileSystemSynchronizerTests {
|
||||
Message<File> atestFile = ms.receive();
|
||||
assertNotNull(atestFile);
|
||||
assertEquals("A.TEST.a", atestFile.getPayload().getName());
|
||||
// The test remote files are created with the current timestamp + 1 day.
|
||||
assertThat(atestFile.getPayload().lastModified(), Matchers.greaterThan(System.currentTimeMillis()));
|
||||
|
||||
Message<File> btestFile = ms.receive();
|
||||
assertNotNull(btestFile);
|
||||
assertEquals("B.TEST.a", btestFile.getPayload().getName());
|
||||
// The test remote files are created with the current timestamp + 1 day.
|
||||
assertThat(atestFile.getPayload().lastModified(), Matchers.greaterThan(System.currentTimeMillis()));
|
||||
|
||||
Message<File> nothing = ms.receive();
|
||||
assertNull(nothing);
|
||||
|
||||
@@ -127,6 +138,9 @@ public class FtpInboundRemoteFileSystemSynchronizerTests {
|
||||
FTPFile file = new FTPFile();
|
||||
file.setName(fileName);
|
||||
file.setType(FTPFile.FILE_TYPE);
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, 1);
|
||||
file.setTimestamp(calendar);
|
||||
ftpFiles.add(file);
|
||||
when(ftpClient.retrieveFile(Mockito.eq("remote-test-dir/" + fileName) , Mockito.any(OutputStream.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
|
||||
package org.springframework.integration.groovy;
|
||||
|
||||
import groovy.lang.Binding;
|
||||
import groovy.lang.GString;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -29,6 +26,9 @@ import org.springframework.scripting.support.StaticScriptSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import groovy.lang.Binding;
|
||||
import groovy.lang.GString;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
@@ -109,11 +109,11 @@ public class GroovyCommandMessageProcessor extends AbstractScriptExecutingMessag
|
||||
customizerDecorator.setVariables(variables);
|
||||
}
|
||||
GroovyScriptFactory factory = new GroovyScriptFactory(this.getClass().getSimpleName(), customizerDecorator);
|
||||
if (getBeanClassLoader() != null) {
|
||||
factory.setBeanClassLoader(getBeanClassLoader());
|
||||
if (this.beanClassLoader != null) {
|
||||
factory.setBeanClassLoader(this.beanClassLoader);
|
||||
}
|
||||
if (getBeanFactory() != null) {
|
||||
factory.setBeanFactory(getBeanFactory());
|
||||
if (this.beanFactory != null) {
|
||||
factory.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
Object result = factory.getScriptedObject(scriptSource, null);
|
||||
return (result instanceof GString) ? result.toString() : result;
|
||||
|
||||
@@ -16,36 +16,52 @@
|
||||
|
||||
package org.springframework.integration.groovy;
|
||||
|
||||
import groovy.lang.Binding;
|
||||
import groovy.lang.GString;
|
||||
import groovy.lang.GroovyClassLoader;
|
||||
import groovy.lang.GroovyObject;
|
||||
import groovy.lang.MetaClass;
|
||||
import groovy.lang.Script;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.integration.scripting.AbstractScriptExecutingMessageProcessor;
|
||||
import org.springframework.integration.scripting.ScriptVariableGenerator;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.scripting.ScriptCompilationException;
|
||||
import org.springframework.scripting.ScriptSource;
|
||||
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
|
||||
import org.springframework.scripting.groovy.GroovyScriptFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* The {@link org.springframework.integration.handler.MessageProcessor} implementation
|
||||
* to evaluate Groovy scripts.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Stefan Reuter
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecutingMessageProcessor<Object> implements InitializingBean {
|
||||
public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecutingMessageProcessor<Object> {
|
||||
|
||||
private final GroovyScriptFactory scriptFactory;
|
||||
private final VariableBindingGroovyObjectCustomizerDecorator customizerDecorator =
|
||||
new VariableBindingGroovyObjectCustomizerDecorator();
|
||||
|
||||
private final VariableBindingGroovyObjectCustomizerDecorator
|
||||
customizerDecorator = new VariableBindingGroovyObjectCustomizerDecorator();
|
||||
private final Lock scriptLock = new ReentrantLock();
|
||||
|
||||
private volatile ScriptSource scriptSource;
|
||||
|
||||
private volatile GroovyClassLoader groovyClassLoader = new GroovyClassLoader(ClassUtils.getDefaultClassLoader());
|
||||
|
||||
private volatile Class<?> scriptClass;
|
||||
|
||||
/**
|
||||
* Create a processor for the given {@link ScriptSource} that will use a
|
||||
@@ -54,7 +70,6 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
|
||||
public GroovyScriptExecutingMessageProcessor(ScriptSource scriptSource) {
|
||||
super();
|
||||
this.scriptSource = scriptSource;
|
||||
this.scriptFactory = new GroovyScriptFactory(this.getClass().getSimpleName(), this.customizerDecorator);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,9 +79,21 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
|
||||
public GroovyScriptExecutingMessageProcessor(ScriptSource scriptSource, ScriptVariableGenerator scriptVariableGenerator) {
|
||||
super(scriptVariableGenerator);
|
||||
this.scriptSource = scriptSource;
|
||||
this.scriptFactory = new GroovyScriptFactory(this.getClass().getSimpleName(), this.customizerDecorator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
super.setBeanClassLoader(classLoader);
|
||||
this.groovyClassLoader = new GroovyClassLoader(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
super.setBeanFactory(beanFactory);
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
((ConfigurableListableBeanFactory) beanFactory).ignoreDependencyType(MetaClass.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a {@link GroovyObjectCustomizer} for this processor.
|
||||
@@ -83,22 +110,58 @@ public class GroovyScriptExecutingMessageProcessor extends AbstractScriptExecuti
|
||||
@Override
|
||||
protected Object executeScript(ScriptSource scriptSource, Map<String, Object> variables) throws Exception {
|
||||
Assert.notNull(scriptSource, "scriptSource must not be null");
|
||||
synchronized (this) {
|
||||
if (!CollectionUtils.isEmpty(variables)) {
|
||||
this.customizerDecorator.setVariables(variables);
|
||||
this.parseScriptIfNecessary(scriptSource);
|
||||
Object result = this.execute(variables);
|
||||
return (result instanceof GString) ? result.toString() : result;
|
||||
}
|
||||
|
||||
|
||||
private void parseScriptIfNecessary(ScriptSource scriptSource) throws Exception {
|
||||
if (this.scriptClass == null || scriptSource.isModified()) {
|
||||
this.scriptLock.lockInterruptibly();
|
||||
try {
|
||||
// synchronized double check
|
||||
if (this.scriptClass == null || scriptSource.isModified()) {
|
||||
this.scriptClass = this.groovyClassLoader.parseClass(
|
||||
scriptSource.getScriptAsString(), scriptSource.suggestedClassName());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.scriptLock.unlock();
|
||||
}
|
||||
Object result = this.scriptFactory.getScriptedObject(scriptSource, null);
|
||||
return (result instanceof GString) ? result.toString() : result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
if (getBeanClassLoader() != null) {
|
||||
this.scriptFactory.setBeanClassLoader(getBeanClassLoader());
|
||||
private Object execute(Map<String, Object> variables) throws ScriptCompilationException {
|
||||
try {
|
||||
GroovyObject goo = (GroovyObject) this.scriptClass.newInstance();
|
||||
|
||||
GroovyObjectCustomizer groovyObjectCustomizer = this.customizerDecorator;
|
||||
if (variables != null) {
|
||||
// Override empty Script.Binding with new one with 'variables'
|
||||
groovyObjectCustomizer = new BindingOverwriteGroovyObjectCustomizerDecorator(new Binding(variables));
|
||||
((VariableBindingGroovyObjectCustomizerDecorator) groovyObjectCustomizer).setCustomizer(this.customizerDecorator);
|
||||
}
|
||||
|
||||
if (goo instanceof Script) {
|
||||
// Allow metaclass and other customization.
|
||||
groovyObjectCustomizer.customize(goo);
|
||||
// A Groovy script, probably creating an instance: let's execute it.
|
||||
return ((Script) goo).run();
|
||||
}
|
||||
else {
|
||||
// An instance of the scripted class: let's return it as-is.
|
||||
return goo;
|
||||
}
|
||||
}
|
||||
if (getBeanFactory() != null) {
|
||||
this.scriptFactory.setBeanFactory(getBeanFactory());
|
||||
catch (InstantiationException ex) {
|
||||
throw new ScriptCompilationException(
|
||||
this.scriptSource, "Could not instantiate Groovy script class: " + this.scriptClass.getName(), ex);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new ScriptCompilationException(
|
||||
this.scriptSource, "Could not access Groovy script constructor: " + this.scriptClass.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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.groovy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import groovy.lang.GroovyObject;
|
||||
import groovy.lang.Script;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.LogManager;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.scripting.groovy.GroovyObjectCustomizer;
|
||||
import org.springframework.scripting.groovy.GroovyScriptFactory;
|
||||
import org.springframework.scripting.support.ResourceScriptSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class GroovyExpressionTests {
|
||||
|
||||
private static Log logger = LogFactory.getLog(GroovyExpressionTests.class);
|
||||
|
||||
@Before
|
||||
public void setLogLevel() {
|
||||
LogManager.getLogger(getClass()).setLevel(Level.DEBUG);
|
||||
}
|
||||
|
||||
@After
|
||||
public void resetLogLevel() {
|
||||
LogManager.getLogger(getClass()).setLevel(Level.INFO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptFactoryCustomizer() throws Exception {
|
||||
Customizer customizer = new Customizer(Collections.singletonMap("name", (Object) "foo"));
|
||||
GroovyScriptFactory factory = new GroovyScriptFactory("Groovy Script", customizer);
|
||||
ResourceScriptSource scriptSource = new ResourceScriptSource(new NamedByteArrayResource("\"name=${name}\"".getBytes(), "InlineScript"));
|
||||
Object scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
assertEquals("name=foo", scriptedObject.toString());
|
||||
customizer.setMap(Collections.singletonMap("name", (Object) "bar"));
|
||||
scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
assertEquals("name=bar", scriptedObject.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptFactoryCustomizerThreadSafety() throws Exception {
|
||||
final Customizer customizer = new Customizer(Collections.singletonMap("name", (Object) "foo"));
|
||||
final GroovyScriptFactory factory = new GroovyScriptFactory("Groovy Script", customizer);
|
||||
final ResourceScriptSource scriptSource = new ResourceScriptSource(new NamedByteArrayResource(
|
||||
"\"name=${name}\"".getBytes(), "InlineScript"));
|
||||
Object scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
assertEquals("name=foo", scriptedObject.toString());
|
||||
CompletionService<String> completionService = new ExecutorCompletionService<String>(Executors.newFixedThreadPool(10));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final String name = "bar" + i;
|
||||
completionService.submit(new Callable<String>() {
|
||||
public String call() throws Exception {
|
||||
Object scriptedObject;
|
||||
synchronized (customizer) {
|
||||
customizer.setMap(Collections.singletonMap("name", (Object) name));
|
||||
scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
}
|
||||
String result = scriptedObject.toString();
|
||||
logger.debug("Result=" + result + " with name=" + name);
|
||||
if (!("name=" + name).equals(result)) {
|
||||
throw new IllegalStateException("Wrong value (" + result + ") for: " + name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
});
|
||||
}
|
||||
Set<String> set = new HashSet<String>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
set.add(completionService.take().get());
|
||||
}
|
||||
assertEquals(100, set.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptFactoryCustomizerStatic() throws Exception {
|
||||
final Customizer customizer = new Customizer(Collections.singletonMap("name", (Object) "foo"));
|
||||
final GroovyScriptFactory factory = new GroovyScriptFactory("Groovy Script", customizer);
|
||||
final ResourceScriptSource scriptSource = new ResourceScriptSource(new NamedByteArrayResource(
|
||||
"\"name=${name}\"".getBytes(), "InlineScript"));
|
||||
Object scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
assertEquals("name=foo", scriptedObject.toString());
|
||||
CompletionService<String> completionService = new ExecutorCompletionService<String>(Executors.newFixedThreadPool(10));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final String name = "bar" + i;
|
||||
completionService.submit(new Callable<String>() {
|
||||
public String call() throws Exception {
|
||||
Object scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
String result = scriptedObject.toString();
|
||||
logger.debug("Result=" + result + " with name=" + name);
|
||||
if (!("name=foo").equals(result)) {
|
||||
throw new IllegalStateException("Wrong value (" + result + ") for: " + name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
});
|
||||
}
|
||||
Set<String> set = new HashSet<String>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
set.add(completionService.take().get());
|
||||
}
|
||||
assertEquals(100, set.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptFactoryCustomizerThreadSafetyWithNewScript() throws Exception {
|
||||
final Customizer customizer = new Customizer(Collections.singletonMap("name", (Object) "foo"));
|
||||
final GroovyScriptFactory factory = new GroovyScriptFactory("Groovy Script", customizer);
|
||||
CompletionService<String> completionService = new ExecutorCompletionService<String>(Executors.newFixedThreadPool(5));
|
||||
for (int i = 0; i < 100; i++) {
|
||||
final String name = "Bar" + i;
|
||||
completionService.submit(new Callable<String>() {
|
||||
public String call() throws Exception {
|
||||
Object scriptedObject;
|
||||
synchronized (customizer) {
|
||||
customizer.setMap(Collections.singletonMap("name", (Object) name));
|
||||
ResourceScriptSource scriptSource = new ResourceScriptSource(new NamedByteArrayResource(
|
||||
"\"name=${name}\"".getBytes(), "InlineScript" + name));
|
||||
scriptedObject = factory.getScriptedObject(scriptSource, null);
|
||||
}
|
||||
String result = scriptedObject.toString();
|
||||
logger.debug("Result=" + result + " with name=" + name);
|
||||
if (!("name=" + name).equals(result)) {
|
||||
throw new IllegalStateException("Wrong value (" + result + ") for: " + name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
});
|
||||
}
|
||||
Set<String> set = new HashSet<String>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
set.add(completionService.take().get());
|
||||
}
|
||||
assertEquals(100, set.size());
|
||||
}
|
||||
|
||||
|
||||
private static class Customizer implements GroovyObjectCustomizer {
|
||||
|
||||
private Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
public Customizer(Map<String, Object> map) {
|
||||
super();
|
||||
this.map.putAll(map);
|
||||
}
|
||||
|
||||
public void customize(GroovyObject goo) {
|
||||
Assert.state(goo instanceof Script, "Expected a Script");
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
((Script) goo).getBinding().setVariable(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void setMap(Map<String, Object> map) {
|
||||
this.map.clear();
|
||||
this.map.putAll(map);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NamedByteArrayResource extends ByteArrayResource {
|
||||
|
||||
private final String fileName;
|
||||
|
||||
public NamedByteArrayResource(byte[] bytes, String fileName) {
|
||||
super(bytes);
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() throws IllegalStateException {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,26 +18,35 @@ package org.springframework.integration.groovy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.scripting.RefreshableResourceScriptSource;
|
||||
import org.springframework.integration.scripting.ScriptVariableGenerator;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.scripting.ScriptSource;
|
||||
import org.springframework.scripting.support.ResourceScriptSource;
|
||||
import org.springframework.scripting.support.StaticScriptSource;
|
||||
import org.springframework.test.annotation.Repeat;
|
||||
|
||||
import groovy.lang.Script;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Dave Syer
|
||||
@@ -176,6 +185,43 @@ public class GroovyScriptExecutingMessageProcessorTests {
|
||||
assertEquals("payload is 'hello'", result.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3166GroovyScriptExecutingMessageProcessorPerformance() throws Exception {
|
||||
final Message<?> message = new GenericMessage<Object>("test");
|
||||
|
||||
final AtomicInteger var1 = new AtomicInteger();
|
||||
final AtomicInteger var2 = new AtomicInteger();
|
||||
|
||||
String script =
|
||||
"var1.incrementAndGet(); Thread.sleep(100); var2.set(Math.max(var1.get(), var2.get())); var1.decrementAndGet()";
|
||||
|
||||
ScriptSource scriptSource = new StaticScriptSource(script, Script.class.getName());
|
||||
final MessageProcessor<Object> processor =
|
||||
new GroovyScriptExecutingMessageProcessor(scriptSource, new ScriptVariableGenerator() {
|
||||
@Override
|
||||
public Map<String, Object> generateScriptVariables(Message<?> message) {
|
||||
Map<String, Object> variables = new HashMap<String, Object>(2);
|
||||
variables.put("var1", var1);
|
||||
variables.put("var2", var2);
|
||||
return variables;
|
||||
}
|
||||
});
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
processor.processMessage(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
executor.shutdown();
|
||||
assertTrue(executor.awaitTermination(10, TimeUnit.SECONDS));
|
||||
|
||||
assertTrue(var2.get() > 1);
|
||||
|
||||
}
|
||||
|
||||
private static class TestResource extends AbstractResource {
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.integration.groovy.config;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import groovy.lang.GroovyObject;
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.junit.FixMethodOrder;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.MethodSorters;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanCreationNotAllowedException;
|
||||
import org.springframework.beans.factory.BeanIsAbstractException;
|
||||
@@ -115,6 +116,8 @@ public class GroovyControlBusTests {
|
||||
Message<?> message = MessageBuilder.withPayload("def result = requestScopedService.convert('testString')").build();
|
||||
this.input.send(message);
|
||||
assertEquals("cat", output.receive(0).getPayload());
|
||||
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test //INT-2567
|
||||
@@ -180,7 +183,7 @@ public class GroovyControlBusTests {
|
||||
|
||||
private static class MockRequestAttributes implements RequestAttributes {
|
||||
|
||||
private Map<String, Object> fakeRequest = new HashMap<String, Object>();
|
||||
private final Map<String, Object> fakeRequest = new HashMap<String, Object>();
|
||||
|
||||
public Object getAttribute(String name, int scope) {
|
||||
return fakeRequest.get(name);
|
||||
|
||||
@@ -97,6 +97,7 @@ public class GroovyFilterTests {
|
||||
assertTrue(this.groovyFilterMessageHandler instanceof MessageFilter);
|
||||
MessageSelector selector = TestUtils.getPropertyValue(this.groovyFilterMessageHandler, "selector",
|
||||
MethodInvokingSelector.class);
|
||||
@SuppressWarnings("rawtypes")
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(selector, "messageProcessor", MessageProcessor.class);
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
assertTrue(messageProcessor instanceof GroovyScriptExecutingMessageProcessor);
|
||||
|
||||
@@ -107,6 +107,7 @@ public class GroovyRouterTests {
|
||||
@Test
|
||||
public void testInt2433VerifyRiddingOfMessageProcessorsWrapping() {
|
||||
assertTrue(this.groovyRouterMessageHandler instanceof MethodInvokingRouter);
|
||||
@SuppressWarnings("rawtypes")
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(this.groovyRouterMessageHandler,
|
||||
"messageProcessor", MessageProcessor.class);
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
|
||||
@@ -90,7 +90,6 @@ public class GroovyServiceActivatorTests {
|
||||
String value1 = (String) replyChannel.receive(0).getPayload();
|
||||
String value2 = (String) replyChannel.receive(0).getPayload();
|
||||
String value3 = (String) replyChannel.receive(0).getPayload();
|
||||
System.out.println(value1 + "\n" + value2 + "\n" + value3);
|
||||
assertTrue(value1.startsWith("groovy-test-1-foo - bar"));
|
||||
assertTrue(value2.startsWith("groovy-test-2-foo - bar"));
|
||||
assertTrue(value3.startsWith("groovy-test-3-foo - bar"));
|
||||
|
||||
@@ -82,10 +82,11 @@ public class GroovySplitterTests {
|
||||
|
||||
@Test
|
||||
public void testInt2433VerifyRiddingOfMessageProcessorsWrapping() {
|
||||
assertTrue(this.groovySplitterMessageHandler instanceof MethodInvokingSplitter);
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(this.groovySplitterMessageHandler,
|
||||
assertTrue(this.groovySplitterMessageHandler instanceof MethodInvokingSplitter);
|
||||
@SuppressWarnings("rawtypes")
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(this.groovySplitterMessageHandler,
|
||||
"messageProcessor", MessageProcessor.class);
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
assertTrue(messageProcessor instanceof GroovyScriptExecutingMessageProcessor);
|
||||
}
|
||||
|
||||
|
||||
@@ -88,11 +88,12 @@ public class GroovyTransformerTests {
|
||||
|
||||
@Test
|
||||
public void testInt2433VerifyRiddingOfMessageProcessorsWrapping() {
|
||||
assertTrue(this.groovyTransformerMessageHandler instanceof MessageTransformingHandler);
|
||||
Transformer transformer = TestUtils.getPropertyValue(this.groovyTransformerMessageHandler, "transformer", Transformer.class);
|
||||
assertTrue(transformer instanceof AbstractMessageProcessingTransformer);
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(transformer, "messageProcessor", MessageProcessor.class);
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
assertTrue(this.groovyTransformerMessageHandler instanceof MessageTransformingHandler);
|
||||
Transformer transformer = TestUtils.getPropertyValue(this.groovyTransformerMessageHandler, "transformer", Transformer.class);
|
||||
assertTrue(transformer instanceof AbstractMessageProcessingTransformer);
|
||||
@SuppressWarnings("rawtypes")
|
||||
MessageProcessor messageProcessor = TestUtils.getPropertyValue(transformer, "messageProcessor", MessageProcessor.class);
|
||||
//before it was MethodInvokingMessageProcessor
|
||||
assertTrue(messageProcessor instanceof GroovyScriptExecutingMessageProcessor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,12 @@ package org.springframework.integration.http.inbound;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -65,6 +67,7 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.MultipartResolver;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
@@ -210,8 +213,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
/**
|
||||
* Specifies a SpEL expression to evaluate in order to generate the Message payload.
|
||||
* The EvaluationContext will be populated with an HttpEntity instance as the root object,
|
||||
* and it may contain one or both of the <code>#pathVariables</code> and
|
||||
* <code>#queryParameters</code> variables if present. Those variables' values are Maps.
|
||||
* and it may contain variables:
|
||||
* <ul>
|
||||
* <li><code>#pathVariables</code></li>
|
||||
* <li><code>#requestParams</code></li>
|
||||
* <li><code>#requestAttributes</code></li>
|
||||
* <li><code>#requestHeaders</code></li>
|
||||
* <li><code>#matrixVariables</code></li>
|
||||
* <li><code>#cookies</code>
|
||||
* </ul>
|
||||
*/
|
||||
public void setPayloadExpression(Expression payloadExpression) {
|
||||
this.payloadExpression = payloadExpression;
|
||||
@@ -221,8 +231,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
* Specifies a Map of SpEL expressions to evaluate in order to generate the Message headers.
|
||||
* The keys in the map will be used as the header names. When evaluating the expression,
|
||||
* the EvaluationContext will be populated with an HttpEntity instance as the root object,
|
||||
* and it may contain one or both of the <code>#pathVariables</code> and
|
||||
* <code>#queryParameters</code> variables if present. Those variables' values are Maps.
|
||||
* and it may contain variables:
|
||||
* <ul>
|
||||
* <li><code>#pathVariables</code></li>
|
||||
* <li><code>#requestParams</code></li>
|
||||
* <li><code>#requestAttributes</code></li>
|
||||
* <li><code>#requestHeaders</code></li>
|
||||
* <li><code>#matrixVariables</code></li>
|
||||
* <li><code>#cookies</code>
|
||||
* </ul>
|
||||
*/
|
||||
public void setHeaderExpressions(Map<String, Expression> headerExpressions) {
|
||||
this.headerExpressions = headerExpressions;
|
||||
@@ -379,9 +396,22 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
StandardEvaluationContext evaluationContext = this.createEvaluationContext();
|
||||
evaluationContext.setRootObject(httpEntity);
|
||||
|
||||
evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes());
|
||||
|
||||
MultiValueMap<String, String> requestParams = this.convertParameterMap(servletRequest.getParameterMap());
|
||||
evaluationContext.setVariable("requestParams", requestParams);
|
||||
|
||||
evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders());
|
||||
|
||||
Cookie[] requestCookies = servletRequest.getCookies();
|
||||
if (!ObjectUtils.isEmpty(requestCookies)) {
|
||||
Map<String, Cookie> cookies = new HashMap<String, Cookie>(requestCookies.length);
|
||||
for (Cookie requestCookie : requestCookies) {
|
||||
cookies.put(requestCookie.getName(), requestCookie);
|
||||
}
|
||||
evaluationContext.setVariable("cookies", cookies);
|
||||
}
|
||||
|
||||
Map<String, String> pathVariables =
|
||||
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
|
||||
@@ -392,6 +422,17 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
evaluationContext.setVariable("pathVariables", pathVariables);
|
||||
}
|
||||
|
||||
//TODO change it to HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE after upgrade to Spring 4.0
|
||||
Map<String, MultiValueMap<String, String>> matrixVariables =
|
||||
(Map<String, MultiValueMap<String, String>>) servletRequest.getAttribute(HandlerMapping.class.getName() + ".matrixVariables");
|
||||
|
||||
if (!CollectionUtils.isEmpty(matrixVariables)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapped matrix variables: " + matrixVariables);
|
||||
}
|
||||
evaluationContext.setVariable("matrixVariables", matrixVariables);
|
||||
}
|
||||
|
||||
Map<String, Object> headers = this.headerMapper.toHeaders(request.getHeaders());
|
||||
Object payload = null;
|
||||
if (this.payloadExpression != null) {
|
||||
|
||||
@@ -291,8 +291,7 @@ public class HttpRequestExecutingMessageHandler extends AbstractReplyProducingMe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
|
||||
ConversionService conversionService = this.getConversionService();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* 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.http;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
/**
|
||||
* @author Artem Bilan
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractHttpInboundTests {
|
||||
|
||||
@Before
|
||||
public void setupHttpInbound() {
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownHttpInbound() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -137,6 +137,7 @@ public class HttpProxyScenarioTests {
|
||||
assertEquals(ifModifiedSince, headers.get("If-Modified-Since"));
|
||||
assertEquals(ifUnmodifiedSince, headers.get("If-Unmodified-Since"));
|
||||
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ package org.springframework.integration.http.config;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
@@ -36,11 +36,9 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -48,6 +46,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.integration.history.MessageHistory;
|
||||
import org.springframework.integration.http.AbstractHttpInboundTests;
|
||||
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
|
||||
import org.springframework.integration.http.inbound.HttpRequestHandlingController;
|
||||
import org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway;
|
||||
@@ -74,7 +73,7 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class HttpInboundChannelAdapterParserTests {
|
||||
public class HttpInboundChannelAdapterParserTests extends AbstractHttpInboundTests {
|
||||
|
||||
@Autowired
|
||||
private PollableChannel requests;
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.http.AbstractHttpInboundTests;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -54,7 +55,7 @@ import org.springframework.web.servlet.View;
|
||||
* @author Biju Kunjummen
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpRequestHandlingControllerTests {
|
||||
public class HttpRequestHandlingControllerTests extends AbstractHttpInboundTests {
|
||||
|
||||
@Test
|
||||
public void sendOnly() throws Exception {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.integration.http.inbound;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -43,6 +43,7 @@ import org.springframework.messaging.Message;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.http.AbstractHttpInboundTests;
|
||||
import org.springframework.integration.http.converter.SerializingHttpMessageConverter;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -58,7 +59,7 @@ import org.springframework.util.SerializationUtils;
|
||||
* @author Biju Kunjummen
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpRequestHandlingMessagingGatewayTests {
|
||||
public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboundTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -27,11 +27,12 @@ import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.http.AbstractHttpInboundTests;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
@@ -45,7 +46,7 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||
* @author Artem Bilan
|
||||
* @author Biju Kunjummen
|
||||
*/
|
||||
public class HttpRequestHandlingMessagingGatewayWithPathMappingTests {
|
||||
public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends AbstractHttpInboundTests {
|
||||
|
||||
private static ExpressionParser PARSER = new SpelExpressionParser();
|
||||
|
||||
|
||||
@@ -26,8 +26,16 @@
|
||||
request-channel="toLowerCaseChannel"
|
||||
payload-expression="#pathVariables.value">
|
||||
<int-http:request-mapping headers="toLowerCase"/>
|
||||
|
||||
<int-http:header name="requestAttributes" expression="#requestAttributes"/>
|
||||
<int-http:header name="requestParams" expression="#requestParams"/>
|
||||
<int-http:header name="requestHeaders" expression="#requestHeaders"/>
|
||||
<int-http:header name="matrixVariables" expression="#matrixVariables"/>
|
||||
<int-http:header name="cookies" expression="#cookies"/>
|
||||
</int-http:inbound-gateway>
|
||||
|
||||
<int:publish-subscribe-channel id="toLowerCaseChannel"/>
|
||||
|
||||
<int:transformer input-channel="toLowerCaseChannel" expression="payload.toLowerCase()"/>
|
||||
|
||||
<int-http:inbound-gateway path="#{TEST_PATH}"
|
||||
|
||||
@@ -17,22 +17,37 @@
|
||||
package org.springframework.integration.http.inbound;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.MessagingException;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.http.AbstractHttpInboundTests;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.servlet.HandlerAdapter;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
@@ -44,7 +59,7 @@ import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
|
||||
//INT-2312
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class Int2312RequestMappingIntegrationTests {
|
||||
public class Int2312RequestMappingIntegrationTests extends AbstractHttpInboundTests {
|
||||
|
||||
public static final String TEST_PATH = "/test/{value}";
|
||||
|
||||
@@ -53,6 +68,9 @@ public class Int2312RequestMappingIntegrationTests {
|
||||
@Autowired
|
||||
private HandlerMapping handlerMapping;
|
||||
|
||||
@Autowired
|
||||
private SubscribableChannel toLowerCaseChannel;
|
||||
|
||||
private HandlerAdapter handlerAdapter = new HttpRequestHandlerAdapter();
|
||||
|
||||
@Test
|
||||
@@ -77,26 +95,63 @@ public class Int2312RequestMappingIntegrationTests {
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
//INT-1362
|
||||
public void testURIVariablesAndHeaders() throws Exception {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setMethod("GET");
|
||||
String testRequest = "aBc";
|
||||
// TODO test it after upgrade to Spring 4.0
|
||||
// String testRequest = "aBc;q1=1;q2=2";
|
||||
String requestURI = "/test/" + testRequest;
|
||||
request.setRequestURI(requestURI);
|
||||
request.setContentType("text/plain");
|
||||
final Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("foo", "bar");
|
||||
request.setParameters(params);
|
||||
request.setContent("hello".getBytes());
|
||||
final Cookie cookie = new Cookie("foo", "bar");
|
||||
request.setCookies(cookie);
|
||||
request.addHeader("toLowerCase", true);
|
||||
|
||||
//See org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping#handleMatch
|
||||
Map<String, String> uriTemplateVariables =
|
||||
new AntPathMatcher().extractUriTemplateVariables(TEST_PATH, requestURI);
|
||||
request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
|
||||
//See org.springframework.web.servlet.FrameworkServlet#initContextHolders
|
||||
final RequestAttributes attributes = new ServletRequestAttributes(request);
|
||||
RequestContextHolder.setRequestAttributes(attributes);
|
||||
|
||||
this.toLowerCaseChannel.subscribe(new MessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(Message<?> message) throws MessagingException {
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
|
||||
assertEquals(attributes, headers.get("requestAttributes"));
|
||||
|
||||
Object requestParams = headers.get("requestParams");
|
||||
assertNotNull(requestParams);
|
||||
assertEquals(params, ((MultiValueMap<String, String>) requestParams).toSingleValueMap());
|
||||
|
||||
// TODO test it after upgrade to Spring 4.0
|
||||
// assertEquals(matrixVariables, headers.get("matrixVariables"));
|
||||
|
||||
Object requestHeaders = headers.get("requestHeaders");
|
||||
assertNotNull(requestParams);
|
||||
assertEquals(MediaType.TEXT_PLAIN, ((HttpHeaders) requestHeaders).getContentType());
|
||||
|
||||
Map<String, Cookie> cookies = (Map<String, Cookie>) headers.get("cookies");
|
||||
assertEquals(1, cookies.size());
|
||||
Cookie foo = cookies.get("foo");
|
||||
assertNotNull(foo);
|
||||
assertEquals(cookie, foo);
|
||||
}
|
||||
});
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
request.addHeader("toLowerCase", true);
|
||||
|
||||
Object handler = this.handlerMapping.getHandler(request).getHandler();
|
||||
this.handlerAdapter.handle(request, response, handler);
|
||||
final String testResponse = response.getContentAsString();
|
||||
assertEquals(testRequest.toLowerCase(), testResponse);
|
||||
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -17,17 +17,22 @@
|
||||
package org.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Abstract class for client connection factories; client connection factories
|
||||
* establish outgoing connections.
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractClientConnectionFactory extends AbstractConnectionFactory {
|
||||
|
||||
private TcpConnectionSupport theConnection;
|
||||
private final ReadWriteLock theConnectionLock = new ReentrantReadWriteLock();
|
||||
|
||||
private volatile TcpConnectionSupport theConnection;
|
||||
|
||||
/**
|
||||
* Constructs a factory that will established connections to the host and port.
|
||||
@@ -45,18 +50,70 @@ public abstract class AbstractClientConnectionFactory extends AbstractConnection
|
||||
*/
|
||||
public TcpConnectionSupport getConnection() throws Exception {
|
||||
this.checkActive();
|
||||
if (this.isSingleUse()) {
|
||||
return obtainConnection();
|
||||
} else {
|
||||
synchronized(this) {
|
||||
TcpConnectionSupport connection = obtainConnection();
|
||||
this.setTheConnection(connection);
|
||||
return this.obtainConnection();
|
||||
}
|
||||
|
||||
protected TcpConnectionSupport obtainConnection() throws Exception {
|
||||
if (!this.isSingleUse()) {
|
||||
TcpConnectionSupport connection = this.obtainSharedConnection();
|
||||
if (connection != null) {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
return this.obtainNewConnection();
|
||||
}
|
||||
|
||||
protected final TcpConnectionSupport obtainSharedConnection() throws InterruptedException {
|
||||
this.theConnectionLock.readLock().lockInterruptibly();
|
||||
try {
|
||||
TcpConnectionSupport theConnection = this.getTheConnection();
|
||||
if (theConnection != null && theConnection.isOpen()) {
|
||||
return theConnection;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.theConnectionLock.readLock().unlock();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected final TcpConnectionSupport obtainNewConnection() throws Exception {
|
||||
boolean singleUse = this.isSingleUse();
|
||||
if (!singleUse) {
|
||||
this.theConnectionLock.writeLock().lockInterruptibly();
|
||||
}
|
||||
try {
|
||||
TcpConnectionSupport connection;
|
||||
if (!singleUse) {
|
||||
// Another write lock holder might have created a new one by now.
|
||||
connection = this.obtainSharedConnection();
|
||||
if (connection != null) {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening new socket connection to " + this.getHost() + ":" + this.getPort());
|
||||
}
|
||||
|
||||
connection = this.buildNewConnection();
|
||||
if (!singleUse) {
|
||||
this.setTheConnection(connection);
|
||||
}
|
||||
connection.publishConnectionOpenEvent();
|
||||
return connection;
|
||||
}
|
||||
finally {
|
||||
if (!singleUse) {
|
||||
this.theConnectionLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract TcpConnectionSupport obtainConnection() throws Exception;
|
||||
protected TcpConnectionSupport buildNewConnection() throws Exception {
|
||||
throw new UnsupportedOperationException("Factories that don't override this class' obtainConnection() must implement this method");
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfers attributes such as (de)serializers, singleUse etc to a new connection.
|
||||
|
||||
@@ -127,7 +127,6 @@ public abstract class TcpConnectionSupport implements TcpConnection {
|
||||
if (connectionFactoryName != null) {
|
||||
this.connectionFactoryName = connectionFactoryName;
|
||||
}
|
||||
this.publishConnectionOpenEvent();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("New connection " + this.getConnectionId());
|
||||
}
|
||||
|
||||
@@ -44,20 +44,8 @@ public class TcpNetClientConnectionFactory extends
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IOException
|
||||
* @throws SocketException
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected TcpConnectionSupport obtainConnection() throws Exception {
|
||||
TcpConnectionSupport theConnection = this.getTheConnection();
|
||||
if (theConnection != null && theConnection.isOpen()) {
|
||||
return theConnection;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening new socket connection to " + this.getHost() + ":" + this.getPort());
|
||||
}
|
||||
protected TcpConnectionSupport buildNewConnection() throws IOException, SocketException, Exception {
|
||||
Socket socket = createSocket(this.getHost(), this.getPort());
|
||||
setSocketAttributes(socket);
|
||||
TcpConnectionSupport connection = new TcpNetConnection(socket, false, this.isLookupHost(),
|
||||
|
||||
@@ -64,7 +64,8 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
try {
|
||||
if (this.getLocalAddress() == null) {
|
||||
theServerSocket = createServerSocket(this.getPort(), this.getBacklog(), null);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
InetAddress whichNic = InetAddress.getByName(this.getLocalAddress());
|
||||
theServerSocket = createServerSocket(this.getPort(), this.getBacklog(), whichNic);
|
||||
}
|
||||
@@ -80,7 +81,8 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
*/
|
||||
try {
|
||||
socket = serverSocket.accept();
|
||||
} catch (SocketTimeoutException ste) {
|
||||
}
|
||||
catch (SocketTimeoutException ste) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Timed out on accept; continuing");
|
||||
}
|
||||
@@ -104,9 +106,11 @@ public class TcpNetServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
this.initializeConnection(connection, socket);
|
||||
this.getTaskExecutor().execute(connection);
|
||||
this.harvestClosedConnections();
|
||||
connection.publishConnectionOpenEvent();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
// don't log an error if we had a good socket once and now it's closed
|
||||
if (e instanceof SocketException && theServerSocket != null) {
|
||||
logger.warn("Server Socket closed");
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.integration.ip.tcp.connection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
@@ -61,13 +60,9 @@ public class TcpNioClientConnectionFactory extends
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws IOException
|
||||
* @throws SocketException
|
||||
*/
|
||||
@Override
|
||||
protected TcpConnectionSupport obtainConnection() throws Exception {
|
||||
protected void checkActive() throws IOException {
|
||||
super.checkActive();
|
||||
int n = 0;
|
||||
while (this.selector == null) {
|
||||
try {
|
||||
@@ -76,16 +71,13 @@ public class TcpNioClientConnectionFactory extends
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (n++ > 600) {
|
||||
throw new Exception("Factory failed to start");
|
||||
throw new IOException("Factory failed to start");
|
||||
}
|
||||
}
|
||||
TcpConnectionSupport theConnection = this.getTheConnection();
|
||||
if (theConnection != null && theConnection.isOpen()) {
|
||||
return theConnection;
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opening new socket channel connection to " + this.getHost() + ":" + this.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TcpConnectionSupport buildNewConnection() throws Exception {
|
||||
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(this.getHost(), this.getPort()));
|
||||
setSocketAttributes(socketChannel.socket());
|
||||
TcpNioConnection connection = this.tcpNioConnectionSupport.createNewConnection(
|
||||
|
||||
@@ -168,6 +168,7 @@ public class TcpNioServerConnectionFactory extends AbstractServerConnectionFacto
|
||||
connection.setLastRead(now);
|
||||
this.channelMap.put(channel, connection);
|
||||
channel.register(selector, SelectionKey.OP_READ, connection);
|
||||
connection.publishConnectionOpenEvent();
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Exception accepting new connection", e);
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
<bean id="tcpIpUtils" class="org.springframework.integration.test.util.SocketUtils" />
|
||||
|
||||
<int-ip:tcp-connection-factory id="server"
|
||||
<int-ip:tcp-connection-factory id="serverNet"
|
||||
type="server"
|
||||
using-nio="true"
|
||||
using-nio="false"
|
||||
single-use="true"
|
||||
port="#{tcpIpUtils.findAvailableServerSocket(10000)}"
|
||||
task-executor="exec"
|
||||
@@ -23,18 +23,44 @@
|
||||
so-timeout="20000"
|
||||
/>
|
||||
|
||||
<int-ip:tcp-connection-factory id="client"
|
||||
<int-ip:tcp-connection-factory id="clientNet"
|
||||
type="client"
|
||||
host="localhost"
|
||||
port="#{server.port}"
|
||||
port="#{serverNet.port}"
|
||||
single-use="true"
|
||||
lookup-host="false"
|
||||
so-timeout="100000"
|
||||
/>
|
||||
|
||||
<int-ip:tcp-inbound-gateway id="looper"
|
||||
<int-ip:tcp-connection-factory id="serverNio"
|
||||
type="server"
|
||||
using-nio="true"
|
||||
single-use="true"
|
||||
port="#{tcpIpUtils.findAvailableServerSocket(20000)}"
|
||||
task-executor="exec"
|
||||
lookup-host="false"
|
||||
so-timeout="20000"
|
||||
/>
|
||||
|
||||
<int-ip:tcp-connection-factory id="clientNio"
|
||||
type="client"
|
||||
host="localhost"
|
||||
using-nio="true"
|
||||
port="#{serverNio.port}"
|
||||
single-use="true"
|
||||
lookup-host="false"
|
||||
so-timeout="100000"
|
||||
/>
|
||||
|
||||
<int-ip:tcp-inbound-gateway id="gwNet"
|
||||
request-channel="serverSideChannel"
|
||||
connection-factory="server"
|
||||
connection-factory="serverNet"
|
||||
reply-timeout="1"
|
||||
/>
|
||||
|
||||
<int-ip:tcp-inbound-gateway id="gwNio"
|
||||
request-channel="serverSideChannel"
|
||||
connection-factory="serverNio"
|
||||
reply-timeout="1"
|
||||
/>
|
||||
|
||||
|
||||
@@ -65,10 +65,16 @@ public class ConnectionToConnectionTests {
|
||||
AbstractApplicationContext ctx;
|
||||
|
||||
@Autowired
|
||||
private AbstractClientConnectionFactory client;
|
||||
private AbstractClientConnectionFactory clientNet;
|
||||
|
||||
@Autowired
|
||||
private AbstractServerConnectionFactory server;
|
||||
private AbstractServerConnectionFactory serverNet;
|
||||
|
||||
@Autowired
|
||||
private AbstractClientConnectionFactory clientNio;
|
||||
|
||||
@Autowired
|
||||
private AbstractServerConnectionFactory serverNio;
|
||||
|
||||
@Autowired
|
||||
private QueueChannel serverSideChannel;
|
||||
@@ -90,9 +96,19 @@ public class ConnectionToConnectionTests {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testConnect() throws Exception {
|
||||
public void testConnectNet() throws Exception {
|
||||
testConnectGuts(this.clientNet, this.serverNet, "gwNet", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectNio() throws Exception {
|
||||
testConnectGuts(this.clientNio, this.serverNio, "gwNio", false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void testConnectGuts(AbstractClientConnectionFactory client, AbstractServerConnectionFactory server,
|
||||
String gatewayName, boolean expectExceptionOnClose) throws Exception {
|
||||
TestingUtilities.waitListening(server, null);
|
||||
client.start();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
@@ -102,7 +118,7 @@ public class ConnectionToConnectionTests {
|
||||
assertNotNull(message);
|
||||
MessageHistory history = MessageHistory.read(message);
|
||||
//org.springframework.integration.test.util.TestUtils
|
||||
Properties componentHistoryRecord = TestUtils.locateComponentInHistory(history, "looper", 0);
|
||||
Properties componentHistoryRecord = TestUtils.locateComponentInHistory(history, gatewayName, 0);
|
||||
assertNotNull(componentHistoryRecord);
|
||||
assertTrue(componentHistoryRecord.get("type").equals("ip:tcp-inbound-gateway"));
|
||||
assertNotNull(message);
|
||||
@@ -116,7 +132,7 @@ public class ConnectionToConnectionTests {
|
||||
Message<TcpConnectionEvent> eventMessage;
|
||||
while ((eventMessage = (Message<TcpConnectionEvent>) events.receive(1000)) != null) {
|
||||
TcpConnectionEvent event = eventMessage.getPayload();
|
||||
if ("client".equals(event.getConnectionFactoryName())) {
|
||||
if (event.getConnectionFactoryName().startsWith("client")) {
|
||||
if (event instanceof TcpConnectionOpenEvent) {
|
||||
clientOpens++;
|
||||
}
|
||||
@@ -127,7 +143,7 @@ public class ConnectionToConnectionTests {
|
||||
clientExceptions++;
|
||||
}
|
||||
}
|
||||
else if ("server".equals(event.getConnectionFactoryName())) {
|
||||
else if (event.getConnectionFactoryName().startsWith("server")) {
|
||||
if (event instanceof TcpConnectionOpenEvent) {
|
||||
serverOpens++;
|
||||
}
|
||||
@@ -138,7 +154,9 @@ public class ConnectionToConnectionTests {
|
||||
}
|
||||
assertEquals(100, clientOpens);
|
||||
assertEquals(100, clientCloses);
|
||||
assertEquals(100, clientExceptions);
|
||||
if (expectExceptionOnClose) {
|
||||
assertEquals(100, clientExceptions);
|
||||
}
|
||||
assertEquals(100, serverOpens);
|
||||
assertEquals(100, serverCloses);
|
||||
}
|
||||
@@ -146,16 +164,16 @@ public class ConnectionToConnectionTests {
|
||||
@Test
|
||||
public void testConnectRaw() throws Exception {
|
||||
ByteArrayRawSerializer serializer = new ByteArrayRawSerializer();
|
||||
client.setSerializer(serializer);
|
||||
server.setDeserializer(serializer);
|
||||
client.start();
|
||||
TcpConnection connection = client.getConnection();
|
||||
clientNet.setSerializer(serializer);
|
||||
serverNet.setDeserializer(serializer);
|
||||
clientNet.start();
|
||||
TcpConnection connection = clientNet.getConnection();
|
||||
connection.send(MessageBuilder.withPayload("Test").build());
|
||||
Message<?> message = serverSideChannel.receive(10000);
|
||||
assertNotNull(message);
|
||||
MessageHistory history = MessageHistory.read(message);
|
||||
//org.springframework.integration.test.util.TestUtils
|
||||
Properties componentHistoryRecord = TestUtils.locateComponentInHistory(history, "looper", 0);
|
||||
Properties componentHistoryRecord = TestUtils.locateComponentInHistory(history, "gwNet", 0);
|
||||
assertNotNull(componentHistoryRecord);
|
||||
assertTrue(componentHistoryRecord.get("type").equals("ip:tcp-inbound-gateway"));
|
||||
assertNotNull(message);
|
||||
@@ -164,16 +182,16 @@ public class ConnectionToConnectionTests {
|
||||
|
||||
@Test
|
||||
public void testLookup() throws Exception {
|
||||
client.start();
|
||||
TcpConnection connection = client.getConnection();
|
||||
clientNet.start();
|
||||
TcpConnection connection = clientNet.getConnection();
|
||||
assertFalse(connection.getConnectionId().contains("localhost"));
|
||||
connection.close();
|
||||
client.setLookupHost(true);
|
||||
connection = client.getConnection();
|
||||
clientNet.setLookupHost(true);
|
||||
connection = clientNet.getConnection();
|
||||
assertTrue(connection.getConnectionId().contains("localhost"));
|
||||
connection.close();
|
||||
client.setLookupHost(false);
|
||||
connection = client.getConnection();
|
||||
clientNet.setLookupHost(false);
|
||||
connection = clientNet.getConnection();
|
||||
assertFalse(connection.getConnectionId().contains("localhost"));
|
||||
connection.close();
|
||||
}
|
||||
|
||||
@@ -52,10 +52,10 @@ public class ConnectionEventTests {
|
||||
theEvent.add((TcpConnectionEvent) event);
|
||||
}
|
||||
}, "foo");
|
||||
assertTrue(theEvent.size() > 0);
|
||||
assertNotNull(theEvent.get(0));
|
||||
assertTrue(theEvent.get(0) instanceof TcpConnectionOpenEvent);
|
||||
assertTrue(theEvent.get(0).toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "] **OPENED**"));
|
||||
/*
|
||||
* Open is not published by the connection itself; the factory publishes it after initialization.
|
||||
* See ConnectionToConnectionTests.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
Serializer<Object> serializer = mock(Serializer.class);
|
||||
RuntimeException toBeThrown = new RuntimeException("foo");
|
||||
@@ -67,17 +67,17 @@ public class ConnectionEventTests {
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (Exception e) {}
|
||||
assertTrue(theEvent.size() > 1);
|
||||
assertNotNull(theEvent.get(1));
|
||||
assertTrue(theEvent.get(1) instanceof TcpConnectionExceptionEvent);
|
||||
assertTrue(theEvent.get(1).toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "]"));
|
||||
assertTrue(theEvent.get(1).toString().contains("cause=java.lang.RuntimeException: foo]"));
|
||||
TcpConnectionExceptionEvent event = (TcpConnectionExceptionEvent) theEvent.get(1);
|
||||
assertTrue(theEvent.size() > 0);
|
||||
assertNotNull(theEvent.get(0));
|
||||
assertTrue(theEvent.get(0) instanceof TcpConnectionExceptionEvent);
|
||||
assertTrue(theEvent.get(0).toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "]"));
|
||||
assertTrue(theEvent.get(0).toString().contains("cause=java.lang.RuntimeException: foo]"));
|
||||
TcpConnectionExceptionEvent event = (TcpConnectionExceptionEvent) theEvent.get(0);
|
||||
assertNotNull(event.getCause());
|
||||
assertSame(toBeThrown, event.getCause());
|
||||
assertTrue(theEvent.size() > 2);
|
||||
assertNotNull(theEvent.get(2));
|
||||
assertTrue(theEvent.get(2).toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "] **CLOSED**"));
|
||||
assertTrue(theEvent.size() > 1);
|
||||
assertNotNull(theEvent.get(1));
|
||||
assertTrue(theEvent.get(1).toString().endsWith("[factory=foo, connectionId=" + conn.getConnectionId() + "] **CLOSED**"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -107,9 +107,7 @@ public class JdbcOutboundGateway extends AbstractReplyProducingMessageHandler im
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
|
||||
protected void doInit() {
|
||||
if (this.maxRowsPerPoll != null) {
|
||||
Assert.notNull(poller, "If you want to set 'maxRowsPerPoll', then you must provide a 'selectQuery'.");
|
||||
poller.setMaxRowsPerPoll(this.maxRowsPerPoll);
|
||||
|
||||
@@ -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.
|
||||
@@ -88,8 +88,7 @@ public class StoredProcOutboundGateway extends AbstractReplyProducingMessageHand
|
||||
* when {@link ProcedureParameter} are passed in.
|
||||
*/
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
};
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
@@ -40,12 +41,12 @@ import org.springframework.core.serializer.support.SerializingConverter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.integration.jdbc.JdbcMessageStore;
|
||||
import org.springframework.integration.jdbc.store.channel.ChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.jdbc.store.channel.DerbyChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.jdbc.store.channel.MessageRowMapper;
|
||||
import org.springframework.integration.jdbc.store.channel.MySqlChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.jdbc.store.channel.OracleChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.jdbc.store.channel.PostgresChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.jdbc.store.channel.ChannelMessageStoreQueryProvider;
|
||||
import org.springframework.integration.store.AbstractMessageGroupStore;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.store.MessageGroupStore;
|
||||
@@ -590,7 +591,9 @@ public class JdbcChannelMessageStore extends AbstractMessageGroupStore implement
|
||||
final Message<?> polledMessage = this.doPollForMessage(key);
|
||||
|
||||
if (polledMessage != null){
|
||||
this.removeMessageFromGroup(groupId, polledMessage);
|
||||
if (!this.doRemoveMessageFromGroup(groupId, polledMessage)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return polledMessage;
|
||||
@@ -605,18 +608,26 @@ public class JdbcChannelMessageStore extends AbstractMessageGroupStore implement
|
||||
*/
|
||||
public MessageGroup removeMessageFromGroup(Object groupId, Message<?> messageToRemove) {
|
||||
|
||||
this.doRemoveMessageFromGroup(groupId, messageToRemove);
|
||||
|
||||
return getMessageGroup(groupId);
|
||||
}
|
||||
|
||||
private boolean doRemoveMessageFromGroup(Object groupId, Message<?> messageToRemove) {
|
||||
final UUID id = messageToRemove.getHeaders().getId();
|
||||
|
||||
int updated = jdbcTemplate.update(getQuery(channelMessageStoreQueryProvider.getDeleteMessageQuery()), new Object[] { getKey(id), getKey(groupId), region }, new int[] {
|
||||
Types.VARCHAR, Types.VARCHAR, Types.VARCHAR });
|
||||
|
||||
if (updated != 0) {
|
||||
boolean result = updated != 0;
|
||||
if (result) {
|
||||
logger.debug(String.format("Message with id '%s' was deleted.", id));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
logger.warn(String.format("Message with id '%s' was not deleted.", id));
|
||||
}
|
||||
|
||||
return getMessageGroup(groupId);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,15 +12,14 @@
|
||||
*/
|
||||
package org.springframework.integration.jdbc;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
|
||||
@@ -42,7 +41,7 @@ public class JdbcOutboundGatewayTests {
|
||||
|
||||
try {
|
||||
jdbcOutboundGateway.setMaxRowsPerPoll(10);
|
||||
jdbcOutboundGateway.onInit();
|
||||
jdbcOutboundGateway.afterPropertiesSet();
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("If you want to set 'maxRowsPerPoll', then you must provide a 'selectQuery'.", e.getMessage());
|
||||
|
||||
@@ -12,27 +12,32 @@
|
||||
*/
|
||||
package org.springframework.integration.jdbc.store.channel;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.sql.DataSource;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Assert;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Assert;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.jdbc.store.JdbcChannelMessageStore;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.jdbc.store.JdbcChannelMessageStore;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
@@ -62,8 +67,19 @@ abstract class AbstractTxTimeoutMessageStoreTests {
|
||||
protected TestService testService;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("store")
|
||||
protected JdbcChannelMessageStore jdbcChannelMessageStore;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel first;
|
||||
|
||||
@Autowired
|
||||
private CountDownLatch successfulLatch;
|
||||
|
||||
@Autowired
|
||||
private AtomicInteger errorAtomicInteger;
|
||||
|
||||
|
||||
public void test() throws InterruptedException {
|
||||
|
||||
int maxMessages = 10;
|
||||
@@ -141,7 +157,9 @@ abstract class AbstractTxTimeoutMessageStoreTests {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!result) return false;
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -157,4 +175,14 @@ abstract class AbstractTxTimeoutMessageStoreTests {
|
||||
assertTrue(executorService.awaitTermination(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public void testInt3181ConcurrentPolling() throws InterruptedException {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
this.first.send(new GenericMessage<Object>("test"));
|
||||
}
|
||||
|
||||
assertTrue(this.successfulLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
assertEquals(0, errorAtomicInteger.get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ package org.springframework.integration.jdbc.store.channel;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -36,4 +37,10 @@ public class DerbyTxTimeoutMessageStoreTests extends AbstractTxTimeoutMessageSto
|
||||
super.test();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testInt3181ConcurrentPolling() throws InterruptedException {
|
||||
super.testInt3181ConcurrentPolling();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -40,4 +41,10 @@ public class HsqlTxTimeoutMessageStoreTests extends AbstractTxTimeoutMessageStor
|
||||
super.testInt2993IdCacheConcurrency();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testInt3181ConcurrentPolling() throws InterruptedException {
|
||||
super.testInt3181ConcurrentPolling();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ package org.springframework.integration.jdbc.store.channel;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -35,4 +36,10 @@ public class MySqlTxTimeoutMessageStoreTests extends AbstractTxTimeoutMessageSto
|
||||
super.test();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void testInt3181ConcurrentPolling() throws InterruptedException {
|
||||
super.testInt3181ConcurrentPolling();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -54,5 +54,44 @@
|
||||
|
||||
<int:logging-channel-adapter id="loggit" log-full-message="true" level="ERROR"/>
|
||||
|
||||
<task:executor id="threadPoolTaskExecutor" pool-size="100"/>
|
||||
|
||||
<bean id="messageStore" class="org.springframework.integration.jdbc.store.JdbcChannelMessageStore">
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
<property name="region" value="CONCURRENT_POLL"/>
|
||||
<property name="channelMessageStoreQueryProvider" ref="queryProvider"/>
|
||||
</bean>
|
||||
|
||||
<int:channel id="first">
|
||||
<int:queue message-store="messageStore"/>
|
||||
</int:channel>
|
||||
|
||||
<int:channel id="second">
|
||||
<int:queue message-store="messageStore"/>
|
||||
</int:channel>
|
||||
|
||||
<int:bridge input-channel="first" output-channel="second">
|
||||
<int:poller fixed-rate="200" task-executor="threadPoolTaskExecutor" max-messages-per-poll="3">
|
||||
<int:transactional transaction-manager="transactionManager"/>
|
||||
</int:poller>
|
||||
</int:bridge>
|
||||
|
||||
<bean id="successfulLatch" class="java.util.concurrent.CountDownLatch">
|
||||
<constructor-arg value="10"/>
|
||||
</bean>
|
||||
|
||||
<int:outbound-channel-adapter channel="second" expression="@successfulLatch.countDown()">
|
||||
<int:poller fixed-delay="1000">
|
||||
<int:transactional transaction-manager="transactionManager"/>
|
||||
</int:poller>
|
||||
</int:outbound-channel-adapter>
|
||||
|
||||
<bean id="errorAtomicInteger" class="java.util.concurrent.atomic.AtomicInteger"/>
|
||||
|
||||
<int:service-activator input-channel="errorChannel" output-channel="nullChannel"
|
||||
expression="@errorAtomicInteger.incrementAndGet()">
|
||||
<int:poller fixed-delay="1000"/>
|
||||
</int:service-activator>
|
||||
|
||||
</beans>
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler imp
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
protected void doInit() {
|
||||
synchronized (this.initializationMonitor) {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
@@ -470,7 +470,6 @@ public class JmsOutboundGateway extends AbstractReplyProducingMessageHandler imp
|
||||
^ this.requestDestinationName != null
|
||||
^ this.requestDestinationExpressionProcessor != null,
|
||||
"Exactly one of 'requestDestination', 'requestDestinationName', or 'requestDestinationExpression' is required.");
|
||||
super.onInit();
|
||||
if (this.requestDestinationExpressionProcessor != null) {
|
||||
this.requestDestinationExpressionProcessor.setBeanFactory(getBeanFactory());
|
||||
this.requestDestinationExpressionProcessor.setConversionService(getConversionService());
|
||||
|
||||
@@ -62,172 +62,171 @@ import org.springframework.util.ObjectUtils;
|
||||
*/
|
||||
public class OperationInvokingMessageHandler extends AbstractReplyProducingMessageHandler implements InitializingBean {
|
||||
|
||||
private volatile MBeanServerConnection server;
|
||||
private volatile MBeanServerConnection server;
|
||||
|
||||
private volatile ObjectName objectName;
|
||||
private volatile ObjectName objectName;
|
||||
|
||||
private volatile String operationName;
|
||||
private volatile String operationName;
|
||||
|
||||
|
||||
/**
|
||||
* Provide a reference to the MBeanServer within which the MBean
|
||||
* target for operation invocation has been registered.
|
||||
*/
|
||||
public void setServer(MBeanServerConnection server) {
|
||||
this.server = server;
|
||||
}
|
||||
/**
|
||||
* Provide a reference to the MBeanServer within which the MBean
|
||||
* target for operation invocation has been registered.
|
||||
*/
|
||||
public void setServer(MBeanServerConnection server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a default ObjectName to use when no such header is
|
||||
* available on the Message being handled.
|
||||
*/
|
||||
public void setObjectName(String objectName) {
|
||||
try {
|
||||
if (objectName != null) {
|
||||
this.objectName = ObjectNameManager.getInstance(objectName);
|
||||
}
|
||||
}
|
||||
catch (MalformedObjectNameException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Specify a default ObjectName to use when no such header is
|
||||
* available on the Message being handled.
|
||||
*/
|
||||
public void setObjectName(String objectName) {
|
||||
try {
|
||||
if (objectName != null) {
|
||||
this.objectName = ObjectNameManager.getInstance(objectName);
|
||||
}
|
||||
}
|
||||
catch (MalformedObjectNameException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify an operation name to be invoked when no such
|
||||
* header is available on the Message being handled.
|
||||
*/
|
||||
public void setOperationName(String operationName) {
|
||||
this.operationName = operationName;
|
||||
}
|
||||
/**
|
||||
* Specify an operation name to be invoked when no such
|
||||
* header is available on the Message being handled.
|
||||
*/
|
||||
public void setOperationName(String operationName) {
|
||||
this.operationName = operationName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onInit() {
|
||||
Assert.notNull(this.server, "MBeanServer is required.");
|
||||
super.onInit();
|
||||
}
|
||||
@Override
|
||||
protected void doInit() {
|
||||
Assert.notNull(this.server, "MBeanServer is required.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
ObjectName objectName = this.resolveObjectName(requestMessage);
|
||||
String operationName = this.resolveOperationName(requestMessage);
|
||||
Map<String, Object> paramsFromMessage = this.resolveParameters(requestMessage);
|
||||
try {
|
||||
MBeanInfo mbeanInfo = this.server.getMBeanInfo(objectName);
|
||||
MBeanOperationInfo[] opInfoArray = mbeanInfo.getOperations();
|
||||
boolean hasNoArgOption = false;
|
||||
for (MBeanOperationInfo opInfo : opInfoArray) {
|
||||
if (operationName.equals(opInfo.getName())) {
|
||||
MBeanParameterInfo[] paramInfoArray = opInfo.getSignature();
|
||||
if (paramInfoArray.length == 0) {
|
||||
hasNoArgOption = true;
|
||||
}
|
||||
if (paramInfoArray.length == paramsFromMessage.size()) {
|
||||
int index = 0;
|
||||
Object values[] = new Object[paramInfoArray.length];
|
||||
String signature[] = new String[paramInfoArray.length];
|
||||
for (MBeanParameterInfo paramInfo : paramInfoArray) {
|
||||
Object value = paramsFromMessage.get(paramInfo.getName());
|
||||
if (value == null) {
|
||||
/*
|
||||
* With Spring 3.2.3 and greater, the parameter names are
|
||||
* registered instead of the JVM's default p1, p2 etc.
|
||||
* Fall back to that naming style if not found.
|
||||
*/
|
||||
value = paramsFromMessage.get("p" + (index + 1));
|
||||
}
|
||||
if (value != null && value.getClass().getName().equals(paramInfo.getType())) {
|
||||
values[index] = value;
|
||||
signature[index] = paramInfo.getType();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
if (index == paramInfoArray.length) {
|
||||
return this.server.invoke(objectName, operationName, values, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasNoArgOption) {
|
||||
return this.server.invoke(objectName, operationName, null, null);
|
||||
}
|
||||
throw new MessagingException(requestMessage, "failed to find JMX operation '"
|
||||
+ operationName + "' on MBean [" + objectName + "] of type [" + mbeanInfo.getClassName()
|
||||
+ "] with " + paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet());
|
||||
}
|
||||
catch (JMException e) {
|
||||
throw new MessageHandlingException(requestMessage, "failed to invoke JMX operation '" +
|
||||
operationName + "' on MBean [" + objectName + "]" + " with " +
|
||||
paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet(), e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageHandlingException(requestMessage, "IOException on MBeanServerConnection", e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
ObjectName objectName = this.resolveObjectName(requestMessage);
|
||||
String operationName = this.resolveOperationName(requestMessage);
|
||||
Map<String, Object> paramsFromMessage = this.resolveParameters(requestMessage);
|
||||
try {
|
||||
MBeanInfo mbeanInfo = this.server.getMBeanInfo(objectName);
|
||||
MBeanOperationInfo[] opInfoArray = mbeanInfo.getOperations();
|
||||
boolean hasNoArgOption = false;
|
||||
for (MBeanOperationInfo opInfo : opInfoArray) {
|
||||
if (operationName.equals(opInfo.getName())) {
|
||||
MBeanParameterInfo[] paramInfoArray = opInfo.getSignature();
|
||||
if (paramInfoArray.length == 0) {
|
||||
hasNoArgOption = true;
|
||||
}
|
||||
if (paramInfoArray.length == paramsFromMessage.size()) {
|
||||
int index = 0;
|
||||
Object values[] = new Object[paramInfoArray.length];
|
||||
String signature[] = new String[paramInfoArray.length];
|
||||
for (MBeanParameterInfo paramInfo : paramInfoArray) {
|
||||
Object value = paramsFromMessage.get(paramInfo.getName());
|
||||
if (value == null) {
|
||||
/*
|
||||
* With Spring 3.2.3 and greater, the parameter names are
|
||||
* registered instead of the JVM's default p1, p2 etc.
|
||||
* Fall back to that naming style if not found.
|
||||
*/
|
||||
value = paramsFromMessage.get("p" + (index + 1));
|
||||
}
|
||||
if (value != null && value.getClass().getName().equals(paramInfo.getType())) {
|
||||
values[index] = value;
|
||||
signature[index] = paramInfo.getType();
|
||||
index++;
|
||||
}
|
||||
}
|
||||
if (index == paramInfoArray.length) {
|
||||
return this.server.invoke(objectName, operationName, values, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasNoArgOption) {
|
||||
return this.server.invoke(objectName, operationName, null, null);
|
||||
}
|
||||
throw new MessagingException(requestMessage, "failed to find JMX operation '"
|
||||
+ operationName + "' on MBean [" + objectName + "] of type [" + mbeanInfo.getClassName()
|
||||
+ "] with " + paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet());
|
||||
}
|
||||
catch (JMException e) {
|
||||
throw new MessageHandlingException(requestMessage, "failed to invoke JMX operation '" +
|
||||
operationName + "' on MBean [" + objectName + "]" + " with " +
|
||||
paramsFromMessage.size() + " parameters: " + paramsFromMessage.keySet(), e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new MessageHandlingException(requestMessage, "IOException on MBeanServerConnection", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* First checks if defaultObjectName is set, otherwise falls back on {@link JmxHeaders#OBJECT_NAME} header.
|
||||
*/
|
||||
private ObjectName resolveObjectName(Message<?> message) {
|
||||
ObjectName objectName = this.objectName;
|
||||
if (objectName == null){
|
||||
Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME);
|
||||
if (objectNameHeader instanceof ObjectName) {
|
||||
objectName = (ObjectName) objectNameHeader;
|
||||
}
|
||||
else if (objectNameHeader instanceof String) {
|
||||
try {
|
||||
objectName = ObjectNameManager.getInstance(objectNameHeader);
|
||||
}
|
||||
catch (MalformedObjectNameException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(objectName, "Failed to resolve ObjectName.");
|
||||
return objectName;
|
||||
}
|
||||
/**
|
||||
* First checks if defaultObjectName is set, otherwise falls back on {@link JmxHeaders#OBJECT_NAME} header.
|
||||
*/
|
||||
private ObjectName resolveObjectName(Message<?> message) {
|
||||
ObjectName objectName = this.objectName;
|
||||
if (objectName == null){
|
||||
Object objectNameHeader = message.getHeaders().get(JmxHeaders.OBJECT_NAME);
|
||||
if (objectNameHeader instanceof ObjectName) {
|
||||
objectName = (ObjectName) objectNameHeader;
|
||||
}
|
||||
else if (objectNameHeader instanceof String) {
|
||||
try {
|
||||
objectName = ObjectNameManager.getInstance(objectNameHeader);
|
||||
}
|
||||
catch (MalformedObjectNameException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Assert.notNull(objectName, "Failed to resolve ObjectName.");
|
||||
return objectName;
|
||||
}
|
||||
|
||||
/**
|
||||
* First checks if defaultOperationName is set, otherwise falls back on {@link JmxHeaders#OPERATION_NAME} header.
|
||||
*/
|
||||
private String resolveOperationName(Message<?> message) {
|
||||
String operationName = this.operationName;
|
||||
if (operationName == null){
|
||||
operationName = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class);
|
||||
}
|
||||
Assert.notNull(operationName, "Failed to resolve operation name.");
|
||||
return operationName;
|
||||
}
|
||||
/**
|
||||
* First checks if defaultOperationName is set, otherwise falls back on {@link JmxHeaders#OPERATION_NAME} header.
|
||||
*/
|
||||
private String resolveOperationName(Message<?> message) {
|
||||
String operationName = this.operationName;
|
||||
if (operationName == null){
|
||||
operationName = message.getHeaders().get(JmxHeaders.OPERATION_NAME, String.class);
|
||||
}
|
||||
Assert.notNull(operationName, "Failed to resolve operation name.");
|
||||
return operationName;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Map<String, Object> resolveParameters(Message<?> message) {
|
||||
Map<String, Object> map = null;
|
||||
if (message.getPayload() instanceof Map) {
|
||||
map = (Map<String, Object>) message.getPayload();
|
||||
}
|
||||
else if (message.getPayload() instanceof List) {
|
||||
map = this.createParameterMapFromList((List) message.getPayload());
|
||||
}
|
||||
else if (message.getPayload() != null && message.getPayload().getClass().isArray()) {
|
||||
map = this.createParameterMapFromList(
|
||||
Arrays.asList(ObjectUtils.toObjectArray(message.getPayload())));
|
||||
}
|
||||
else if (message.getPayload() != null) {
|
||||
map = this.createParameterMapFromList(Collections.singletonList(message.getPayload()));
|
||||
}
|
||||
else {
|
||||
map = Collections.EMPTY_MAP;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Map<String, Object> resolveParameters(Message<?> message) {
|
||||
Map<String, Object> map = null;
|
||||
if (message.getPayload() instanceof Map) {
|
||||
map = (Map<String, Object>) message.getPayload();
|
||||
}
|
||||
else if (message.getPayload() instanceof List) {
|
||||
map = this.createParameterMapFromList((List) message.getPayload());
|
||||
}
|
||||
else if (message.getPayload() != null && message.getPayload().getClass().isArray()) {
|
||||
map = this.createParameterMapFromList(
|
||||
Arrays.asList(ObjectUtils.toObjectArray(message.getPayload())));
|
||||
}
|
||||
else if (message.getPayload() != null) {
|
||||
map = this.createParameterMapFromList(Collections.singletonList(message.getPayload()));
|
||||
}
|
||||
else {
|
||||
map = Collections.EMPTY_MAP;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<String, Object> createParameterMapFromList(List parameters) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
map.put("p" + (i + 1), parameters.get(i));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Map<String, Object> createParameterMapFromList(List parameters) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
for (int i = 0; i < parameters.size(); i++) {
|
||||
map.put("p" + (i + 1), parameters.get(i));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/integration"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:int-jmx="http://www.springframework.org/schema/integration/jmx"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/jmx http://www.springframework.org/schema/integration/jmx/spring-integration-jmx.xsd
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:mbean-server/>
|
||||
<int-jmx:mbean-export/>
|
||||
|
||||
<message-history/>
|
||||
|
||||
<service-activator id="gatewayTestService" input-channel="gatewayTestInputChannel" ref="gateway"/>
|
||||
|
||||
<service-activator id="replyingHandlerTestService" input-channel="replyingHandlerTestInputChannel">
|
||||
<beans:bean
|
||||
class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
|
||||
</service-activator>
|
||||
|
||||
<service-activator id="optimizedRefReplyingHandlerTestService"
|
||||
input-channel="optimizedRefReplyingHandlerTestInputChannel" ref="testReplyingMessageHandler"/>
|
||||
|
||||
<service-activator id="replyingHandlerWithStandardMethodTestService"
|
||||
input-channel="replyingHandlerWithStandardMethodTestInputChannel"
|
||||
method="handleMessage">
|
||||
<beans:bean
|
||||
class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
|
||||
</service-activator>
|
||||
|
||||
<service-activator id="replyingHandlerWithOtherMethodTestService"
|
||||
input-channel="replyingHandlerWithOtherMethodTestInputChannel"
|
||||
method="foo">
|
||||
<beans:bean
|
||||
class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
|
||||
</service-activator>
|
||||
|
||||
<service-activator id="handlerTestService" input-channel="handlerTestInputChannel">
|
||||
<beans:bean
|
||||
class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestMessageHandler"/>
|
||||
</service-activator>
|
||||
|
||||
<service-activator id="processorTestService" input-channel="processorTestInputChannel" ref="testMessageProcessor"/>
|
||||
|
||||
<gateway id="gateway" default-request-channel="requestChannel" default-reply-channel="replyChannel"/>
|
||||
|
||||
<channel id="requestChannel"/>
|
||||
|
||||
<bridge id="bridge" input-channel="requestChannel" output-channel="replyChannel"/>
|
||||
|
||||
<channel id="replyChannel">
|
||||
<queue/>
|
||||
</channel>
|
||||
|
||||
<beans:bean id="testReplyingMessageHandler"
|
||||
class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestReplyingMessageHandler"/>
|
||||
|
||||
<beans:bean id="testMessageProcessor" class="org.springframework.integration.jmx.ServiceActivatorDefaultFrameworkMethodTests$TestMessageProcessor">
|
||||
<beans:property name="prefix" value="foo"/>
|
||||
</beans:bean>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.jmx;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.MessageProcessor;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* See INT-1688 for background.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
* @since 2.0.1
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ServiceActivatorDefaultFrameworkMethodTests {
|
||||
|
||||
@Autowired
|
||||
private MessageChannel gatewayTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel replyingHandlerTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel optimizedRefReplyingHandlerTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel replyingHandlerWithStandardMethodTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel replyingHandlerWithOtherMethodTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel handlerTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel processorTestInputChannel;
|
||||
|
||||
@Autowired
|
||||
private EventDrivenConsumer processorTestService;
|
||||
|
||||
@Autowired
|
||||
private MessageProcessor<?> testMessageProcessor;
|
||||
|
||||
@Test
|
||||
public void testGateway() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.gatewayTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,bridge,replyChannel", reply.getHeaders().get("history").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplyingMessageHandler() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.replyingHandlerTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("TEST", reply.getPayload());
|
||||
assertEquals("replyingHandlerTestInputChannel,replyingHandlerTestService", reply.getHeaders().get("history").toString());
|
||||
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
|
||||
assertEquals("doDispatch", st[15].getMethodName()); // close to the metal
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptimizedReplyingMessageHandler() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.optimizedRefReplyingHandlerTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("TEST", reply.getPayload());
|
||||
assertEquals("optimizedRefReplyingHandlerTestInputChannel,optimizedRefReplyingHandlerTestService",
|
||||
reply.getHeaders().get("history").toString());
|
||||
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
|
||||
assertEquals("doDispatch", st[15].getMethodName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplyingMessageHandlerWithStandardMethod() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.replyingHandlerWithStandardMethodTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("TEST", reply.getPayload());
|
||||
assertEquals("replyingHandlerWithStandardMethodTestInputChannel,replyingHandlerWithStandardMethodTestService", reply.getHeaders().get("history").toString());
|
||||
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
|
||||
assertEquals("doDispatch", st[15].getMethodName()); // close to the metal
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReplyingMessageHandlerWithOtherMethod() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.replyingHandlerWithOtherMethodTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("bar", reply.getPayload());
|
||||
assertEquals("replyingHandlerWithOtherMethodTestInputChannel,replyingHandlerWithOtherMethodTestService", reply.getHeaders().get("history").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageHandler() {
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
|
||||
this.handlerTestInputChannel.send(message);
|
||||
}
|
||||
|
||||
// INT-2399
|
||||
@Test
|
||||
public void testMessageProcessor() {
|
||||
Object processor = TestUtils.getPropertyValue(processorTestService, "handler.h.advised.targetSource.target.processor");
|
||||
assertSame(testMessageProcessor, processor);
|
||||
|
||||
QueueChannel replyChannel = new QueueChannel();
|
||||
Message<?> message = MessageBuilder.withPayload("bar").setReplyChannel(replyChannel).build();
|
||||
this.processorTestInputChannel.send(message);
|
||||
Message<?> reply = replyChannel.receive(0);
|
||||
assertEquals("foo:bar", reply.getPayload());
|
||||
assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString());
|
||||
}
|
||||
|
||||
private interface Foo {
|
||||
|
||||
public String foo(String in);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler implements Foo {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
Exception e = new RuntimeException();
|
||||
StackTraceElement[] st = e.getStackTrace();
|
||||
return MessageBuilder.withPayload(requestMessage.getPayload().toString().toUpperCase())
|
||||
.setHeader("callStack", st);
|
||||
}
|
||||
|
||||
public String foo(String in) {
|
||||
return "bar";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestMessageHandler implements MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> requestMessage) {
|
||||
Exception e = new RuntimeException();
|
||||
StackTraceElement[] st = e.getStackTrace();
|
||||
assertEquals("doDispatch", st[28].getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestMessageProcessor implements MessageProcessor<String> {
|
||||
|
||||
private String prefix;
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String processMessage(Message<?> message) {
|
||||
return prefix + ":" + message.getPayload();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -62,12 +62,8 @@ public class JpaOutboundGateway extends AbstractReplyProducingMessageHandler {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
this.jpaExecutor.setBeanFactory(this.getBeanFactory());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* 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. 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.
|
||||
@@ -27,7 +27,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base {@link MessageProcessor} for scripting implementations to extend.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Stefan Reuter
|
||||
* @since 2.0
|
||||
@@ -36,9 +36,9 @@ public abstract class AbstractScriptExecutingMessageProcessor<T> implements Mess
|
||||
|
||||
private final ScriptVariableGenerator scriptVariableGenerator;
|
||||
|
||||
private volatile ClassLoader beanClassLoader;
|
||||
protected volatile ClassLoader beanClassLoader;
|
||||
|
||||
private volatile BeanFactory beanFactory;
|
||||
protected volatile BeanFactory beanFactory;
|
||||
|
||||
protected AbstractScriptExecutingMessageProcessor() {
|
||||
this.scriptVariableGenerator = new DefaultScriptVariableGenerator();
|
||||
@@ -48,7 +48,7 @@ public abstract class AbstractScriptExecutingMessageProcessor<T> implements Mess
|
||||
Assert.notNull(scriptVariableGenerator, "scriptVariableGenerator must not be null");
|
||||
this.scriptVariableGenerator = scriptVariableGenerator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Executes the script and returns the result.
|
||||
@@ -69,23 +69,15 @@ public abstract class AbstractScriptExecutingMessageProcessor<T> implements Mess
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
protected BeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to create a script source,
|
||||
* optionally using the message to locate or create the script.
|
||||
*
|
||||
*
|
||||
* @param message the message being processed
|
||||
* @return a ScriptSource to use to create a script
|
||||
*/
|
||||
|
||||
@@ -72,7 +72,6 @@ public class Jsr223ServiceActivatorTests {
|
||||
String value1 = (String) replyChannel.receive(0).getPayload();
|
||||
String value2 = (String) replyChannel.receive(0).getPayload();
|
||||
String value3 = (String) replyChannel.receive(0).getPayload();
|
||||
System.out.println(value1 + "\n" + value2 + "\n" + value3);
|
||||
assertTrue(value1.startsWith("python-test-1-foo - bar"));
|
||||
assertTrue(value2.startsWith("python-test-2-foo - bar"));
|
||||
assertTrue(value3.startsWith("python-test-3-foo - bar"));
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.
|
||||
@@ -13,12 +13,12 @@
|
||||
package org.springframework.integration.scripting.jsr223;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.integration.scripting.ScriptExecutor;
|
||||
import org.springframework.integration.scripting.ScriptingException;
|
||||
@@ -30,28 +30,29 @@ import org.springframework.scripting.support.StaticScriptSource;
|
||||
*
|
||||
*/
|
||||
public class Jsr223ScriptExecutorTests {
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
public void test() {
|
||||
ScriptExecutor executor = ScriptExecutorFactory.getScriptExecutor("jruby");
|
||||
executor.executeScript(new StaticScriptSource("puts 'hello, world'"));
|
||||
executor.executeScript(new StaticScriptSource("puts 'hello, again'"));
|
||||
|
||||
executor.executeScript(new StaticScriptSource("'hello, world'"));
|
||||
executor.executeScript(new StaticScriptSource("'hello, again'"));
|
||||
|
||||
Map<String,Object> variables = new HashMap<String,Object>();
|
||||
|
||||
|
||||
Map<String,Object> headers = new HashMap<String,Object>();
|
||||
headers.put("one",1);
|
||||
headers.put("two","two");
|
||||
headers.put("three", new Integer(3));
|
||||
|
||||
headers.put("one", 1);
|
||||
headers.put("two", "two");
|
||||
headers.put("three", 3);
|
||||
|
||||
variables.put("payload", "payload");
|
||||
variables.put("headers", headers);
|
||||
|
||||
|
||||
String result = (String)executor.executeScript(
|
||||
new ResourceScriptSource(new ClassPathResource("/org/springframework/integration/scripting/jsr223/print_message.rb")),
|
||||
variables
|
||||
);
|
||||
|
||||
assertEquals("payload modified",result.substring(0,"payload modified".length()));
|
||||
|
||||
assertEquals("payload modified", result.substring(0, "payload modified".length()));
|
||||
}
|
||||
@Test
|
||||
public void testJs(){
|
||||
@@ -59,25 +60,20 @@ public class Jsr223ScriptExecutorTests {
|
||||
Object obj = executor.executeScript(new StaticScriptSource("function js(){ return 'js';} js();"));
|
||||
assertEquals("js",obj.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void testPython() {
|
||||
ScriptExecutor executor = ScriptExecutorFactory.getScriptExecutor("python");
|
||||
Object obj = executor.executeScript(new StaticScriptSource("x=2") );
|
||||
assertEquals(2,obj);
|
||||
|
||||
|
||||
obj = executor.executeScript(new StaticScriptSource("def foo(y):\n\tx=y\n\treturn y\nz=foo(2)") );
|
||||
assertEquals(2,obj);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test(expected = ScriptingException.class)
|
||||
public void testInvalidLanguageThrowsScriptingException() {
|
||||
try {
|
||||
ScriptExecutor executor = ScriptExecutorFactory.getScriptExecutor("foo");
|
||||
executor.executeScript(new StaticScriptSource("x=2"));
|
||||
fail("should throw Exception");
|
||||
} catch (ScriptingException e) {
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
ScriptExecutor executor = ScriptExecutorFactory.getScriptExecutor("foo");
|
||||
executor.executeScript(new StaticScriptSource("x=2"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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. 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.
|
||||
@@ -36,39 +36,38 @@ public class PythonScriptExecutorTests {
|
||||
public void init() {
|
||||
executor = new PythonScriptExecutor();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void testLiteral() {
|
||||
Object obj = executor.executeScript(new StaticScriptSource("3+4") );
|
||||
assertEquals(7,obj);
|
||||
|
||||
|
||||
obj = executor.executeScript(new StaticScriptSource("'hello,world'") );
|
||||
assertEquals("hello,world",obj);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
|
||||
@Test
|
||||
|
||||
public void test1() {
|
||||
Object obj = executor.executeScript(new StaticScriptSource("x=2") );
|
||||
assertEquals(2,obj);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void test2() {
|
||||
Object obj = executor.executeScript(new StaticScriptSource("def foo(y):\n\tx=y\n\treturn y\nz=foo(2)") );
|
||||
assertEquals(2,obj);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@Test
|
||||
public void test3() {
|
||||
ScriptSource source =
|
||||
ScriptSource source =
|
||||
new ResourceScriptSource(new ClassPathResource("/org/springframework/integration/scripting/jsr223/test3.py"));
|
||||
Object obj = executor.executeScript(source);
|
||||
System.out.println(obj);
|
||||
PyTuple tuple = (PyTuple) obj;
|
||||
assertEquals(1, tuple.get(0));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEmbeddedVariable() {
|
||||
Map<String,Object> variables = new HashMap<String,Object>();
|
||||
|
||||
@@ -46,4 +46,9 @@ public class SftpInboundFileSynchronizer extends AbstractInboundFileSynchronizer
|
||||
return (file != null ? file.getFilename() : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getModified(LsEntry file) {
|
||||
return (long) file.getAttrs().getMTime() * 1000;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="delete-remote-files" type="xsd:string">
|
||||
<xsd:attribute name="delete-remote-files" type="xsd:string" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether to delete the remote source
|
||||
@@ -246,6 +246,16 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="preserve-timestamp" type="xsd:string" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Specify whether to preserve the modified timestamp from the remote source
|
||||
file on the local file after copying.
|
||||
By default, the remote timestamp will NOT be
|
||||
preserved.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
<channel id="requestChannel">
|
||||
<queue/>
|
||||
</channel>
|
||||
|
||||
|
||||
<beans:bean id="pattern" class="java.util.regex.Pattern" factory-method="compile">
|
||||
<beans:constructor-arg value="."/>
|
||||
</beans:bean>
|
||||
|
||||
|
||||
<beans:bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
|
||||
<beans:property name="host" value="loclahost"/>
|
||||
<beans:property name="knownHosts" value="local, foo.com, bar.foo"/>
|
||||
@@ -49,14 +49,15 @@
|
||||
temporary-file-suffix=".bar"
|
||||
comparator="comparator"
|
||||
local-filter="acceptAllFilter"
|
||||
delete-remote-files="${delete.remote.files}">
|
||||
delete-remote-files="${delete.remote.files}"
|
||||
preserve-timestamp="true">
|
||||
<poller fixed-rate="1000">
|
||||
<transactional synchronization-factory="syncFactory"/>
|
||||
</poller>
|
||||
</sftp:inbound-channel-adapter>
|
||||
|
||||
<beans:bean id="acceptAllFilter" class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
|
||||
|
||||
|
||||
<transaction-synchronization-factory id="syncFactory">
|
||||
<after-commit expression="'foo'" channel="successChannel"/>
|
||||
<after-rollback expression="'bar'" channel="failureChannel"/>
|
||||
@@ -118,7 +119,7 @@
|
||||
</sftp:inbound-channel-adapter>
|
||||
|
||||
<bridge input-channel="autoChannel" output-channel="nullChannel" />
|
||||
|
||||
|
||||
<beans:bean id="transactionManager" class="org.springframework.integration.transaction.PseudoTransactionManager"/>
|
||||
|
||||
</beans:beans>
|
||||
|
||||
@@ -82,6 +82,7 @@ public class InboundChannelAdapterParserTests {
|
||||
assertNotNull(comparator);
|
||||
SftpInboundFileSynchronizer synchronizer = (SftpInboundFileSynchronizer) TestUtils.getPropertyValue(source, "synchronizer");
|
||||
assertNotNull(TestUtils.getPropertyValue(synchronizer, "localFilenameGeneratorExpression"));
|
||||
assertTrue(TestUtils.getPropertyValue(synchronizer, "preserveTimestamp", Boolean.class));
|
||||
String remoteFileSeparator = (String) TestUtils.getPropertyValue(synchronizer, "remoteFileSeparator");
|
||||
assertEquals(".bar", TestUtils.getPropertyValue(synchronizer, "temporaryFileSuffix", String.class));
|
||||
assertNotNull(remoteFileSeparator);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -29,8 +30,10 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Calendar;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -49,6 +52,7 @@ import com.jcraft.jsch.SftpATTRS;
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
@@ -81,6 +85,7 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
|
||||
SftpInboundFileSynchronizer synchronizer = spy(new SftpInboundFileSynchronizer(ftpSessionFactory));
|
||||
synchronizer.setDeleteRemoteFiles(true);
|
||||
synchronizer.setPreserveTimestamp(true);
|
||||
synchronizer.setRemoteDirectory("remote-test-dir");
|
||||
synchronizer.setFilter(new SftpRegexPatternFileListFilter(".*\\.test$"));
|
||||
synchronizer.setIntegrationEvaluationContext(ExpressionUtils.createStandardEvaluationContext());
|
||||
@@ -93,9 +98,15 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
Message<File> atestFile = ms.receive();
|
||||
assertNotNull(atestFile);
|
||||
assertEquals("a.test", atestFile.getPayload().getName());
|
||||
// The test remote files are created with the current timestamp + 1 day.
|
||||
assertThat(atestFile.getPayload().lastModified(), Matchers.greaterThan(System.currentTimeMillis()));
|
||||
|
||||
Message<File> btestFile = ms.receive();
|
||||
assertNotNull(btestFile);
|
||||
assertEquals("b.test", btestFile.getPayload().getName());
|
||||
// The test remote files are created with the current timestamp + 1 day.
|
||||
assertThat(atestFile.getPayload().lastModified(), Matchers.greaterThan(System.currentTimeMillis()));
|
||||
|
||||
Message<File> nothing = ms.receive();
|
||||
assertNull(nothing);
|
||||
|
||||
@@ -120,6 +131,10 @@ public class SftpInboundRemoteFileSystemSynchronizerTests {
|
||||
LsEntry lsEntry = mock(LsEntry.class);
|
||||
SftpATTRS attributes = mock(SftpATTRS.class);
|
||||
when(lsEntry.getAttrs()).thenReturn(attributes);
|
||||
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.DATE, 1);
|
||||
when(lsEntry.getAttrs().getMTime()).thenReturn(new Long(calendar.getTimeInMillis() / 1000).intValue());
|
||||
when(lsEntry.getFilename()).thenReturn(fileName);
|
||||
sftpEntries.add(lsEntry);
|
||||
when(channel.get("remote-test-dir/"+fileName)).thenReturn(new FileInputStream("remote-test-dir/" + fileName));
|
||||
|
||||
@@ -144,8 +144,7 @@ public abstract class AbstractWebServiceOutboundGateway extends AbstractReplyPro
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit() {
|
||||
super.onInit();
|
||||
protected void doInit() {
|
||||
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(this.getBeanFactory());
|
||||
Assert.state(this.destinationProvider == null || CollectionUtils.isEmpty(this.uriVariableExpressions),
|
||||
"uri variables are not supported when a DestinationProvider is supplied.");
|
||||
|
||||
@@ -187,6 +187,43 @@ public interface Cafe {
|
||||
</para>
|
||||
|
||||
</section>
|
||||
<section id="gateway-mapping">
|
||||
<title>Mapping Method Arguments to a Message</title>
|
||||
<para>
|
||||
Using the configuration techniques in the previous section allows control of how method arguments are mapped
|
||||
to message elements (payload and header(s)). When no explicit configuration is used, certain conventions are
|
||||
used to perform the mapping. In some cases, these conventions cannot determine which argument is the payload
|
||||
and which should be mapped to headers.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[
|
||||
public String send1(Object foo, Map bar);
|
||||
|
||||
public String send2(Map foo, Map bar);
|
||||
]]></programlisting>
|
||||
<para>
|
||||
In the first case, the convention will map the first argument to the payload (as long as it is not a
|
||||
<code>Map</code>) and the contents of the second become headers.
|
||||
</para>
|
||||
<para>
|
||||
In the second case (or the first when the argument for parameter <code>foo</code> is a <code>Map</code>),
|
||||
the framework cannot determine
|
||||
which argument should be the payload; mapping will fail. This can generally be resolved using a
|
||||
<code>payload-expression</code>, a <code>@Payload</code> annotation and/or a <code>@Headers</code>
|
||||
annotation.
|
||||
</para>
|
||||
<para>
|
||||
Alternatively, and whenever the conventions break down, you can take the entire responsibility for
|
||||
mapping the method calls to messages. To do this, implement an
|
||||
<classname>MethodArgsMessageMapper</classname> and provide it to the
|
||||
<code><gateway/></code> using the <code>mapper</code> attribute. The mapper maps a
|
||||
<classname>MethodArgsHolder</classname>, which is a simple class wrapping the <classname>java.reflect.Method</classname>
|
||||
instance and an <code>Object[]</code> containing the arguments. When providing a custom mapper,
|
||||
the <code>default-payload-expression</code> attribute and <code><default-header/></code> elements
|
||||
are not allowed on the gateway; similarly, the <code>payload-expression</code> attribute and
|
||||
<code><header/></code> elements are not allowed on any <code><method/></code> elements.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="gateway-calling-no-argument-methods">
|
||||
<title>Invoking No-Argument Methods</title>
|
||||
<para>
|
||||
|
||||
@@ -354,9 +354,57 @@ By default the HTTP request will be generated using an instance of <classname>Si
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
<para>
|
||||
Since <emphasis>Spring Integration 3.0</emphasis>, in addition to the existing
|
||||
<code>#pathVariables</code> and <code>#requestParams</code> variables being available in payload and header
|
||||
expressions, other useful variables have been added.
|
||||
</para>
|
||||
<para>
|
||||
The entire list of available expression variables:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<emphasis>#requestParams</emphasis> - the <interfacename>MultiValueMap</interfacename> from the
|
||||
<interfacename>ServletRequest</interfacename> <code>parameterMap</code>.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#pathVariables</emphasis> - the <interfacename>Map</interfacename> from URI Template placeholders and their values;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#matrixVariables</emphasis> - the <interfacename>Map</interfacename> of <interfacename>MultiValueMap</interfacename>
|
||||
according to
|
||||
<ulink url="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-matrix-variables"
|
||||
>Spring MVC Specification</ulink>. Note, <emphasis>#matrixVariables</emphasis> require Spring MVC 3.2 or higher;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#requestAttributes</emphasis> - the <interfacename>org.springframework.web.context.request.RequestAttributes</interfacename>
|
||||
associated with the current Request;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#requestHeaders</emphasis> - the <classname>org.springframework.http.HttpHeaders</classname> object from the current Request;
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis>#cookies</emphasis> - the <interfacename>Map<String, Cookie></interfacename>
|
||||
of <classname>javax.servlet.http.Cookie</classname>s from the current Request.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
<para>
|
||||
Note, all these values (and others) can be accessed within expressions in the downstream message
|
||||
flow via the <classname>ThreadLocal</classname> <interfacename>org.springframework.web.context.request.RequestAttributes</interfacename>
|
||||
variable, if that message flow is single-threaded and lives within the request thread:
|
||||
</para>
|
||||
<programlisting language="xml"><![CDATA[<int-:transformer
|
||||
expression="T(org.springframework.web.context.request.RequestContextHolder).
|
||||
requestAttributes.request.queryString"/>
|
||||
]]></programlisting>
|
||||
</para>
|
||||
|
||||
<para><emphasis>Outbound</emphasis></para>
|
||||
<para>
|
||||
To configure the outbound gateway you can use the namespace support as well. The following code snippet shows the different configuration options for an outbound Http gateway. Most importantly, notice that the 'http-method' and 'expected-response-type' are provided. Those are two of the most commonly configured values. The
|
||||
To configure the outbound gateway you can use the namespace support as well.
|
||||
The following code snippet shows the different configuration options for an outbound Http gateway.
|
||||
Most importantly, notice that the 'http-method' and 'expected-response-type' are provided. Those are two of the most commonly configured values. The
|
||||
default http-method is POST, and the default response type is <emphasis>null</emphasis>. With a null response type, the payload of the reply Message would
|
||||
contain the ResponseEntity as long as it's http status is a success (non-successful status codes will throw Exceptions).
|
||||
If you are expecting a different type, such as a <classname>String</classname>, then provide that fully-qualified class name as shown below.
|
||||
|
||||
@@ -416,6 +416,7 @@
|
||||
to configure the associated <classname>Poller</classname> with a
|
||||
<interfacename>TaskExecutor</interfacename> reference.
|
||||
</para>
|
||||
<important>
|
||||
<para>
|
||||
Keep in mind, though, that if you use a JDBC backed <emphasis>Message Channel</emphasis> and
|
||||
you are planning on polling the channel and consequently the message
|
||||
@@ -426,6 +427,13 @@
|
||||
threads, may not materialize as expected. For example Apache Derby is
|
||||
problematic in that regard.
|
||||
</para>
|
||||
<para>
|
||||
To achieve better JDBC queue throughput, and avoid issues when different threads may poll the same
|
||||
<interfacename>Message</interfacename> from the queue, it is <emphasis role="bold">important</emphasis>
|
||||
to set the <code>usingIdCache</code> property of <classname>JdbcChannelMessageStore</classname> to <code>true</code>
|
||||
when using databases that do not support MVCC:
|
||||
</para>
|
||||
</important>
|
||||
<programlisting language="xml"><![CDATA[…
|
||||
<bean id="queryProvider"
|
||||
class="o.s.i.jdbc.store.channel.PostgresChannelMessageStoreQueryProvider"/>
|
||||
@@ -459,6 +467,7 @@
|
||||
|
||||
<int:channel id="outputChannel" />
|
||||
…]]></programlisting>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>Initializing the Database</title>
|
||||
|
||||
@@ -163,6 +163,10 @@
|
||||
It is now possible to set common headers across all gateway methods, and more options
|
||||
are provided for adding, to the message, information about which method was invoked.
|
||||
</listitem>
|
||||
<listitem>
|
||||
It is now possible to entirely customize the way that gateway method calls are mapped
|
||||
to messages.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
@@ -244,6 +248,10 @@
|
||||
to be maintained across JVM executions, a custom filter that retains state, perhaps on
|
||||
the file system, can now be configured.
|
||||
</para>
|
||||
<para>
|
||||
Inbound Channel Adapters now support the <code>preserve-timestamp</code> attribute, which
|
||||
sets the local file modified timestamp to the timestamp from the server (default false).
|
||||
</para>
|
||||
<para>
|
||||
For more information, see
|
||||
<xref linkend="ftp-inbound"/> and <xref linkend="sftp-inbound"/>.
|
||||
@@ -329,12 +337,20 @@
|
||||
<interfacename>HttpMessageConverter</interfacename>s after the custom message converters.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis role="bold">'If-(Un)Modified-Since' HTTP headers</emphasis> - previously,
|
||||
<emphasis role="bold">'If-(Un)Modified-Since' HTTP Headers</emphasis> - previously,
|
||||
'If-Modified-Since' and 'If-Unmodified-Since' HTTP headers were incorrectly processed
|
||||
within from/to HTTP headers mapping in the <classname>DefaultHttpHeaderMapper</classname>.
|
||||
Now, in addition correcting that issue, <classname>DefaultHttpHeaderMapper</classname> provides
|
||||
date parsing from formatted strings for any HTTP headers that accept date-time values.
|
||||
</listitem>
|
||||
<listitem>
|
||||
<emphasis role="bold">Inbound Endpoint Expression Variables</emphasis> -
|
||||
In addition to the existing <emphasis>#requestParams</emphasis> and <emphasis>#pathVariables</emphasis>,
|
||||
the <code><http:inbound-gateway/></code> and <code><http:inbound-channel-adapter/></code>
|
||||
now support additional useful variables: <emphasis>#matrixVariables</emphasis>, <emphasis>#requestAttributes</emphasis>,
|
||||
<emphasis>#requestHeaders</emphasis> and <emphasis>#cookies</emphasis>. These variables are available in
|
||||
both payload and header expressions.
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user