Merge branch 'master' of git.springsource.org:spring-integration/spring-integration

This commit is contained in:
Josh Long
2010-10-14 11:24:43 -07:00
174 changed files with 4464 additions and 2624 deletions

View File

@@ -16,8 +16,13 @@
package org.springframework.integration.aggregator;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.util.Assert;
/**
* {@link CorrelationStrategy} implementation that evaluates an expression.
@@ -26,12 +31,23 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
*/
public class ExpressionEvaluatingCorrelationStrategy implements CorrelationStrategy {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final ExpressionEvaluatingMessageProcessor<Object> processor;
public ExpressionEvaluatingCorrelationStrategy(String expression) {
public ExpressionEvaluatingCorrelationStrategy(String expressionString) {
Assert.hasText(expressionString, "expressionString must not be empty");
Expression expression = expressionParser.parseExpression(expressionString);
this.processor = new ExpressionEvaluatingMessageProcessor<Object>(expression, Object.class);
}
public ExpressionEvaluatingCorrelationStrategy(Expression expression) {
this.processor = new ExpressionEvaluatingMessageProcessor<Object>(expression, Object.class);
}
public Object getCorrelationKey(Message<?> message) {
return processor.processMessage(message);
}

View File

@@ -1,3 +1,19 @@
/*
* 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.aggregator;
import java.util.Map;
@@ -15,12 +31,16 @@ import org.springframework.integration.store.MessageGroup;
*
* @author Alex Peters
* @author Dave Syer
*
*/
public class ExpressionEvaluatingMessageGroupProcessor extends AbstractAggregatingMessageGroupProcessor implements BeanFactoryAware {
private final ExpressionEvaluatingMessageListProcessor processor;
public ExpressionEvaluatingMessageGroupProcessor(String expression) {
processor = new ExpressionEvaluatingMessageListProcessor(expression);
}
public void setBeanFactory(BeanFactory beanFactory) {
processor.setBeanFactory(beanFactory);
}
@@ -33,10 +53,6 @@ public class ExpressionEvaluatingMessageGroupProcessor extends AbstractAggregati
processor.setExpectedType(expectedType);
}
public ExpressionEvaluatingMessageGroupProcessor(String expression) {
processor = new ExpressionEvaluatingMessageListProcessor(expression);
}
/**
* Evaluate the expression provided on the unmarked messages (a collection) in the group, and delegate to the
* {@link MessagingTemplate} to send downstream.

View File

@@ -1,64 +0,0 @@
/*
* Copyright 2002-2009 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.channel;
import java.util.HashMap;
import java.util.Map;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.util.Assert;
/**
* {@link ChannelResolver} implementation that resolves {@link MessageChannel}
* instances by matching the channel name against keys within a Map.
*
* @author Mark Fisher
*/
public class MapBasedChannelResolver implements ChannelResolver {
private volatile Map<String, ? extends MessageChannel> channelMap = new HashMap<String, MessageChannel>();
/**
* Empty constructor for use when providing the channel map via
* {@link #setChannelMap(Map)}.
*/
public MapBasedChannelResolver() {
}
/**
* Create a {@link ChannelResolver} that uses the provided Map.
* Each String key will resolve to the associated channel value.
*/
public MapBasedChannelResolver(Map<String, ? extends MessageChannel> channelMap) {
this.setChannelMap(channelMap);
}
/**
* Provide a map of channels to be used by this resolver.
* Each String key will resolve to the associated channel value.
*/
public void setChannelMap(Map<String, ? extends MessageChannel> channelMap) {
Assert.notNull(channelMap, "channelMap must not be null");
this.channelMap = channelMap;
}
public MessageChannel resolveChannelName(String channelName) {
return this.channelMap.get(channelName);
}
}

View File

@@ -22,6 +22,10 @@ import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.AbstractMessageHandler;
@@ -38,13 +42,16 @@ import org.springframework.util.StringUtils;
*/
abstract class AbstractMessageHandlerFactoryBean implements FactoryBean<MessageHandler>, BeanFactoryAware {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private volatile MessageHandler handler;
private volatile Object targetObject;
private volatile String targetMethodName;
private volatile String expression;
private volatile Expression expression;
private volatile MessageChannel outputChannel;
@@ -65,7 +72,11 @@ abstract class AbstractMessageHandlerFactoryBean implements FactoryBean<MessageH
this.targetMethodName = targetMethodName;
}
public void setExpression(String expression) {
public void setExpressionString(String expressionString) {
this.expression = expressionParser.parseExpression(expressionString);
}
public void setExpression(Expression expression) {
this.expression = expression;
}
@@ -125,7 +136,7 @@ abstract class AbstractMessageHandlerFactoryBean implements FactoryBean<MessageH
if (this.targetObject != null) {
Assert.state(this.expression == null,
"The 'targetObject' and 'expression' properties are mutually exclusive.");
if (this.targetObject instanceof MessageProcessor) {
if (this.targetObject instanceof MessageProcessor<?>) {
this.handler = this.createMessageProcessingHandler((MessageProcessor<?>) this.targetObject);
}
else {
@@ -158,7 +169,7 @@ abstract class AbstractMessageHandlerFactoryBean implements FactoryBean<MessageH
*/
abstract MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName);
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support expressions.");
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.config;
import org.springframework.expression.Expression;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageSelector;
@@ -67,7 +68,7 @@ public class FilterFactoryBean extends AbstractMessageHandlerFactoryBean {
}
@Override
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
return this.createFilter(new ExpressionEvaluatingSelector(expression));
}

View File

@@ -1,24 +1,25 @@
/*
* 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.
*
* 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.config;
import java.util.Map;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.expression.Expression;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.router.AbstractChannelNameResolvingMessageRouter;
import org.springframework.integration.router.AbstractMessageRouter;
import org.springframework.integration.router.ExpressionEvaluatingRouter;
import org.springframework.integration.router.MethodInvokingRouter;
@@ -31,10 +32,13 @@ import org.springframework.util.StringUtils;
*
* @author Mark Fisher
* @author Jonas Partner
* @author Oleg Zhurakousky
*/
public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
private volatile ChannelResolver channelResolver;
private volatile Map<String, String> channelIdentifierMap;
private volatile MessageChannel defaultOutputChannel;
@@ -48,7 +52,6 @@ public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
private volatile Boolean ignoreSendFailures;
public void setChannelResolver(ChannelResolver channelResolver) {
this.channelResolver = channelResolver;
}
@@ -76,36 +79,75 @@ public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
public void setIgnoreSendFailures(Boolean ignoreSendFailures) {
this.ignoreSendFailures = ignoreSendFailures;
}
@Override
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
Assert.notNull(targetObject, "target object must not be null");
AbstractMessageRouter router = this.createRouter(targetObject, targetMethodName);
return this.configureRouter(router);
public void setChannelIdentifierMap(Map<String, String> channelIdentifierMap) {
this.channelIdentifierMap = channelIdentifierMap;
}
@Override
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
Assert.notNull(targetObject, "target object must not be null");
AbstractMessageRouter router = extractRouter(targetObject);
if (router == null) {
router = this.createRouter(targetObject, targetMethodName);
this.configureRouter(router);
return router;
}
Assert.isTrue(!StringUtils.hasText(targetMethodName), "target method should not be provided when the target "
+ "object is an implementation of AbstractMessageRouter");
this.configureRouter(router);
if (targetObject instanceof MessageHandler) {
return (MessageHandler) targetObject;
}
return router;
}
private AbstractMessageRouter extractRouter(Object targetObject) {
if (targetObject instanceof AbstractMessageRouter) {
return (AbstractMessageRouter) targetObject;
}
if (targetObject instanceof Advised) {
return extractAopTarget((Advised) targetObject);
}
return null;
}
private AbstractMessageRouter extractAopTarget(Advised advised) {
TargetSource targetSource = advised.getTargetSource();
if (targetSource == null) {
return null;
}
Object target;
try {
target = targetSource.getTarget();
} catch (Exception e) {
throw new IllegalStateException(e);
}
return extractRouter(target);
}
@Override
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
return this.configureRouter(new ExpressionEvaluatingRouter(expression));
}
private AbstractMessageRouter createRouter(Object targetObject, String targetMethodName) {
if (targetObject instanceof AbstractMessageRouter) {
Assert.isTrue(!StringUtils.hasText(targetMethodName),
"target method should not be provided when the target " +
"object is an implementation of AbstractMessageRouter");
return (AbstractMessageRouter) targetObject;
}
MethodInvokingRouter router = (StringUtils.hasText(targetMethodName))
? new MethodInvokingRouter(targetObject, targetMethodName)
: new MethodInvokingRouter(targetObject);
MethodInvokingRouter router = (StringUtils.hasText(targetMethodName)) ? new MethodInvokingRouter(targetObject,
targetMethodName) : new MethodInvokingRouter(targetObject);
return router;
}
private AbstractMessageRouter configureRouter(AbstractMessageRouter router) {
if (this.channelResolver != null &&
router instanceof AbstractChannelNameResolvingMessageRouter) {
((AbstractChannelNameResolvingMessageRouter) router).setChannelResolver(this.channelResolver);
if (this.channelResolver != null && router instanceof AbstractMessageRouter) {
((AbstractMessageRouter) router).setChannelResolver(this.channelResolver);
}
if (this.channelIdentifierMap != null && router instanceof AbstractMessageRouter) {
((AbstractMessageRouter) router).setChannelIdentifierMap(this.channelIdentifierMap);
}
if (this.defaultOutputChannel != null) {
router.setDefaultOutputChannel(this.defaultOutputChannel);
@@ -114,10 +156,11 @@ public class RouterFactoryBean extends AbstractMessageHandlerFactoryBean {
router.setTimeout(timeout.longValue());
}
if (this.ignoreChannelNameResolutionFailures != null) {
Assert.isTrue(router instanceof AbstractChannelNameResolvingMessageRouter,
Assert.isTrue(router instanceof AbstractMessageRouter,
"The 'ignoreChannelNameResolutionFailures' property can only be set on routers that extend "
+ AbstractChannelNameResolvingMessageRouter.class.getName());
((AbstractChannelNameResolvingMessageRouter) router).setIgnoreChannelNameResolutionFailures(ignoreChannelNameResolutionFailures);
+ AbstractMessageRouter.class.getName());
((AbstractMessageRouter) router)
.setIgnoreChannelNameResolutionFailures(ignoreChannelNameResolutionFailures);
}
if (this.applySequence != null) {
router.setApplySequence(this.applySequence);

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.config;
import org.springframework.expression.Expression;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.MessageProcessor;
@@ -51,7 +52,7 @@ public class ServiceActivatorFactoryBean extends AbstractMessageHandlerFactoryBe
}
@Override
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
ExpressionEvaluatingMessageProcessor<Object> processor = new ExpressionEvaluatingMessageProcessor<Object>(expression);
processor.setBeanFactory(this.getBeanFactory());
return this.configureHandler(new ServiceActivatingHandler(processor));

View File

@@ -125,6 +125,11 @@ public class SourcePollingChannelAdapterFactoryBean implements FactoryBean<Sourc
Assert.notNull(this.pollerMetadata, "No poller has been defined for channel-adapter '"
+ this.beanName + "', and no default poller is available within the context.");
}
if (this.pollerMetadata.getMaxMessagesPerPoll() < 1){
// the default is 1 since a source might return
// a non-null and non-interruptable value every time it is invoked
this.pollerMetadata.setMaxMessagesPerPoll(1);
}
spca.setPollerMetadata(this.pollerMetadata);
spca.setBeanClassLoader(this.beanClassLoader);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* 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.
@@ -16,6 +16,7 @@
package org.springframework.integration.config;
import org.springframework.expression.Expression;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.splitter.AbstractMessageSplitter;
import org.springframework.integration.splitter.DefaultMessageSplitter;
@@ -31,12 +32,22 @@ import org.springframework.util.StringUtils;
public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean {
private volatile Long sendTimeout;
private volatile boolean requiresReply;
public void setSendTimeout(Long sendTimeout) {
this.sendTimeout = sendTimeout;
}
public boolean isRequiresReply() {
return requiresReply;
}
public void setRequiresReply(boolean requiresReply) {
this.requiresReply = requiresReply;
}
@Override
MessageHandler createMethodInvokingHandler(Object targetObject, String targetMethodName) {
AbstractMessageSplitter splitter = null;
@@ -52,7 +63,7 @@ public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean {
}
@Override
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
return this.configureSplitter(new ExpressionEvaluatingSplitter(expression));
}
@@ -68,11 +79,5 @@ public class SplitterFactoryBean extends AbstractMessageHandlerFactoryBean {
splitter.setRequiresReply(requiresReply);
return splitter;
}
public boolean isRequiresReply() {
return requiresReply;
}
public void setRequiresReply(boolean requiresReply) {
this.requiresReply = requiresReply;
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.config;
import org.springframework.expression.Expression;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.transformer.ExpressionEvaluatingTransformer;
import org.springframework.integration.transformer.MessageTransformingHandler;
@@ -54,7 +55,7 @@ public class TransformerFactoryBean extends AbstractMessageHandlerFactoryBean {
}
@Override
MessageHandler createExpressionEvaluatingHandler(String expression) {
MessageHandler createExpressionEvaluatingHandler(Expression expression) {
Transformer transformer = new ExpressionEvaluatingTransformer(expression);
return this.createHandler(transformer);
}

View File

@@ -1,60 +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.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
/**
* Base parser for routers that create instances that are subclasses of AbstractChannelNameResolvingMessageRouter.
*
* @author Mark Fisher
*/
public abstract class AbstractChannelNameResolvingRouterParser extends AbstractRouterParser {
@Override
protected final BeanDefinition parseRouter(Element element, ParserContext parserContext) {
BeanDefinition beanDefinition = this.doParseRouter(element, parserContext);
if (beanDefinition != null) {
// check if mapping is provided otherwise returned values will be treated as channel names
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (childElements != null && childElements.size() > 0) {
BeanDefinitionBuilder channelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
ManagedMap<String, RuntimeBeanReference> channelMap = new ManagedMap<String, RuntimeBeanReference>();
for (Element childElement : childElements) {
channelMap.put(childElement.getAttribute("value"),
new RuntimeBeanReference(childElement.getAttribute("channel")));
}
channelResolverBuilder.addPropertyValue("channelMap", channelMap);
beanDefinition.getPropertyValues().add("channelResolver", channelResolverBuilder.getBeanDefinition());
}
}
return beanDefinition;
}
protected abstract BeanDefinition doParseRouter(Element element, ParserContext parserContext);
}

View File

@@ -38,6 +38,7 @@ abstract class AbstractDelegatingConsumerEndpointParser extends AbstractConsumer
@Override
protected final BeanDefinitionBuilder parseHandler(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(this.getFactoryBeanClassName());
BeanComponentDefinition innerDefinition = IntegrationNamespaceUtils.parseInnerHandlerDefinition(element, parserContext);
String ref = element.getAttribute(REF_ATTRIBUTE);
@@ -45,23 +46,43 @@ abstract class AbstractDelegatingConsumerEndpointParser extends AbstractConsumer
boolean hasRef = StringUtils.hasText(ref);
boolean hasExpression = StringUtils.hasText(expression);
Element scriptElement = DomUtils.getChildElementByTagName(element, "script");
Element expressionElement = DomUtils.getChildElementByTagName(element, "expression");
if (innerDefinition != null) {
if (hasRef || hasExpression) {
if (hasRef || hasExpression || expressionElement != null) {
parserContext.getReaderContext().error(
"Neither 'ref' nor 'expression' are permitted when an inner bean (<bean/>) is configured.", element);
"Neither 'ref' nor 'expression' are permitted when an inner bean (<bean/>) is configured.", source);
return null;
}
builder.addPropertyValue("targetObject", innerDefinition);
}
else if (scriptElement != null) {
if (hasRef || hasExpression || expressionElement != null) {
parserContext.getReaderContext().error(
"Neither 'ref' nor 'expression' are permitted when an inner script element is configured.", source);
return null;
}
BeanDefinition scriptBeanDefinition = parserContext.getDelegate().parseCustomElement(scriptElement, builder.getBeanDefinition());
builder.addPropertyValue("targetObject", scriptBeanDefinition);
}
else if (expressionElement != null) {
if (hasRef || hasExpression) {
parserContext.getReaderContext().error(
"Neither 'ref' nor 'expression' are permitted when an inner 'expression' element is configured.", source);
return null;
}
BeanDefinitionBuilder dynamicExpressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.expression.DynamicExpression");
String key = expressionElement.getAttribute("key");
String expressionSourceReference = expressionElement.getAttribute("source");
dynamicExpressionBuilder.addConstructorArgValue(key);
dynamicExpressionBuilder.addConstructorArgReference(expressionSourceReference);
builder.addPropertyValue("expression", dynamicExpressionBuilder.getBeanDefinition());
}
else if (hasRef) {
builder.addPropertyReference("targetObject", ref);
}
else if (hasExpression) {
builder.addPropertyValue("expression", expression);
}
else if (scriptElement != null) {
BeanDefinition scriptBeanDefinition = parserContext.getDelegate().parseCustomElement(scriptElement, builder.getBeanDefinition());
builder.addPropertyValue("targetObject", scriptBeanDefinition);
builder.addPropertyValue("expressionString", expression);
}
else if (!this.hasDefaultOption()) {
parserContext.getReaderContext().error("Exactly one of the 'ref' attribute, 'expression' attribute, " +

View File

@@ -16,11 +16,17 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Base parser for routers.
@@ -44,6 +50,34 @@ public abstract class AbstractRouterParser extends AbstractConsumerEndpointParse
return builder;
}
protected abstract BeanDefinition parseRouter(Element element, ParserContext parserContext);
protected final BeanDefinition parseRouter(Element element, ParserContext parserContext) {
BeanDefinition beanDefinition = this.doParseRouter(element, parserContext);
if (beanDefinition != null) {
String channelResolver = element.getAttribute("channel-resolver");
if (StringUtils.hasText(channelResolver)){
beanDefinition.getPropertyValues().add("channelResolver", new RuntimeBeanReference(channelResolver));
}
// check if mapping is provided otherwise returned values will be treated as channel names
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
if (childElements != null && childElements.size() > 0) {
ManagedMap<String, String> channelMap = new ManagedMap<String, String>();
for (Element childElement : childElements) {
String beanClassName = beanDefinition.getBeanClassName();
String key = null;
if (beanClassName.endsWith("PayloadTypeRouter")){
key = childElement.getAttribute("type");
}
else {
key = childElement.getAttribute("value");
}
channelMap.put(key, childElement.getAttribute("channel"));
}
beanDefinition.getPropertyValues().add("channelIdentifierMap", channelMap);
}
}
return beanDefinition;
}
protected abstract BeanDefinition doParseRouter(Element element, ParserContext parserContext);
}

View File

@@ -18,9 +18,6 @@ package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -28,11 +25,13 @@ import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for the &lt;router/&gt; element.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class DefaultRouterParser extends AbstractDelegatingConsumerEndpointParser {
@@ -60,13 +59,12 @@ public class DefaultRouterParser extends AbstractDelegatingConsumerEndpointParse
parserContext.extractSource(element));
}
BeanDefinitionBuilder channelResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.MapBasedChannelResolver");
ManagedMap<String, RuntimeBeanReference> channelMap = new ManagedMap<String, RuntimeBeanReference>();
IntegrationNamespaceUtils.BASE_PACKAGE + ".support.channel.BeanFactoryChannelResolver");
ManagedMap<String, String> channelMap = new ManagedMap<String, String>();
for (Element mappingElement : mappingElements) {
channelMap.put(mappingElement.getAttribute("value"),
new RuntimeBeanReference(mappingElement.getAttribute("channel")));
channelMap.put(mappingElement.getAttribute("value"), mappingElement.getAttribute("channel"));
}
channelResolverBuilder.addPropertyValue("channelMap", channelMap);
builder.addPropertyValue("channelIdentifierMap", channelMap);
builder.addPropertyValue(CHANNEL_RESOLVER_PROPERTY, channelResolverBuilder.getBeanDefinition());
}
else if (StringUtils.hasText(resolverBeanName)) {

View File

@@ -29,6 +29,7 @@ import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
/**
* Base support class for 'header-enricher' parsers.
@@ -110,12 +111,17 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
if (headerName != null) {
String value = headerElement.getAttribute("value");
String ref = headerElement.getAttribute("ref");
String expression = headerElement.getAttribute("expression");
String method = headerElement.getAttribute("method");
String expression = headerElement.getAttribute("expression");
Element expressionElement = DomUtils.getChildElementByTagName(headerElement, "expression");
if (StringUtils.hasText(expression) && expressionElement != null) {
parserContext.getReaderContext().error("The 'expression' attribute and sub-element are mutually exclusive", element);
return;
}
boolean isValue = StringUtils.hasText(value);
boolean isRef = StringUtils.hasText(ref);
boolean isExpression = StringUtils.hasText(expression);
boolean hasMethod = StringUtils.hasText(method);
boolean isExpression = StringUtils.hasText(expression) || expressionElement != null;
if (!(isValue ^ (isRef ^ isExpression))) {
parserContext.getReaderContext().error(
"Exactly one of the 'ref', 'value', or 'expression' attributes is required.", element);
@@ -139,7 +145,16 @@ public abstract class HeaderEnricherParserSupport extends AbstractTransformerPar
}
valueProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".transformer.HeaderEnricher$ExpressionEvaluatingHeaderValueMessageProcessor");
valueProcessorBuilder.addConstructorArgValue(expression);
if (expressionElement != null) {
BeanDefinitionBuilder dynamicExpressionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.expression.DynamicExpression");
dynamicExpressionBuilder.addConstructorArgValue(expressionElement.getAttribute("key"));
dynamicExpressionBuilder.addConstructorArgReference(expressionElement.getAttribute("source"));
valueProcessorBuilder.addConstructorArgValue(dynamicExpressionBuilder.getBeanDefinition());
}
else {
valueProcessorBuilder.addConstructorArgValue(expression);
}
valueProcessorBuilder.addConstructorArgValue(headerType);
}
else {

View File

@@ -29,7 +29,7 @@ import org.springframework.beans.factory.xml.ParserContext;
* @author Mark Fisher
* @since 1.0.3
*/
public class HeaderValueRouterParser extends AbstractChannelNameResolvingRouterParser {
public class HeaderValueRouterParser extends AbstractRouterParser {
@Override
protected BeanDefinition doParseRouter(Element element, ParserContext parserContext) {

View File

@@ -16,18 +16,10 @@
package org.springframework.integration.config.xml;
import java.util.List;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for the &lt;payload-type-router/&gt; element.
@@ -37,27 +29,12 @@ import org.springframework.util.xml.DomUtils;
* @since 1.0.3
*/
public class PayloadTypeRouterParser extends AbstractRouterParser {
@Override
@SuppressWarnings("unchecked")
protected BeanDefinition parseRouter(Element element, ParserContext parserContext) {
protected BeanDefinition doParseRouter(Element element,
ParserContext parserContext) {
BeanDefinitionBuilder payloadTypeRouterBuilder = BeanDefinitionBuilder.genericBeanDefinition(
IntegrationNamespaceUtils.BASE_PACKAGE + ".router.PayloadTypeRouter");
List<Element> childElements = DomUtils.getChildElementsByTagName(element, "mapping");
Assert.notEmpty(childElements,
"Type mapping must be provided (e.g., <mapping type=\"X\" channel=\"channel1\"/>)");
ManagedMap channelMap = new ManagedMap();
for (Element childElement : childElements) {
String typeName = childElement.getAttribute("type");
ClassLoader classLoader = parserContext.getReaderContext().getBeanClassLoader();
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
Assert.isTrue(ClassUtils.isPresent(typeName, classLoader), typeName + " can not be loaded");
channelMap.put(typeName, new RuntimeBeanReference(childElement.getAttribute("channel")));
}
payloadTypeRouterBuilder.addPropertyValue("payloadTypeChannelMap", channelMap);
return payloadTypeRouterBuilder.getBeanDefinition();
}
}

View File

@@ -53,10 +53,9 @@ public class PublishingInterceptorParser extends AbstractBeanDefinitionParser {
spelSourceBuilder.addPropertyValue("headerExpressionMap", mappings.get("headers"));
}
BeanDefinitionBuilder chResolverBuilder = BeanDefinitionBuilder.genericBeanDefinition(
"org.springframework.integration.channel.MapBasedChannelResolver");
"org.springframework.integration.support.channel.BeanFactoryChannelResolver");
if (mappings.get("channels") != null){
spelSourceBuilder.addPropertyValue("channelMap", mappings.get("channels"));
chResolverBuilder.addConstructorArgValue(mappings.get("resolvableChannels"));
}
String chResolverName =
BeanDefinitionReaderUtils.registerWithGeneratedName(chResolverBuilder.getBeanDefinition(), parserContext.getRegistry());

View File

@@ -18,13 +18,13 @@ package org.springframework.integration.endpoint;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledFuture;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
@@ -43,11 +43,11 @@ import org.springframework.util.ErrorHandler;
*/
public abstract class AbstractPollingEndpoint extends AbstractEndpoint implements BeanClassLoaderAware {
private volatile TaskExecutor taskExecutor = new SyncTaskExecutor();
private volatile Executor taskExecutor = new SyncTaskExecutor();
private volatile ErrorHandler errorHandler;
private volatile PollerMetadata pollerMetadata;
private volatile PollerMetadata pollerMetadata = new PollerMetadata();
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -84,14 +84,14 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
return;
}
Assert.notNull(this.pollerMetadata.getTrigger(), "Trigger is required");
Assert.notNull(this.getBeanFactory(), "BeanFactory is required");
TaskExecutor providedExecutor = this.pollerMetadata.getTaskExecutor();
Executor providedExecutor = this.pollerMetadata.getTaskExecutor();
if (providedExecutor != null) {
this.taskExecutor = providedExecutor;
}
if (this.taskExecutor != null) {
if (!(this.taskExecutor instanceof ErrorHandlingTaskExecutor)) {
if (this.errorHandler == null) {
Assert.notNull(this.getBeanFactory(), "BeanFactory is required");
this.errorHandler = new MessagePublishingErrorHandler(
new BeanFactoryChannelResolver(getBeanFactory()));
}

View File

@@ -0,0 +1,149 @@
/*
* 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.expression;
import java.util.Locale;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.util.Assert;
/**
* An implementation of {@link Expression} that delegates to an {@link ExpressionSource}
* for resolving the actual Expression instance per-invocation at runtime.
*
* @author Mark Fisher
* @since 2.0
*/
public class DynamicExpression implements Expression {
private final String key;
private final ExpressionSource expressionSource;
public DynamicExpression(String key, ExpressionSource expressionSource) {
Assert.notNull(key, "key must not be null");
Assert.notNull(expressionSource, "expressionSource must not be null");
this.key = key;
this.expressionSource = expressionSource;
}
public Object getValue() throws EvaluationException {
return this.resolveExpression().getValue();
}
public Object getValue(Object rootObject) throws EvaluationException {
return this.resolveExpression().getValue(rootObject);
}
public <T> T getValue(Class<T> desiredResultType) throws EvaluationException {
return this.resolveExpression().getValue(desiredResultType);
}
public <T> T getValue(Object rootObject, Class<T> desiredResultType) throws EvaluationException {
return this.resolveExpression().getValue(rootObject, desiredResultType);
}
public Object getValue(EvaluationContext context) throws EvaluationException {
return this.resolveExpression().getValue(context);
}
public Object getValue(EvaluationContext context, Object rootObject) throws EvaluationException {
return this.resolveExpression().getValue(context, rootObject);
}
public <T> T getValue(EvaluationContext context, Class<T> desiredResultType) throws EvaluationException {
return this.resolveExpression().getValue(context, desiredResultType);
}
public <T> T getValue(EvaluationContext context, Object rootObject, Class<T> desiredResultType) throws EvaluationException {
return this.resolveExpression().getValue(context, rootObject, desiredResultType);
}
public Class<?> getValueType() throws EvaluationException {
return this.resolveExpression().getValueType();
}
public Class<?> getValueType(Object rootObject) throws EvaluationException {
return this.resolveExpression().getValueType(rootObject);
}
public Class<?> getValueType(EvaluationContext context) throws EvaluationException {
return this.resolveExpression().getValueType(context);
}
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
return this.resolveExpression().getValueType(context, rootObject);
}
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
return this.resolveExpression().getValueTypeDescriptor();
}
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
return this.resolveExpression().getValueTypeDescriptor(rootObject);
}
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
return this.resolveExpression().getValueTypeDescriptor(context);
}
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject) throws EvaluationException {
return this.resolveExpression().getValueTypeDescriptor(context, rootObject);
}
public boolean isWritable(EvaluationContext context) throws EvaluationException {
return this.resolveExpression().isWritable(context);
}
public boolean isWritable(EvaluationContext context, Object rootObject) throws EvaluationException {
return this.resolveExpression().isWritable(context, rootObject);
}
public boolean isWritable(Object rootObject) throws EvaluationException {
return this.resolveExpression().isWritable(rootObject);
}
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
this.resolveExpression().setValue(context, value);
}
public void setValue(Object rootObject, Object value) throws EvaluationException {
this.resolveExpression().setValue(rootObject, value);
}
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
this.resolveExpression().setValue(context, rootObject, value);
}
public String getExpressionString() {
return this.resolveExpression().getExpressionString();
}
private Expression resolveExpression() {
Locale locale = LocaleContextHolder.getLocale();
Expression expression = this.expressionSource.getExpression(this.key, locale);
Assert.state(expression != null, "Unable to resolve Expression with key '" + this.key + "'");
return expression;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -14,12 +14,20 @@
* limitations under the License.
*/
package org.springframework.integration.xml.router;
package org.springframework.integration.expression;
import javax.xml.transform.Source;
import java.util.Locale;
public interface XmlValidator {
public boolean isValid(Source source) ;
import org.springframework.expression.Expression;
/**
* Strategy interface for retrieving Expressions.
*
* @author Mark Fisher
* @since 2.0
*/
public interface ExpressionSource {
Expression getExpression(String key, Locale locale);
}

View File

@@ -0,0 +1,572 @@
/*
* 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.expression;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import org.springframework.util.StringUtils;
/**
* {@link ExpressionSource} implementation that accesses resource bundles using specified basenames.
* This class uses {@link java.util.Properties} instances as its custom data structure for expressions,
* loading them via a {@link org.springframework.util.PropertiesPersister} strategy: The default
* strategy is capable of loading properties files with a specific character encoding, if desired.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.0
* @see #setCacheSeconds
* @see #setBasenames
* @see #setDefaultEncoding
* @see #setFileEncodings
* @see #setPropertiesPersister
* @see #setResourceLoader
* @see org.springframework.util.DefaultPropertiesPersister
* @see org.springframework.core.io.DefaultResourceLoader
* @see java.util.ResourceBundle
*/
public class ReloadableResourceBundleExpressionSource implements ExpressionSource, ResourceLoaderAware {
private static final String PROPERTIES_SUFFIX = ".properties";
private static final String XML_SUFFIX = ".xml";
private static final Log logger = LogFactory.getLog(ReloadableResourceBundleExpressionSource.class);
private volatile String[] basenames = new String[0];
private volatile String defaultEncoding;
private volatile Properties fileEncodings;
private volatile boolean fallbackToSystemLocale = true;
private volatile long cacheMillis = -1;
private volatile PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
private volatile ResourceLoader resourceLoader = new DefaultResourceLoader();
/** Cache to hold filename lists per Locale */
private final Map<String, Map<Locale, List<String>>> cachedFilenames =
new HashMap<String, Map<Locale, List<String>>>();
/** Cache to hold already loaded properties per filename */
private final Map<String, PropertiesHolder> cachedProperties = new HashMap<String, PropertiesHolder>();
/** Cache to hold merged loaded properties per locale */
private final Map<Locale, PropertiesHolder> cachedMergedProperties = new HashMap<Locale, PropertiesHolder>();
private final ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
/**
* Set a single basename, following the basic ResourceBundle convention of
* not specifying file extension or language codes, but referring to a Spring
* resource location: e.g. "META-INF/expressions" for "META-INF/expressions.properties",
* "META-INF/expressions_en.properties", etc.
* <p>XML properties files are also supported: .g. "META-INF/expressions" will find
* and load "META-INF/expressions.xml", "META-INF/expressions_en.xml", etc as well.
* @param basename the single basename
* @see #setBasenames
* @see org.springframework.core.io.ResourceEditor
* @see java.util.ResourceBundle
*/
public void setBasename(String basename) {
setBasenames(new String[] {basename});
}
/**
* Set an array of basenames, each following the basic ResourceBundle convention
* of not specifying file extension or language codes, but referring to a Spring
* resource location: e.g. "META-INF/expressions" for "META-INF/expressions.properties",
* "META-INF/expressions_en.properties", etc.
* <p>XML properties files are also supported: .g. "META-INF/expressions" will find
* and load "META-INF/expressions.xml", "META-INF/expressions_en.xml", etc as well.
* <p>The associated resource bundles will be checked sequentially when resolving
* an expression key. Note that expression definitions in a <i>previous</i> resource
* bundle will override ones in a later bundle, due to the sequential lookup.
* @param basenames an array of basenames
* @see #setBasename
* @see java.util.ResourceBundle
*/
public void setBasenames(String[] basenames) {
if (basenames != null) {
this.basenames = new String[basenames.length];
for (int i = 0; i < basenames.length; i++) {
String basename = basenames[i];
Assert.hasText(basename, "Basename must not be empty");
this.basenames[i] = basename.trim();
}
}
else {
this.basenames = new String[0];
}
}
/**
* Set the default charset to use for parsing properties files.
* Used if no file-specific charset is specified for a file.
* <p>Default is none, using the <code>java.util.Properties</code>
* default encoding.
* <p>Only applies to classic properties files, not to XML files.
* @param defaultEncoding the default charset
* @see #setFileEncodings
* @see org.springframework.util.PropertiesPersister#load
*/
public void setDefaultEncoding(String defaultEncoding) {
this.defaultEncoding = defaultEncoding;
}
/**
* Set per-file charsets to use for parsing properties files.
* <p>Only applies to classic properties files, not to XML files.
* @param fileEncodings Properties with filenames as keys and charset
* names as values. Filenames have to match the basename syntax,
* with optional locale-specific appendices: e.g. "META-INF/expressions"
* or "META-INF/expressions_en".
* @see #setBasenames
* @see org.springframework.util.PropertiesPersister#load
*/
public void setFileEncodings(Properties fileEncodings) {
this.fileEncodings = fileEncodings;
}
/**
* Set whether to fall back to the system Locale if no files for a specific
* Locale have been found. Default is "true"; if this is turned off, the only
* fallback will be the default file (e.g. "expressions.properties" for
* basename "expressions").
* <p>Falling back to the system Locale is the default behavior of
* <code>java.util.ResourceBundle</code>. However, this is often not
* desirable in an application server environment, where the system Locale
* is not relevant to the application at all: Set this flag to "false"
* in such a scenario.
*/
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
this.fallbackToSystemLocale = fallbackToSystemLocale;
}
/**
* Set the number of seconds to cache loaded properties files.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* <code>java.util.ResourceBundle</code>).
* <li>A positive number will cache loaded properties files for the given
* number of seconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
* of the file before actually reloading it; so if files don't change, this
* interval can be set rather low, as refresh attempts will not actually reload.
* <li>A value of "0" will check the last-modified timestamp of the file on
* every expression access. <b>Do not use this in a production environment!</b>
* </ul>
*/
public void setCacheSeconds(int cacheSeconds) {
this.cacheMillis = (cacheSeconds * 1000);
}
/**
* Set the PropertiesPersister to use for parsing properties files.
* <p>The default is a DefaultPropertiesPersister.
* @see org.springframework.util.DefaultPropertiesPersister
*/
public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
this.propertiesPersister =
(propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister());
}
/**
* Set the ResourceLoader to use for loading bundle properties files.
* <p>The default is a DefaultResourceLoader. Will get overridden by the
* ApplicationContext if running in a context, as it implements the
* ResourceLoaderAware interface. Can be manually overridden when
* running outside of an ApplicationContext.
* @see org.springframework.core.io.DefaultResourceLoader
* @see org.springframework.context.ResourceLoaderAware
*/
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
}
/**
* Resolves the given key in the retrieved bundle files to an Expression.
*/
public Expression getExpression(String key, Locale locale) {
String expressionString = this.getExpressionString(key, locale);
if (expressionString != null) {
return this.parser.parseExpression(expressionString);
}
return null;
}
private String getExpressionString(String key, Locale locale) {
if (this.cacheMillis < 0) {
PropertiesHolder propHolder = getMergedProperties(locale);
String result = propHolder.getProperty(key);
if (result != null) {
return result;
}
}
else {
for (String basename : this.basenames) {
List<String> filenames = calculateAllFilenames(basename, locale);
for (String filename : filenames) {
PropertiesHolder propHolder = getProperties(filename);
String result = propHolder.getProperty(key);
if (result != null) {
return result;
}
}
}
}
return null;
}
/**
* Get a PropertiesHolder that contains the actually visible properties
* for a Locale, after merging all specified resource bundles.
* Either fetches the holder from the cache or freshly loads it.
* <p>Only used when caching resource bundle contents forever, i.e.
* with cacheSeconds < 0. Therefore, merged properties are always
* cached forever.
*/
private PropertiesHolder getMergedProperties(Locale locale) {
synchronized (this.cachedMergedProperties) {
PropertiesHolder mergedHolder = this.cachedMergedProperties.get(locale);
if (mergedHolder != null) {
return mergedHolder;
}
Properties mergedProps = new Properties();
mergedHolder = new PropertiesHolder(mergedProps, -1);
for (int i = this.basenames.length - 1; i >= 0; i--) {
List<String> filenames = calculateAllFilenames(this.basenames[i], locale);
for (int j = filenames.size() - 1; j >= 0; j--) {
String filename = filenames.get(j);
PropertiesHolder propHolder = getProperties(filename);
if (propHolder.getProperties() != null) {
mergedProps.putAll(propHolder.getProperties());
}
}
}
this.cachedMergedProperties.put(locale, mergedHolder);
return mergedHolder;
}
}
/**
* Calculate all filenames for the given bundle basename and Locale.
* Will calculate filenames for the given Locale, the system Locale
* (if applicable), and the default file.
* @param basename the basename of the bundle
* @param locale the locale
* @return the List of filenames to check
* @see #setFallbackToSystemLocale
* @see #calculateFilenamesForLocale
*/
private List<String> calculateAllFilenames(String basename, Locale locale) {
synchronized (this.cachedFilenames) {
Map<Locale, List<String>> localeMap = this.cachedFilenames.get(basename);
if (localeMap != null) {
List<String> filenames = localeMap.get(locale);
if (filenames != null) {
return filenames;
}
}
List<String> filenames = new ArrayList<String>(7);
filenames.addAll(calculateFilenamesForLocale(basename, locale));
if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) {
List<String> fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
for (String fallbackFilename : fallbackFilenames) {
if (!filenames.contains(fallbackFilename)) {
// Entry for fallback locale that isn't already in filenames list.
filenames.add(fallbackFilename);
}
}
}
filenames.add(basename);
if (localeMap != null) {
localeMap.put(locale, filenames);
}
else {
localeMap = new HashMap<Locale, List<String>>();
localeMap.put(locale, filenames);
this.cachedFilenames.put(basename, localeMap);
}
return filenames;
}
}
/**
* Calculate the filenames for the given bundle basename and Locale,
* appending language code, country code, and variant code.
* E.g.: basename "expressions", Locale "de_AT_oo" -> "expressions_de_AT_OO",
* "expressions_de_AT", "expressions_de".
* <p>Follows the rules defined by {@link java.util.Locale#toString()}.
* @param basename the basename of the bundle
* @param locale the locale
* @return the List of filenames to check
*/
private List<String> calculateFilenamesForLocale(String basename, Locale locale) {
List<String> result = new ArrayList<String>(3);
String language = locale.getLanguage();
String country = locale.getCountry();
String variant = locale.getVariant();
StringBuilder temp = new StringBuilder(basename);
temp.append('_');
if (language.length() > 0) {
temp.append(language);
result.add(0, temp.toString());
}
temp.append('_');
if (country.length() > 0) {
temp.append(country);
result.add(0, temp.toString());
}
if (variant.length() > 0 && (language.length() > 0 || country.length() > 0)) {
temp.append('_').append(variant);
result.add(0, temp.toString());
}
return result;
}
/**
* Get a PropertiesHolder for the given filename, either from the
* cache or freshly loaded.
* @param filename the bundle filename (basename + Locale)
* @return the current PropertiesHolder for the bundle
*/
private PropertiesHolder getProperties(String filename) {
synchronized (this.cachedProperties) {
PropertiesHolder propHolder = this.cachedProperties.get(filename);
if (propHolder != null &&
(propHolder.getRefreshTimestamp() < 0 ||
propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) {
return propHolder;
}
return refreshProperties(filename, propHolder);
}
}
/**
* Refresh the PropertiesHolder for the given bundle filename.
* The holder can be <code>null</code> if not cached before, or a timed-out cache entry
* (potentially getting re-validated against the current last-modified timestamp).
* @param filename the bundle filename (basename + Locale)
* @param propHolder the current PropertiesHolder for the bundle
*/
private PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
long refreshTimestamp = (this.cacheMillis < 0) ? -1 : System.currentTimeMillis();
Resource resource = this.resourceLoader.getResource(filename + PROPERTIES_SUFFIX);
if (!resource.exists()) {
resource = this.resourceLoader.getResource(filename + XML_SUFFIX);
}
if (resource.exists()) {
long fileTimestamp = -1;
if (this.cacheMillis >= 0) {
// Last-modified timestamp of file will just be read if caching with timeout.
try {
fileTimestamp = resource.lastModified();
if (propHolder != null && propHolder.getFileTimestamp() == fileTimestamp) {
if (logger.isDebugEnabled()) {
logger.debug("Re-caching properties for filename [" + filename + "] - file hasn't been modified");
}
propHolder.setRefreshTimestamp(refreshTimestamp);
return propHolder;
}
}
catch (IOException ex) {
// Probably a class path resource: cache it forever.
if (logger.isDebugEnabled()) {
logger.debug(
resource + " could not be resolved in the file system - assuming that is hasn't changed", ex);
}
fileTimestamp = -1;
}
}
try {
Properties props = loadProperties(resource, filename);
propHolder = new PropertiesHolder(props, fileTimestamp);
}
catch (IOException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Could not parse properties file [" + resource.getFilename() + "]", ex);
}
// Empty holder representing "not valid".
propHolder = new PropertiesHolder();
}
}
else {
// Resource does not exist.
if (logger.isDebugEnabled()) {
logger.debug("No properties file found for [" + filename + "] - neither plain properties nor XML");
}
// Empty holder representing "not found".
propHolder = new PropertiesHolder();
}
propHolder.setRefreshTimestamp(refreshTimestamp);
this.cachedProperties.put(filename, propHolder);
return propHolder;
}
/**
* Load the properties from the given resource.
* @param resource the resource to load from
* @param filename the original bundle filename (basename + Locale)
* @return the populated Properties instance
* @throws IOException if properties loading failed
*/
private Properties loadProperties(Resource resource, String filename) throws IOException {
InputStream is = resource.getInputStream();
Properties props = new Properties();
try {
if (resource.getFilename().endsWith(XML_SUFFIX)) {
if (logger.isDebugEnabled()) {
logger.debug("Loading properties [" + resource.getFilename() + "]");
}
this.propertiesPersister.loadFromXml(props, is);
}
else {
String encoding = null;
if (this.fileEncodings != null) {
encoding = this.fileEncodings.getProperty(filename);
}
if (encoding == null) {
encoding = this.defaultEncoding;
}
if (encoding != null) {
if (logger.isDebugEnabled()) {
logger.debug("Loading properties [" + resource.getFilename() + "] with encoding '" + encoding + "'");
}
this.propertiesPersister.load(props, new InputStreamReader(is, encoding));
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Loading properties [" + resource.getFilename() + "]");
}
this.propertiesPersister.load(props, is);
}
}
return props;
}
finally {
is.close();
}
}
/**
* Clear the resource bundle cache.
* Subsequent resolve calls will lead to reloading of the properties files.
*/
public void clearCache() {
logger.debug("Clearing entire resource bundle cache");
synchronized (this.cachedProperties) {
this.cachedProperties.clear();
}
synchronized (this.cachedMergedProperties) {
this.cachedMergedProperties.clear();
}
}
@Override
public String toString() {
return getClass().getName() + ": basenames=[" + StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
}
/**
* PropertiesHolder for caching.
* Stores the last-modified timestamp of the source file for efficient
* change detection, and the timestamp of the last refresh attempt
* (updated every time the cache entry gets re-validated).
*/
private class PropertiesHolder {
private Properties properties;
private long fileTimestamp = -1;
private long refreshTimestamp = -1;
public PropertiesHolder(Properties properties, long fileTimestamp) {
this.properties = properties;
this.fileTimestamp = fileTimestamp;
}
public PropertiesHolder() {
}
public Properties getProperties() {
return properties;
}
public long getFileTimestamp() {
return fileTimestamp;
}
public void setRefreshTimestamp(long refreshTimestamp) {
this.refreshTimestamp = refreshTimestamp;
}
public long getRefreshTimestamp() {
return refreshTimestamp;
}
public String getProperty(String code) {
if (this.properties == null) {
return null;
}
return this.properties.getProperty(code);
}
}
}

View File

@@ -16,6 +16,10 @@
package org.springframework.integration.filter;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
@@ -28,7 +32,14 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
*/
public class ExpressionEvaluatingSelector extends AbstractMessageProcessingSelector {
public ExpressionEvaluatingSelector(String expression) {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
public ExpressionEvaluatingSelector(String expressionString) {
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expressionParser.parseExpression(expressionString), Boolean.class));
}
public ExpressionEvaluatingSelector(Expression expression) {
super(new ExpressionEvaluatingMessageProcessor<Boolean>(expression, Boolean.class));
}

View File

@@ -100,14 +100,24 @@ public class MessageFilter extends AbstractReplyProducingMessageHandler {
@Override
protected Object handleRequestMessage(Message<?> message) {
if (this.selector.accept(message)) {
return message;
}
Throwable filterException = null;
try {
if (this.selector.accept(message)) {
return message;
}
} catch (Exception e) {
filterException = e;
}
if (this.discardChannel != null) {
this.getMessagingTemplate().send(this.discardChannel, message);
}
if (this.throwExceptionOnRejection) {
throw new MessageRejectedException(message);
if (filterException != null){
throw new MessageRejectedException(message, "MessageFilter '" + this.getComponentName() + "' rejected Message", filterException);
}
else {
throw new MessageRejectedException(message, "MessageFilter '" + this.getComponentName() + "' rejected Message");
}
}
return null;
}

View File

@@ -31,6 +31,7 @@ import org.springframework.integration.MessageHeaders;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.store.MessageStore;
@@ -70,7 +71,7 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @since 1.0.3
*/
public class DelayHandler extends IntegrationObjectSupport implements MessageHandler, Ordered, DisposableBean {
public class DelayHandler extends IntegrationObjectSupport implements MessageHandler, MessageProducer, Ordered, DisposableBean {
private final Log logger = LogFactory.getLog(this.getClass());

View File

@@ -18,10 +18,7 @@ package org.springframework.integration.handler;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.ParseException;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.util.Assert;
@@ -34,25 +31,27 @@ import org.springframework.util.Assert;
*/
public class ExpressionEvaluatingMessageProcessor<T> extends AbstractMessageProcessor<T> {
private final ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final Expression expression;
private final Class<T> expectedType;
public ExpressionEvaluatingMessageProcessor(String expression) {
/**
* Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression.
*/
public ExpressionEvaluatingMessageProcessor(Expression expression) {
this(expression, null);
}
/**
* Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression String.
* Create an {@link ExpressionEvaluatingMessageProcessor} for the given expression
* and expected type for its evaluation result.
*/
public ExpressionEvaluatingMessageProcessor(String expression, Class<T> expectedType) {
Assert.hasLength(expression, "The expression must be non empty");
public ExpressionEvaluatingMessageProcessor(Expression expression, Class<T> expectedType) {
Assert.notNull(expression, "The expression must not be null");
try {
this.expression = parser.parseExpression(expression);
this.expression = expression;
this.getEvaluationContext().addPropertyAccessor(new MapAccessor());
this.expectedType = expectedType;
}

View File

@@ -1,23 +1,21 @@
/*
* 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.
*
* 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.handler;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.EvaluationContext;
@@ -25,20 +23,22 @@ import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.Message;
import org.springframework.integration.dispatcher.AggregateMessageDeliveryException;
import org.springframework.util.StringUtils;
/**
* MessageHandler implementation that simply logs the Message or its payload
* depending on the value of the 'shouldLogFullMessage' property. If logging
* the payload, and it is assignable to Throwable, it will log the stack trace.
* By default, it will log the payload only.
* MessageHandler implementation that simply logs the Message or its payload depending on the value of the
* 'shouldLogFullMessage' property. If logging the payload, and it is assignable to Throwable, it will log the stack
* trace. By default, it will log the payload only.
*
* @author Mark Fisher
* @since 1.0.1
*/
public class LoggingHandler extends AbstractMessageHandler {
private static enum Level { FATAL, ERROR, WARN, INFO, DEBUG, TRACE }
private static enum Level {
FATAL, ERROR, WARN, INFO, DEBUG, TRACE
}
private static final SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
@@ -48,18 +48,18 @@ public class LoggingHandler extends AbstractMessageHandler {
private final EvaluationContext evaluationContext;
/**
* Create a LoggingHandler with the given log level (case-insensitive).
* <p>The valid levels are: FATAL, ERROR, WARN, INFO, DEBUG, or TRACE
* <p>
* The valid levels are: FATAL, ERROR, WARN, INFO, DEBUG, or TRACE
*/
public LoggingHandler(String level) {
try {
this.level = Level.valueOf(level.toUpperCase());
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid log level '" + level +
"'. The (case-insensitive) supported values are: " + StringUtils.arrayToCommaDelimitedString(Level.values()));
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid log level '" + level
+ "'. The (case-insensitive) supported values are: "
+ StringUtils.arrayToCommaDelimitedString(Level.values()));
}
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
evaluationContext.addPropertyAccessor(new MapAccessor());
@@ -67,18 +67,17 @@ public class LoggingHandler extends AbstractMessageHandler {
this.expression = EXPRESSION_PARSER.parseExpression("payload");
}
public void setExpression(String expressionString) {
this.expression = EXPRESSION_PARSER.parseExpression(expressionString);
}
/**
* Specify whether to log the full Message. Otherwise, only the payload
* will be logged. This value is <code>false</code> by default.
* Specify whether to log the full Message. Otherwise, only the payload will be logged. This value is
* <code>false</code> by default.
*/
public void setShouldLogFullMessage(boolean shouldLogFullMessage) {
this.expression = (shouldLogFullMessage) ? EXPRESSION_PARSER.parseExpression("#root")
: EXPRESSION_PARSER.parseExpression("payload");
this.expression = (shouldLogFullMessage) ? EXPRESSION_PARSER.parseExpression("#root") : EXPRESSION_PARSER
.parseExpression("payload");
}
@Override
@@ -91,40 +90,47 @@ public class LoggingHandler extends AbstractMessageHandler {
Object logMessage = this.expression.getValue(this.evaluationContext, message);
if (logMessage instanceof Throwable) {
StringWriter stringWriter = new StringWriter();
((Throwable) logMessage).printStackTrace(new PrintWriter(stringWriter, true));
if (logMessage instanceof AggregateMessageDeliveryException) {
stringWriter.append(((Throwable) logMessage).getMessage());
for (Exception exception : (List<? extends Exception>) ((AggregateMessageDeliveryException)logMessage).getAggregatedExceptions()) {
exception.printStackTrace(new PrintWriter(stringWriter, true));
}
} else {
((Throwable) logMessage).printStackTrace(new PrintWriter(stringWriter, true));
}
logMessage = stringWriter.toString();
}
switch (this.level) {
case FATAL :
if (logger.isFatalEnabled()) {
logger.fatal(logMessage);
}
break;
case ERROR :
if (logger.isErrorEnabled()) {
logger.error(logMessage);
}
break;
case WARN :
if (logger.isWarnEnabled()) {
logger.warn(logMessage);
}
break;
case INFO :
if (logger.isInfoEnabled()) {
logger.info(logMessage);
}
break;
case DEBUG :
if (logger.isDebugEnabled()) {
logger.debug(logMessage);
}
break;
case TRACE :
if (logger.isTraceEnabled()) {
logger.trace(logMessage);
}
break;
case FATAL:
if (logger.isFatalEnabled()) {
logger.fatal(logMessage);
}
break;
case ERROR:
if (logger.isErrorEnabled()) {
logger.error(logMessage);
}
break;
case WARN:
if (logger.isWarnEnabled()) {
logger.warn(logMessage);
}
break;
case INFO:
if (logger.isInfoEnabled()) {
logger.info(logMessage);
}
break;
case DEBUG:
if (logger.isDebugEnabled()) {
logger.debug(logMessage);
}
break;
case TRACE:
if (logger.isTraceEnabled()) {
logger.trace(logMessage);
}
break;
}
}

View File

@@ -1,190 +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.router;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessagingException;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolutionException;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A base class for router implementations that return only the channel name(s)
* rather than {@link MessageChannel} instances.
*
* @author Mark Fisher
* @author Jonas Partner
*/
public abstract class AbstractChannelNameResolvingMessageRouter extends AbstractMessageRouter {
private volatile String prefix;
private volatile String suffix;
private volatile ChannelResolver channelResolver;
private volatile boolean ignoreChannelNameResolutionFailures;
/**
* Specify the {@link ChannelResolver} strategy to use.
* The default is a BeanFactoryChannelResolver.
*/
public void setChannelResolver(ChannelResolver channelResolver) {
Assert.notNull(channelResolver, "'channelResolver' must not be null");
this.channelResolver = channelResolver;
}
/**
* Specify a prefix to be added to each channel name prior to resolution.
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* Specify a suffix to be added to each channel name prior to resolution.
*/
public void setSuffix(String suffix) {
this.suffix = suffix;
}
/**
* Specify whether this router should ignore any failure to resolve a channel name to
* an actual MessageChannel instance when delegating to the ChannelResolver strategy.
*/
public void setIgnoreChannelNameResolutionFailures(boolean ignoreChannelNameResolutionFailures) {
this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures;
}
@Override
public void onInit() {
BeanFactory beanFactory = this.getBeanFactory();
if (this.channelResolver == null && beanFactory != null) {
this.channelResolver = new BeanFactoryChannelResolver(beanFactory);
}
}
private MessageChannel resolveChannelForName(String channelName, Message<?> message) {
Assert.state(this.channelResolver != null,
"unable to resolve channel names, no ChannelResolver available");
MessageChannel channel = null;
try {
channel = this.channelResolver.resolveChannelName(channelName);
}
catch (ChannelResolutionException e) {
if (!this.ignoreChannelNameResolutionFailures) {
throw new MessagingException(message,
"failed to resolve channel name '" + channelName + "'", e);
}
}
if (channel == null && !this.ignoreChannelNameResolutionFailures) {
throw new MessagingException(message,
"failed to resolve channel name '" + channelName + "'");
}
return channel;
}
@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
this.afterPropertiesSet();
Collection<MessageChannel> channels = new ArrayList<MessageChannel>();
Collection<Object> channelsReturned = this.getChannelIndicatorList(message);
addToCollection(channels, channelsReturned, message);
return channels;
}
@SuppressWarnings("unchecked")
private void addToCollection(Collection<MessageChannel> channels, Collection<?> channelIndicators, Message<?> message) {
if (channelIndicators == null) {
return;
}
for (Object channelIndicator : channelIndicators) {
if (channelIndicator == null) {
continue;
}
else if (channelIndicator instanceof MessageChannel) {
channels.add((MessageChannel) channelIndicator);
}
else if (channelIndicator instanceof MessageChannel[]) {
channels.addAll(Arrays.asList((MessageChannel[]) channelIndicator));
}
else if (channelIndicator instanceof String) {
addChannelFromString(channels, (String) channelIndicator, message);
}
else if (channelIndicator instanceof String[]) {
for (String indicatorName : (String[]) channelIndicator) {
addChannelFromString(channels, indicatorName, message);
}
}
else if (channelIndicator instanceof Collection) {
addToCollection(channels, (Collection<?>) channelIndicator, message);
}
else if (this.getRequiredConversionService().canConvert(channelIndicator.getClass(), String.class)) {
addChannelFromString(channels,
this.getConversionService().convert(channelIndicator, String.class), message);
}
else {
throw new MessagingException(
"unsupported return type for router [" + channelIndicator.getClass() + "]");
}
}
}
private void addChannelFromString(Collection<MessageChannel> channels, String channelName, Message<?> message) {
if (channelName.indexOf(',') != -1) {
for (String name : StringUtils.commaDelimitedListToStringArray(channelName)) {
addChannelFromString(channels, name, message);
}
return;
}
if (this.prefix != null) {
channelName = this.prefix + channelName;
}
if (this.suffix != null) {
channelName = channelName + suffix;
}
MessageChannel channel = resolveChannelForName(channelName, message);
if (channel != null) {
channels.add(channel);
}
}
private ConversionService getRequiredConversionService() {
if (this.getConversionService() == null) {
this.setConversionService(ConversionServiceFactory.createDefaultConversionService());
}
return this.getConversionService();
}
/**
* Subclasses must implement this method to return the channel indicators.
*/
protected abstract List<Object> getChannelIndicatorList(Message<?> message);
}

View File

@@ -32,7 +32,7 @@ import org.springframework.util.Assert;
* @author Mark Fisher
* @since 2.0
*/
class AbstractMessageProcessingRouter extends AbstractChannelNameResolvingMessageRouter {
class AbstractMessageProcessingRouter extends AbstractMessageRouter {
private final MessageProcessor<Object> messageProcessor;

View File

@@ -16,8 +16,16 @@
package org.springframework.integration.router;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageDeliveryException;
@@ -25,11 +33,18 @@ import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.AbstractMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolutionException;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* Base class for Message Routers.
* Base class for all Message Routers.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public abstract class AbstractMessageRouter extends AbstractMessageHandler {
@@ -42,6 +57,67 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler {
private volatile boolean applySequence;
private final MessagingTemplate messagingTemplate = new MessagingTemplate();
private volatile String prefix;
private volatile String suffix;
private volatile ChannelResolver channelResolver;
private volatile boolean ignoreChannelNameResolutionFailures;
protected volatile Map<String, String> channelIdentifierMap = new ConcurrentHashMap<String, String>();
/**
* Specify the {@link ChannelResolver} strategy to use.
* The default is a BeanFactoryChannelResolver.
*/
public void setChannelResolver(ChannelResolver channelResolver) {
Assert.notNull(channelResolver, "'channelResolver' must not be null");
this.channelResolver = channelResolver;
}
/**
* Specify a prefix to be added to each channel name prior to resolution.
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* Specify a suffix to be added to each channel name prior to resolution.
*/
public void setSuffix(String suffix) {
this.suffix = suffix;
}
/**
* Specify whether this router should ignore any failure to resolve a channel name to
* an actual MessageChannel instance when delegating to the ChannelResolver strategy.
*/
public void setIgnoreChannelNameResolutionFailures(boolean ignoreChannelNameResolutionFailures) {
this.ignoreChannelNameResolutionFailures = ignoreChannelNameResolutionFailures;
}
/**
* Allows you to set the map which will map channel identifiers to channel names.
* Channel names will be resolve via {@link ChannelResolver}
* @param channelIdentifierMap
*/
public void setChannelIdentifierMap(Map<String, String> channelIdentifierMap) {
this.channelIdentifierMap.clear();
this.channelIdentifierMap.putAll(channelIdentifierMap);
}
public void setChannelMapping(String channelIdentifier, String channelName){
this.channelIdentifierMap.put(channelIdentifier, channelName);
}
/**
* Removes channel mapping for a give channel identifier
* @param channelIdentifier
*/
public void removeChannelMapping(String channelIdentifier){
this.channelIdentifierMap.remove(channelIdentifier);
}
/**
* Set the default channel where Messages should be sent if channel resolution fails to return any channels. If no
@@ -99,6 +175,33 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler {
protected MessagingTemplate getMessagingTemplate() {
return this.messagingTemplate;
}
@Override
public void onInit() {
BeanFactory beanFactory = this.getBeanFactory();
if (this.channelResolver == null && beanFactory != null) {
this.channelResolver = new BeanFactoryChannelResolver(beanFactory);
}
}
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
this.afterPropertiesSet();
Collection<MessageChannel> channels = new ArrayList<MessageChannel>();
Collection<Object> channelsReturned = this.getChannelIndicatorList(message);
addToCollection(channels, channelsReturned, message);
return channels;
}
protected ConversionService getRequiredConversionService() {
if (this.getConversionService() == null) {
this.setConversionService(ConversionServiceFactory.createDefaultConversionService());
}
return this.getConversionService();
}
/**
* Subclasses must implement this method to return the channel indicators.
*/
protected abstract List<Object> getChannelIndicatorList(Message<?> message);
@Override
protected void handleMessageInternal(Message<?> message) {
@@ -137,9 +240,89 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler {
}
}
/**
* Subclasses must implement this method to return the target channels for a given Message.
*/
protected abstract Collection<MessageChannel> determineTargetChannels(Message<?> message);
private MessageChannel resolveChannelForName(String channelName, Message<?> message) {
Assert.state(this.channelResolver != null,
"unable to resolve channel names, no ChannelResolver available");
MessageChannel channel = null;
try {
channel = this.channelResolver.resolveChannelName(channelName);
}
catch (ChannelResolutionException e) {
if (!this.ignoreChannelNameResolutionFailures) {
throw new MessagingException(message,
"failed to resolve channel name '" + channelName + "'", e);
}
}
if (channel == null && !this.ignoreChannelNameResolutionFailures) {
throw new MessagingException(message,
"failed to resolve channel name '" + channelName + "'");
}
return channel;
}
private void addChannelFromString(Collection<MessageChannel> channels, String channelIdentifier, Message<?> message) {
if (channelIdentifier.indexOf(',') != -1) {
for (String name : StringUtils.commaDelimitedListToStringArray(channelIdentifier)) {
addChannelFromString(channels, name, message);
}
return;
}
if (this.prefix != null) {
channelIdentifier = this.prefix + channelIdentifier;
}
if (this.suffix != null) {
channelIdentifier = channelIdentifier + suffix;
}
/*
* Some routers due to their complex nature will already resolve 'channelIdentifier'
* to 'channelName' (e.g., PTR, EMETR)
*/
String channelName = channelIdentifier;
if (!CollectionUtils.isEmpty(channelIdentifierMap) && channelIdentifierMap.containsKey(channelIdentifier)){
channelName = channelIdentifierMap.get(channelIdentifier);
}
if (this.channelResolver != null){
MessageChannel channel = resolveChannelForName(channelName, message);
if (channel != null) {
channels.add(channel);
}
}
}
private void addToCollection(Collection<MessageChannel> channels, Collection<?> channelIndicators, Message<?> message) {
if (channelIndicators == null) {
return;
}
for (Object channelIndicator : channelIndicators) {
if (channelIndicator == null) {
continue;
}
else if (channelIndicator instanceof MessageChannel) {
channels.add((MessageChannel) channelIndicator);
}
else if (channelIndicator instanceof MessageChannel[]) {
channels.addAll(Arrays.asList((MessageChannel[]) channelIndicator));
}
else if (channelIndicator instanceof String) {
addChannelFromString(channels, (String) channelIndicator, message);
}
else if (channelIndicator instanceof String[]) {
for (String indicatorName : (String[]) channelIndicator) {
addChannelFromString(channels, indicatorName, message);
}
}
else if (channelIndicator instanceof Collection) {
addToCollection(channels, (Collection<?>) channelIndicator, message);
}
else if (this.getRequiredConversionService().canConvert(channelIndicator.getClass(), String.class)) {
addChannelFromString(channels,
this.getConversionService().convert(channelIndicator, String.class), message);
}
else {
throw new MessagingException(
"unsupported return type for router [" + channelIndicator.getClass() + "]");
}
}
}
}

View File

@@ -27,7 +27,7 @@ import org.springframework.integration.Message;
*
* @author Mark Fisher
*/
public abstract class AbstractSingleChannelNameRouter extends AbstractChannelNameResolvingMessageRouter {
public abstract class AbstractSingleChannelNameRouter extends AbstractMessageRouter {
@Override
protected final List<Object> getChannelIndicatorList(Message<?> message) {

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2002-2008 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.router;
import java.util.Collection;
import java.util.Collections;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
/**
* Extends {@link AbstractMessageRouter} to support router implementations that
* always return a single {@link MessageChannel} instance (or null).
*
* @author Mark Fisher
*/
public abstract class AbstractSingleChannelRouter extends AbstractMessageRouter {
@Override
protected final Collection<MessageChannel> determineTargetChannels(Message<?> message) {
MessageChannel channel = this.determineTargetChannel(message);
return (channel != null) ? Collections.singletonList(channel) : null;
}
/**
* Subclasses must implement this method to return the target channel.
*/
protected abstract MessageChannel determineTargetChannel(Message<?> message);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -16,12 +16,11 @@
package org.springframework.integration.router;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;
import java.util.List;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.util.Assert;
/**
* A Message Router that resolves the target {@link MessageChannel} for
@@ -29,34 +28,26 @@ import org.springframework.util.Assert;
* the most specific cause of the error for which a channel-mapping exists.
*
* @author Mark Fisher
*/
public class ErrorMessageExceptionTypeRouter extends AbstractSingleChannelRouter {
private volatile Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new ConcurrentHashMap<Class<? extends Throwable>, MessageChannel>();
public void setExceptionTypeChannelMap(Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap) {
Assert.notNull(exceptionTypeChannelMap, "exceptionTypeChannelMap must not be null");
this.exceptionTypeChannelMap = exceptionTypeChannelMap;
}
* @author Oleg Zhurakousky
*/
public class ErrorMessageExceptionTypeRouter extends AbstractMessageRouter {
@Override
protected MessageChannel determineTargetChannel(Message<?> message) {
MessageChannel channel = null;
protected List<Object> getChannelIndicatorList(Message<?> message) {
String channelName = null;
String channelIdentifier = null;
Object payload = message.getPayload();
if (payload != null && (payload instanceof Throwable)) {
Throwable mostSpecificCause = (Throwable) payload;
while (mostSpecificCause != null) {
MessageChannel mappedChannel = this.exceptionTypeChannelMap.get(mostSpecificCause.getClass());
if (mappedChannel != null) {
channel = mappedChannel;
channelIdentifier = mostSpecificCause.getClass().getName();
if (channelIdentifierMap != null){
String tempChannelName = channelIdentifierMap.get(channelIdentifier);
channelName = tempChannelName == null ? channelName : tempChannelName;
}
mostSpecificCause = mostSpecificCause.getCause();
}
}
return channel;
return Collections.singletonList((Object)channelName);
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.router;
import org.springframework.expression.Expression;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
/**
@@ -28,7 +29,7 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
*/
public class ExpressionEvaluatingRouter extends AbstractMessageProcessingRouter {
public ExpressionEvaluatingRouter(String expression) {
public ExpressionEvaluatingRouter(Expression expression) {
super(new ExpressionEvaluatingMessageProcessor<Object>(expression));
}

View File

@@ -30,7 +30,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @since 1.0.3
*/
public class HeaderValueRouter extends AbstractChannelNameResolvingMessageRouter {
public class HeaderValueRouter extends AbstractMessageRouter {
private final String headerName;

View File

@@ -16,39 +16,67 @@
package org.springframework.integration.router;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.util.ClassUtils;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
/**
* A Message Router that resolves the {@link MessageChannel} based on the
* {@link Message Message's} payload type.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class PayloadTypeRouter extends AbstractSingleChannelRouter {
private volatile Map<Class<?>, MessageChannel> payloadTypeChannelMap =
new ConcurrentHashMap<Class<?>, MessageChannel>();
public void setPayloadTypeChannelMap(Map<Class<?>, MessageChannel> payloadTypeChannelMap) {
Assert.notNull(payloadTypeChannelMap, "payloadTypeChannelMap must not be null");
this.payloadTypeChannelMap = payloadTypeChannelMap;
}
public class PayloadTypeRouter extends AbstractMessageRouter {
/**
* Will select the most appropriate channel name matching channel identifiers
* which are fully qualifies class name to type available while traversing payload type.
* To resolve ties and conflicts (e.g., Serializable and String) it will match:
* 1. Type name to channel identifier else...
* 2. Name of the subclass of the type to channel identifier elc...
* 3. Name of the Interface of the type to channel identifier while also
* preferring direct interface over in-direct subclass
*
*/
@Override
protected MessageChannel determineTargetChannel(Message<?> message) {
Class<?> closestMatch = ClassUtils.findClosestMatch(
message.getPayload().getClass(), this.payloadTypeChannelMap.keySet(), true);
if (closestMatch != null) {
return this.payloadTypeChannelMap.get(closestMatch);
protected List<Object> getChannelIndicatorList(Message<?> message) {
Class<?> firstInterfaceMatch = null;
Class<?> type = message.getPayload().getClass();
while (type != null && !CollectionUtils.isEmpty(channelIdentifierMap)) {
Class<?>[] interfaces = type.getInterfaces();
// first try to find a match amongst the interfaces and also check if there is more then one
for (Class<?> interfase : interfaces) {
if (channelIdentifierMap.containsKey(interfase.getName())){
if (firstInterfaceMatch != null){
throw new IllegalStateException("Unresolvable ambiguity while attempting to find closest match for [" +
type.getName() + "]. Candidate types [" + firstInterfaceMatch.getName() + "] and [" + interfase.getName() +
"] have equal weight.");
}
else {
firstInterfaceMatch = interfase;
}
}
}
// the actual type should favor the possible interface match
String channelName = channelIdentifierMap.get(type.getName());
if (!StringUtils.hasText(channelName)){
if (firstInterfaceMatch != null){
return Collections.singletonList((Object)channelIdentifierMap.get(firstInterfaceMatch.getName()));
}
}
else {
return Collections.singletonList((Object)channelName);
}
type = type.getSuperclass();
}
return null;
}
}

View File

@@ -17,8 +17,8 @@
package org.springframework.integration.router;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
@@ -51,6 +51,7 @@ import org.springframework.util.Assert;
* solution.
*
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class RecipientListRouter extends AbstractMessageRouter implements InitializingBean {
@@ -88,10 +89,10 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
public final void onInit() {
Assert.notEmpty(this.recipients, "a non-empty recipient list is required");
}
@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
List<MessageChannel> channels = new ArrayList<MessageChannel>();
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
List<Object> channels = new ArrayList<Object>();
List<Recipient> recipientList = this.recipients;
for (Recipient recipient : recipientList) {
if (recipient.accept(message)) {
@@ -100,8 +101,7 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
}
return channels;
}
public static class Recipient {
private final MessageChannel channel;
@@ -125,5 +125,4 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia
return this.channel;
}
}
}

View File

@@ -17,9 +17,9 @@
package org.springframework.integration.scheduling;
import java.util.List;
import java.util.concurrent.Executor;
import org.aopalliance.aop.Advice;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.PeriodicTrigger;
@@ -39,7 +39,7 @@ public class PollerMetadata {
private List<Advice> adviceChain;
private volatile TaskExecutor taskExecutor;
private volatile Executor taskExecutor;
public void setTrigger(Trigger trigger) {
this.trigger = trigger;
@@ -82,11 +82,11 @@ public class PollerMetadata {
return this.adviceChain;
}
public void setTaskExecutor(TaskExecutor taskExecutor) {
public void setTaskExecutor(Executor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public TaskExecutor getTaskExecutor() {
public Executor getTaskExecutor() {
return this.taskExecutor;
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.integration.splitter;
import java.util.Collection;
import org.springframework.expression.Expression;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
/**
@@ -32,7 +33,7 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
public class ExpressionEvaluatingSplitter extends AbstractMessageProcessingSplitter {
@SuppressWarnings({"unchecked", "rawtypes"})
public ExpressionEvaluatingSplitter(String expression) {
public ExpressionEvaluatingSplitter(Expression expression) {
super(new ExpressionEvaluatingMessageProcessor(expression, Collection.class));
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.transformer;
import org.springframework.expression.Expression;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
/**
@@ -28,7 +29,7 @@ import org.springframework.integration.handler.ExpressionEvaluatingMessageProces
*/
public class ExpressionEvaluatingTransformer extends AbstractMessageProcessingTransformer {
public ExpressionEvaluatingTransformer(String expression) {
public ExpressionEvaluatingTransformer(Expression expression) {
super(new ExpressionEvaluatingMessageProcessor<Object>(expression));
}

View File

@@ -21,9 +21,12 @@ import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
@@ -168,15 +171,25 @@ public class HeaderEnricher implements Transformer {
static class ExpressionEvaluatingHeaderValueMessageProcessor<T> extends AbstractHeaderValueMessageProcessor<T> implements BeanFactoryAware {
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
private final ExpressionEvaluatingMessageProcessor<T> targetProcessor;
/**
* Create a header value processor for the given expression String and the expected type
* Create a header value processor for the given Expression and the expected type
* of the expression evaluation result. The expectedType may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(Expression expression, Class<T> expectedType) {
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
/**
* Create a header value processor for the given expression string and the expected type
* of the expression evaluation result. The expectedType may be null if unknown.
*/
public ExpressionEvaluatingHeaderValueMessageProcessor(String expressionString, Class<T> expectedType) {
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expressionString, expectedType);
//this.targetProcessor.setExpectedType(expectedType);
Expression expression = expressionParser.parseExpression(expressionString);
this.targetProcessor = new ExpressionEvaluatingMessageProcessor<T>(expression, expectedType);
}
public void setBeanFactory(BeanFactory beanFactory) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -16,17 +16,25 @@
package org.springframework.integration.util;
import java.beans.PropertyEditor;
import java.util.Collection;
import org.springframework.beans.BeansException;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.ConversionServiceFactory;
import org.springframework.expression.TypeConverter;
import org.springframework.util.CollectionUtils;
/**
*
* @author Dave Syer
* @author Oleg Zhurakousky
*
*/
public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware {
private SimpleTypeConverter delegate = new SimpleTypeConverter();
@@ -89,13 +97,21 @@ public class BeanFactoryTypeConverter implements TypeConverter, BeanFactoryAware
if (targetType.getType() == Void.class || targetType.getType() == Void.TYPE) {
return null;
}
if (value instanceof Collection<?>
&& CollectionUtils.isEmpty((Collection<?>) value)
&& Collection.class.isAssignableFrom(targetType.getObjectType())){
return value;
}
if (conversionService.canConvert(sourceType, targetType)) {
return conversionService.convert(value, sourceType, targetType);
}
if (!String.class.isAssignableFrom(sourceType.getType())) {
PropertyEditor editor = delegate.findCustomEditor(sourceType.getType(), null);
editor.setValue(value);
return editor.getAsText();
if (editor != null){ // INT-1441
editor.setValue(value);
return editor.getAsText();
}
}
return delegate.convertIfNecessary(value, targetType.getType());
}

View File

@@ -1388,6 +1388,9 @@
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="referenceHeaderType">
<xsd:sequence minOccurs="0" maxOccurs="1">
<xsd:element name="expression" type="innerExpressionType"/>
</xsd:sequence>
<xsd:attribute name="value" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -1929,6 +1932,7 @@ Name of the header whose value to use.
<xsd:extension base="channelNameResolvingRouterType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1" />
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="ref" type="xsd:string">
@@ -2357,7 +2361,12 @@ Name of the header whose value to use.
<xsd:complexType name="expressionOrInnerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="innerEndpointDefinitionAware">
<xsd:extension base="handlerEndpointType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element name="poller" type="innerPollerType" minOccurs="0" maxOccurs="1" />
<xsd:element name="expression" type="innerExpressionType" minOccurs="0" maxOccurs="1"/>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="1" />
</xsd:choice>
<xsd:attribute name="expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
@@ -2369,6 +2378,28 @@ Name of the header whose value to use.
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="innerExpressionType">
<xsd:attribute name="key" type="xsd:string" use="required">
<xsd:annotation>
<xsd:documentation>
The key for retrieving the expression from an ExpressionSource.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="source" type="xsd:string" default="expressionSource">
<xsd:annotation>
<xsd:documentation>
The reference to an ExpressionSource.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.integration.expression.ExpressionSource" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="innerEndpointDefinitionAware">
<xsd:complexContent>
<xsd:extension base="handlerEndpointType">

View File

@@ -1,20 +1,36 @@
/*
* 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.aggregator;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.message.GenericMessage;
/**
* @author Alex Peters
*
*/
public class ExpressionEvaluatingCorrelationStrategyTests {
private ExpressionEvaluatingCorrelationStrategy strategy;
@Test(expected = IllegalArgumentException.class)
public void testCreateInstanceWithEmptyExpressionFails() throws Exception {
strategy = new ExpressionEvaluatingCorrelationStrategy("");
@@ -22,12 +38,15 @@ public class ExpressionEvaluatingCorrelationStrategyTests {
@Test(expected = IllegalArgumentException.class)
public void testCreateInstanceWithNullExpressionFails() throws Exception {
strategy = new ExpressionEvaluatingCorrelationStrategy(null);
Expression nullExpression = null;
strategy = new ExpressionEvaluatingCorrelationStrategy(nullExpression);
}
@Test
public void testCorrelationKeyWithMethodInvokingExpression() throws Exception {
strategy = new ExpressionEvaluatingCorrelationStrategy("payload.substring(0,1)");
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
Expression expression = parser.parseExpression("payload.substring(0,1)");
strategy = new ExpressionEvaluatingCorrelationStrategy(expression);
Object correlationKey = strategy.getCorrelationKey(new GenericMessage<String>("bla"));
assertThat(correlationKey, is(String.class));
assertThat((String) correlationKey, is("b"));

View File

@@ -20,17 +20,17 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.channel.MapBasedChannelResolver;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolver;
/**
* @author Mark Fisher
@@ -39,14 +39,16 @@ import org.springframework.integration.channel.QueueChannel;
*/
public class MessagePublishingInterceptorTests {
private final MapBasedChannelResolver channelResolver = new MapBasedChannelResolver();
private ChannelResolver channelResolver;
private final QueueChannel testChannel = new QueueChannel();
@Before
public void setup() {
channelResolver.setChannelMap(Collections.singletonMap("c", testChannel));
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
channelResolver = new BeanFactoryChannelResolver(beanFactory);
beanFactory.registerSingleton("c", testChannel);
}
@Test

View File

@@ -37,19 +37,19 @@
</property>
<property name="channelMap">
<map>
<entry key="setName" value="channel" />
<entry key="setName" value="messagePublishingInterceptorUsageTestChannel" />
</map>
</property>
</bean>
</constructor-arg>
<property name="channelResolver">
<bean
class="org.springframework.integration.channel.MapBasedChannelResolver">
<property name="channelMap">
<map>
<entry key="channel" value-ref="messagePublishingInterceptorUsageTestChannel" />
</map>
</property>
class="org.springframework.integration.support.channel.BeanFactoryChannelResolver">
<!-- <property name="channelMap">-->
<!-- <map>-->
<!-- <entry key="channel" value-ref="messagePublishingInterceptorUsageTestChannel" />-->
<!-- </map>-->
<!-- </property>-->
</bean>
</property>
</bean>

View File

@@ -1,73 +0,0 @@
/*
* Copyright 2002-2008 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.channel;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.integration.MessageChannel;
/**
* @author Mark Fisher
*/
public class MapBasedChannelResolverTests {
@Test
public void mapContainsChannel() {
MessageChannel testChannel = new QueueChannel();
Map<String, MessageChannel> channelMap = new HashMap<String, MessageChannel>();
channelMap.put("testChannel", testChannel);
MapBasedChannelResolver resolver = new MapBasedChannelResolver();
resolver.setChannelMap(channelMap);
MessageChannel result = resolver.resolveChannelName("testChannel");
assertNotNull(result);
assertEquals(testChannel, result);
}
@Test
public void mapDoesNotContainChannel() {
MessageChannel testChannel = new QueueChannel();
Map<String, MessageChannel> channelMap = new HashMap<String, MessageChannel>();
channelMap.put("testChannel", testChannel);
MapBasedChannelResolver resolver = new MapBasedChannelResolver();
resolver.setChannelMap(channelMap);
MessageChannel result = resolver.resolveChannelName("noSuchChannel");
assertNull(result);
}
@Test
public void emptyMap() {
Map<String, MessageChannel> channelMap = new HashMap<String, MessageChannel>();
MapBasedChannelResolver resolver = new MapBasedChannelResolver();
resolver.setChannelMap(channelMap);
MessageChannel result = resolver.resolveChannelName("testChannel");
assertNull(result);
}
@Test(expected = IllegalArgumentException.class)
public void nullMapRejected() {
MapBasedChannelResolver resolver = new MapBasedChannelResolver();
resolver.setChannelMap(null);
}
}

View File

@@ -19,9 +19,9 @@
<bean id="payloadTypeRouter" class="org.springframework.integration.router.PayloadTypeRouter">
<property name="resolutionRequired" value="true"/>
<property name="payloadTypeChannelMap">
<property name="channelIdentifierMap">
<map>
<entry key="java.lang.String" value-ref="strings"/>
<entry key="java.lang.String" value="strings"/>
</map>
</property>
</bean>

View File

@@ -31,12 +31,12 @@ import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.MapBasedChannelResolver;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
@@ -45,7 +45,9 @@ import org.springframework.integration.mapping.OutboundMessageMapper;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolutionException;
import org.springframework.integration.support.channel.ChannelResolver;
import org.springframework.integration.support.converter.SimpleMessageConverter;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.test.util.TestUtils.TestApplicationContext;
@@ -306,15 +308,27 @@ public class MessagingTemplateTests {
@Test
public void sendByChannelNameWithCustomChannelResolver() {
QueueChannel testChannel = new QueueChannel();
Map<String, MessageChannel> channelMap = new HashMap<String, MessageChannel>();
channelMap.put("testChannel", testChannel);
MapBasedChannelResolver channelResolver = new MapBasedChannelResolver(channelMap);
final QueueChannel anotherChannel = new QueueChannel();
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("testChannel", testChannel);
MessagingTemplate template = new MessagingTemplate();
template.setChannelResolver(channelResolver);
template.setBeanFactory(beanFactory);
template.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload("test").build();
template.send("testChannel", message);
assertEquals(message, testChannel.receive(0));
template.setChannelResolver(new ChannelResolver() {
public MessageChannel resolveChannelName(String channelName) {
return anotherChannel;
}
});
message = MessageBuilder.withPayload("test").build();
template.send("testChannel", message);
assertEquals(message, anotherChannel.receive(0));
}
@Test(expected = IllegalStateException.class)
@@ -352,11 +366,11 @@ public class MessagingTemplateTests {
@Test
public void receiveByChannelNameWithCustomChannelResolver() {
QueueChannel testChannel = new QueueChannel();
Map<String, MessageChannel> channelMap = new HashMap<String, MessageChannel>();
channelMap.put("testChannel", testChannel);
MapBasedChannelResolver channelResolver = new MapBasedChannelResolver(channelMap);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("testChannel", testChannel);
MessagingTemplate template = new MessagingTemplate();
template.setChannelResolver(channelResolver);
template.setBeanFactory(beanFactory);
template.afterPropertiesSet();
Message<?> message = MessageBuilder.withPayload("test").build();
testChannel.send(message);

View File

@@ -0,0 +1,122 @@
/*
* 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.endpoint;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static org.easymock.EasyMock.reset;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
import org.springframework.integration.config.TestErrorHandler;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.scheduling.PollerMetadata;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.PeriodicTrigger;
/**
* @author Oleg Zhurakousky
*
*/
public class PollingLifecycleTests {
private ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
private TestErrorHandler errorHandler = new TestErrorHandler();
@Before
public void init() throws Exception {
taskScheduler.afterPropertiesSet();
}
@Test
public void ensurePollerTaskStops() throws Exception{
final CountDownLatch latch = new CountDownLatch(1);
QueueChannel channel = new QueueChannel();
channel.send(new GenericMessage<String>("foo"));
MessageHandler handler = Mockito.spy(new MessageHandler() {
public void handleMessage(Message<?> message) throws MessagingException {
latch.countDown();
}
});
PollingConsumer consumer = new PollingConsumer(channel, handler);
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(0));
consumer.setPollerMetadata(pollerMetadata);
consumer.setErrorHandler(errorHandler);
consumer.setTaskScheduler(taskScheduler);
consumer.setBeanFactory(mock(BeanFactory.class));
consumer.afterPropertiesSet();
consumer.start();
assertTrue(latch.await(2, TimeUnit.SECONDS));
Mockito.verify(handler, times(1)).handleMessage(Mockito.any(Message.class));
consumer.stop();
for (int i = 0; i < 10; i++) {
channel.send(new GenericMessage<String>("foo"));
}
Thread.sleep(2000); // give enough time for poller to kick in if it didn't stop properly
// we'll still have a natural race condition between call to stop() and poller polling
// so what we really have to assert is that it doesn't poll for more then once after stop() was called
Mockito.reset(handler);
Mockito.verify(handler, atMost(1)).handleMessage(Mockito.any(Message.class));
}
@Test
public void ensurePollerTaskStopsForAdapter() throws Exception{
final CountDownLatch latch = new CountDownLatch(1);
QueueChannel channel = new QueueChannel();
SourcePollingChannelAdapterFactoryBean adapterFactory = new SourcePollingChannelAdapterFactoryBean();
PollerMetadata pollerMetadata = new PollerMetadata();
pollerMetadata.setTrigger(new PeriodicTrigger(2000));
adapterFactory.setPollerMetadata(pollerMetadata);
MessageSource<String> source = spy(new MessageSource<String>() {
public Message<String> receive() {
latch.countDown();
return new GenericMessage<String>("hello");
}
});
adapterFactory.setSource(source);
adapterFactory.setOutputChannel(channel);
adapterFactory.setBeanFactory(mock(ConfigurableBeanFactory.class));
SourcePollingChannelAdapter adapter = adapterFactory.getObject();
adapter.setTaskScheduler(taskScheduler);
adapter.afterPropertiesSet();
adapter.start();
assertTrue(latch.await(2, TimeUnit.SECONDS));
assertNotNull(channel.receive(100));
adapter.stop();
assertNull(channel.receive(1000));
Mockito.verify(source, times(1)).receive();
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.expression;
import static org.junit.Assert.assertEquals;
import java.io.FileOutputStream;
import org.junit.After;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
/**
* @author Mark Fisher
* @since 2.0
*/
public class DynamicExpressionTests {
private static final String key = "test.greeting";
private static final String basename = "org/springframework/integration/expression/expressions";
private static final String filepath = basename + ".properties";
@After
public void resetFile() {
writeExpressionStringToFile("'Hello World!'");
}
@Test
public void expressionUpdate() throws Exception {
ReloadableResourceBundleExpressionSource source = new ReloadableResourceBundleExpressionSource();
source.setBasename(basename);
source.setCacheSeconds(0);
DynamicExpression expression = new DynamicExpression(key, source);
assertEquals("Hello World!", expression.getValue());
writeExpressionStringToFile("toUpperCase()");
assertEquals("FOO", expression.getValue("foo"));
}
private static void writeExpressionStringToFile(String expressionString) {
ClassPathResource resource = new ClassPathResource(filepath);
byte[] bytes = new String(key + "=" + expressionString).getBytes();
try {
new FileOutputStream(resource.getFile()).write(bytes);
}
catch (Exception e) {
throw new IllegalStateException("failed to write expression string to file", e);
}
}
}

View File

@@ -0,0 +1 @@
test.greeting='Hello World!'

View File

@@ -0,0 +1,26 @@
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="positives">
<queue/>
</channel>
<channel id="negatives">
<queue/>
</channel>
<filter input-channel="input" output-channel="positives" discard-channel="negatives">
<expression key="filter.positive" source="testExpressionSource"/>
</filter>
<beans:bean id="testExpressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="org/springframework/integration/filter/expressions"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,63 @@
/*
* 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.filter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DynamicExpressionFilterIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel positives;
@Autowired
private PollableChannel negatives;
@Test
public void simpleExpressionBasedFilter() {
this.input.send(new GenericMessage<Integer>(1));
this.input.send(new GenericMessage<Integer>(0));
this.input.send(new GenericMessage<Integer>(99));
this.input.send(new GenericMessage<Integer>(-99));
assertEquals(new Integer(1), positives.receive(0).getPayload());
assertEquals(new Integer(99), positives.receive(0).getPayload());
assertEquals(new Integer(0), negatives.receive(0).getPayload());
assertEquals(new Integer(-99), negatives.receive(0).getPayload());
assertNull(positives.receive(0));
assertNull(negatives.receive(0));
}
}

View File

@@ -0,0 +1 @@
filter.positive=payload > 0

View File

@@ -32,6 +32,10 @@ import org.springframework.context.support.GenericApplicationContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.message.GenericMessage;
/**
@@ -43,6 +47,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
private static final Log logger = LogFactory.getLog(ExpressionEvaluatingMessageProcessorTests.class);
private static final ExpressionParser expressionParser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
@Rule
public ExpectedException expected = ExpectedException.none();
@@ -50,7 +56,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
@Test
public void testProcessMessage() {
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload");
Expression expression = expressionParser.parseExpression("payload");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
assertEquals("foo", processor.processMessage(new GenericMessage<String>("foo")));
}
@@ -62,7 +69,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
return number+"";
}
}
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("#target.stringify(payload)");
Expression expression = expressionParser.parseExpression("#target.stringify(payload)");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
processor.getEvaluationContext().setVariable("target", new TestTarget());
assertEquals("2", processor.processMessage(new GenericMessage<String>("2")));
}
@@ -74,7 +82,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
public void ping(String input) {
}
}
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("#target.ping(payload)");
Expression expression = expressionParser.parseExpression("#target.ping(payload)");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
processor.getEvaluationContext().setVariable("target", new TestTarget());
assertEquals(null, processor.processMessage(new GenericMessage<String>("2")));
}
@@ -88,7 +97,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
}
}
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("#target.find(payload)");
Expression expression = expressionParser.parseExpression("#target.find(payload)");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
processor.setBeanFactory(new GenericApplicationContext().getBeanFactory());
processor.getEvaluationContext().setVariable("target", new TestTarget());
String result = (String) processor.processMessage(new GenericMessage<String>("classpath:*.properties"));
@@ -97,21 +107,24 @@ public class ExpressionEvaluatingMessageProcessorTests {
@Test
public void testProcessMessageWithDollarInBrackets() {
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers['$id']");
Expression expression = expressionParser.parseExpression("headers['$id']");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
GenericMessage<String> message = new GenericMessage<String>("foo");
assertEquals(message.getHeaders().getId(), processor.processMessage(message));
}
@Test
public void testProcessMessageWithDollarPropertyAccess() {
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers.$id");
Expression expression = expressionParser.parseExpression("headers.$id");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
GenericMessage<String> message = new GenericMessage<String>("foo");
assertEquals(message.getHeaders().getId(), processor.processMessage(message));
}
@Test
public void testProcessMessageWithStaticKey() {
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers[headers.ID]");
Expression expression = expressionParser.parseExpression("headers[headers.ID]");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
GenericMessage<String> message = new GenericMessage<String>("foo");
assertEquals(message.getHeaders().getId(), processor.processMessage(message));
}
@@ -122,7 +135,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
BeanDefinition beanDefinition = new RootBeanDefinition(String.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar");
context.registerBeanDefinition("testString", beanDefinition);
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.concat(@testString)");
Expression expression = expressionParser.parseExpression("payload.concat(@testString)");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
processor.setBeanFactory(context);
GenericMessage<String> message = new GenericMessage<String>("foo");
assertEquals("foobar", processor.processMessage(message));
@@ -134,7 +148,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
BeanDefinition beanDefinition = new RootBeanDefinition(String.class);
beanDefinition.getConstructorArgumentValues().addGenericArgumentValue("bar");
context.registerBeanDefinition("testString", beanDefinition);
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("@testString.concat(payload)");
Expression expression = expressionParser.parseExpression("@testString.concat(payload)");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
processor.setBeanFactory(context);
GenericMessage<String> message = new GenericMessage<String>("foo");
assertEquals("barfoo", processor.processMessage(message));
@@ -154,7 +169,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
description.appendText("cause to be EvaluationException but was ").appendValue(cause);
}
});
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.fixMe()");
Expression expression = expressionParser.parseExpression("payload.fixMe()");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
assertEquals("foo", processor.processMessage(new GenericMessage<String>("foo")));
}
@@ -172,7 +188,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
description.appendText("cause to be UnsupportedOperationException but was ").appendValue(cause);
}
});
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.throwRuntimeException()");
Expression expression = expressionParser.parseExpression("payload.throwRuntimeException()");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
assertEquals("foo", processor.processMessage(new GenericMessage<TestPayload>(new TestPayload())));
}
@@ -190,7 +207,8 @@ public class ExpressionEvaluatingMessageProcessorTests {
description.appendText("cause to be CheckedException but was ").appendValue(cause);
}
});
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("payload.throwCheckedException()");
Expression expression = expressionParser.parseExpression("payload.throwCheckedException()");
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor(expression);
assertEquals("foo", processor.processMessage(new GenericMessage<TestPayload>(new TestPayload())));
}
@@ -213,5 +231,5 @@ public class ExpressionEvaluatingMessageProcessorTests {
super(string);
}
}
}

View File

@@ -18,7 +18,6 @@ package org.springframework.integration.handler;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.support.MessageBuilder;
@@ -42,7 +41,6 @@ public class LoggingHandlerTests {
input.send(MessageBuilder.withPayload(bean).setHeader("foo", "bar").build());
}
public static class TestBean {
private final String name;

View File

@@ -22,20 +22,24 @@ import static org.junit.Assert.assertNull;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.message.ErrorMessage;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class ErrorMessageExceptionTypeRouterTests {
private DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
private QueueChannel illegalArgumentChannel = new QueueChannel();
@@ -46,6 +50,15 @@ public class ErrorMessageExceptionTypeRouterTests {
private QueueChannel messageDeliveryExceptionChannel = new QueueChannel();
private QueueChannel defaultChannel = new QueueChannel();
@Before
public void prepare(){
beanFactory.registerSingleton("illegalArgumentChannel", illegalArgumentChannel);
beanFactory.registerSingleton("runtimeExceptionChannel", runtimeExceptionChannel);
beanFactory.registerSingleton("messageHandlingExceptionChannel", messageHandlingExceptionChannel);
beanFactory.registerSingleton("messageDeliveryExceptionChannel", messageDeliveryExceptionChannel);
beanFactory.registerSingleton("defaultChannel", defaultChannel);
}
@Test
@@ -56,12 +69,14 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
ErrorMessage message = new ErrorMessage(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(IllegalArgumentException.class, illegalArgumentChannel);
exceptionTypeChannelMap.put(RuntimeException.class, runtimeExceptionChannel);
exceptionTypeChannelMap.put(MessageHandlingException.class, messageHandlingExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
router.handleMessage(message);
assertNotNull(illegalArgumentChannel.receive(1000));
@@ -78,11 +93,12 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
ErrorMessage message = new ErrorMessage(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(RuntimeException.class, runtimeExceptionChannel);
exceptionTypeChannelMap.put(MessageHandlingException.class, messageHandlingExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "runtimeExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
router.handleMessage(message);
assertNotNull(runtimeExceptionChannel.receive(1000));
@@ -99,10 +115,10 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
ErrorMessage message = new ErrorMessage(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(MessageHandlingException.class, messageHandlingExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
router.handleMessage(message);
assertNotNull(messageHandlingExceptionChannel.receive(1000));
@@ -135,10 +151,10 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
ErrorMessage message = new ErrorMessage(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(MessageDeliveryException.class, messageDeliveryExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(MessageDeliveryException.class.getName(), "messageDeliveryExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setResolutionRequired(true);
router.handleMessage(message);
}
@@ -151,12 +167,12 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
Message<?> message = new GenericMessage<Exception>(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(IllegalArgumentException.class, illegalArgumentChannel);
exceptionTypeChannelMap.put(RuntimeException.class, runtimeExceptionChannel);
exceptionTypeChannelMap.put(MessageHandlingException.class, messageHandlingExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
exceptionTypeChannelMap.put(RuntimeException.class.getName(), "runtimeExceptionChannel");
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
router.handleMessage(message);
assertNotNull(illegalArgumentChannel.receive(1000));
@@ -173,11 +189,11 @@ public class ErrorMessageExceptionTypeRouterTests {
MessageHandlingException error = new MessageHandlingException(failedMessage, "failed", middleCause);
ErrorMessage message = new ErrorMessage(error);
ErrorMessageExceptionTypeRouter router = new ErrorMessageExceptionTypeRouter();
Map<Class<? extends Throwable>, MessageChannel> exceptionTypeChannelMap =
new HashMap<Class<? extends Throwable>, MessageChannel>();
exceptionTypeChannelMap.put(IllegalArgumentException.class, illegalArgumentChannel);
exceptionTypeChannelMap.put(MessageHandlingException.class, messageHandlingExceptionChannel);
router.setExceptionTypeChannelMap(exceptionTypeChannelMap);
Map<String, String> exceptionTypeChannelMap = new HashMap<String, String>();
exceptionTypeChannelMap.put(IllegalArgumentException.class.getName(), "illegalArgumentChannel");
exceptionTypeChannelMap.put(MessageHandlingException.class.getName(), "messageHandlingExceptionChannel");
router.setChannelIdentifierMap(exceptionTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
router.handleMessage(message);
assertNotNull(illegalArgumentChannel.receive(1000));

View File

@@ -16,23 +16,26 @@
package org.springframework.integration.router;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import org.junit.Test;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.channel.MapBasedChannelResolver;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.ChannelResolver;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class HeaderValueRouterTests {
@@ -61,6 +64,7 @@ public class HeaderValueRouterTests {
routerBeanDefinition.getPropertyValues().addPropertyValue("resolutionRequired", "true");
context.registerBeanDefinition("router", routerBeanDefinition);
context.registerBeanDefinition("testChannel", new RootBeanDefinition(QueueChannel.class));
context.registerBeanDefinition("newChannel", new RootBeanDefinition(QueueChannel.class));
context.refresh();
MessageHandler handler = (MessageHandler) context.getBean("router");
Message<?> message = MessageBuilder.withPayload("test").setHeader("testHeaderName", "testChannel").build();
@@ -69,6 +73,20 @@ public class HeaderValueRouterTests {
Message<?> result = channel.receive(1000);
assertNotNull(result);
assertSame(message, result);
// validate dynamics
HeaderValueRouter router = (HeaderValueRouter) context.getBean("router");
router.setChannelMapping("testChannel", "newChannel");
router.handleMessage(message);
QueueChannel newChannel = (QueueChannel) context.getBean("newChannel");
result = newChannel.receive(10);
assertNotNull(result);
router.removeChannelMapping("testChannel");
router.handleMessage(message);
result = channel.receive(1000);
assertNotNull(result);
assertSame(message, result);
}
@Test
@@ -76,14 +94,12 @@ public class HeaderValueRouterTests {
public void resolveChannelNameFromMap() {
StaticApplicationContext context = new StaticApplicationContext();
ManagedMap channelMap = new ManagedMap();
channelMap.put("testKey", new RuntimeBeanReference("testChannel"));
RootBeanDefinition channelResolverBeanDefinition = new RootBeanDefinition(MapBasedChannelResolver.class);
channelResolverBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(channelMap);
channelMap.put("testKey", "testChannel");
RootBeanDefinition routerBeanDefinition = new RootBeanDefinition(HeaderValueRouter.class);
routerBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue("testHeaderName");
routerBeanDefinition.getPropertyValues().addPropertyValue("resolutionRequired", "true");
routerBeanDefinition.getPropertyValues().addPropertyValue("channelResolver", new RuntimeBeanReference("resolver"));
context.registerBeanDefinition("resolver", channelResolverBeanDefinition);
routerBeanDefinition.getPropertyValues().addPropertyValue("channelIdentifierMap", channelMap);
routerBeanDefinition.getPropertyValues().addPropertyValue("beanFactory", context);
context.registerBeanDefinition("router", routerBeanDefinition);
context.registerBeanDefinition("testChannel", new RootBeanDefinition(QueueChannel.class));
context.refresh();
@@ -95,6 +111,34 @@ public class HeaderValueRouterTests {
assertNotNull(result);
assertSame(message, result);
}
@Test
@SuppressWarnings("unchecked")
public void resolveChannelNameFromMapAndCustomeResolver() {
final StaticApplicationContext context = new StaticApplicationContext();
ManagedMap channelMap = new ManagedMap();
channelMap.put("testKey", "testChannel");
RootBeanDefinition routerBeanDefinition = new RootBeanDefinition(HeaderValueRouter.class);
routerBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue("testHeaderName");
routerBeanDefinition.getPropertyValues().addPropertyValue("resolutionRequired", "true");
routerBeanDefinition.getPropertyValues().addPropertyValue("channelIdentifierMap", channelMap);
routerBeanDefinition.getPropertyValues().addPropertyValue("beanFactory", context);
routerBeanDefinition.getPropertyValues().addPropertyValue("channelResolver", new ChannelResolver() {
public MessageChannel resolveChannelName(String channelName) {
return context.getBean("anotherChannel", MessageChannel.class);
}
});
context.registerBeanDefinition("router", routerBeanDefinition);
context.registerBeanDefinition("testChannel", new RootBeanDefinition(QueueChannel.class));
context.registerBeanDefinition("anotherChannel", new RootBeanDefinition(QueueChannel.class));
context.refresh();
MessageHandler handler = (MessageHandler) context.getBean("router");
Message<?> message = MessageBuilder.withPayload("test").setHeader("testHeaderName", "testKey").build();
handler.handleMessage(message);
QueueChannel channel = (QueueChannel) context.getBean("anotherChannel");
Message<?> result = channel.receive(1000);
assertNotNull(result);
assertSame(message, result);
}
@Test
public void resolveMultipleChannelsWithStringArray() {
@@ -144,4 +188,5 @@ public class HeaderValueRouterTests {
assertSame(message, result2);
}
}

View File

@@ -18,16 +18,19 @@ package org.springframework.integration.router;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.TestChannelResolver;
import org.springframework.integration.message.GenericMessage;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.util.CollectionUtils;
/**
@@ -37,7 +40,7 @@ public class MultiChannelRouterTests {
@Test
public void routeWithChannelMapping() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
public List<Object> getChannelIndicatorList(Message<?> message) {
return CollectionUtils.arrayToList(new String[] {"channel1", "channel2"});
@@ -61,7 +64,7 @@ public class MultiChannelRouterTests {
@Test(expected = MessagingException.class)
public void channelNameLookupFailure() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
public List<Object> getChannelIndicatorList(Message<?> message) {
return CollectionUtils.arrayToList(new String[] {"noSuchChannel"} );
@@ -75,12 +78,13 @@ public class MultiChannelRouterTests {
@Test(expected = MessagingException.class)
public void channelMappingNotAvailable() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
public List<Object> getChannelIndicatorList(Message<?> message) {
return CollectionUtils.arrayToList(new String[] {"noSuchChannel"});
}
};
router.setBeanFactory(mock(BeanFactory.class));
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
}

View File

@@ -16,6 +16,7 @@
package org.springframework.integration.router;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -25,7 +26,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageHandlingException;
@@ -34,6 +35,7 @@ import org.springframework.integration.message.GenericMessage;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class PayloadTypeRouterTests {
@@ -41,17 +43,46 @@ public class PayloadTypeRouterTests {
public void resolveExactMatch() {
QueueChannel stringChannel = new QueueChannel();
QueueChannel integerChannel = new QueueChannel();
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(String.class, stringChannel);
payloadTypeChannelMap.put(Integer.class, integerChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
MessageChannel result1 = router.determineTargetChannel(message1);
MessageChannel result2 = router.determineTargetChannel(message2);
assertEquals(1, router.determineTargetChannels(message1).size());
MessageChannel result1 = router.determineTargetChannels(message1).iterator().next();
assertEquals(1, router.determineTargetChannels(message2).size());
MessageChannel result2 = router.determineTargetChannels(message2).iterator().next();
assertEquals(stringChannel, result1);
assertEquals(integerChannel, result2);
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(String.class.getName(), "newChannel");
assertEquals(1, router.determineTargetChannels(message1).size());
result1 = router.determineTargetChannels(message1).iterator().next();
assertEquals(newChannel, result1);
// validate nothing happens if mappings were removed and resolutionRequires = false
router.removeChannelMapping(String.class.getName());
router.removeChannelMapping(Integer.class.getName());
router.handleMessage(message1);
// validate exception is thrown if mappings were removed and resolutionRequires = true
router.setResolutionRequired(true);
try {
router.handleMessage(message1);
fail();
} catch (Exception e) {
// ignore
}
}
@Test
@@ -60,10 +91,15 @@ public class PayloadTypeRouterTests {
defaultChannel.setBeanName("defaultChannel");
QueueChannel numberChannel = new QueueChannel();
numberChannel.setBeanName("numberChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Number.class, numberChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
@@ -71,6 +107,15 @@ public class PayloadTypeRouterTests {
assertNotNull(result);
assertEquals(99, result.getPayload());
assertNull(defaultChannel.receive(0));
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(Integer.class.getName(), "newChannel");
assertEquals(1, router.determineTargetChannels(message).size());
router.handleMessage(message);
result = newChannel.receive(10);
assertNotNull(result);
}
@Test
@@ -81,11 +126,20 @@ public class PayloadTypeRouterTests {
numberChannel.setBeanName("numberChannel");
QueueChannel integerChannel = new QueueChannel();
integerChannel.setBeanName("integerChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Number.class, numberChannel);
payloadTypeChannelMap.put(Integer.class, integerChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
@@ -102,10 +156,18 @@ public class PayloadTypeRouterTests {
defaultChannel.setBeanName("defaultChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Comparable.class, comparableChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
@@ -123,11 +185,20 @@ public class PayloadTypeRouterTests {
numberChannel.setBeanName("numberChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Number.class, numberChannel);
payloadTypeChannelMap.put(Comparable.class, comparableChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
@@ -136,6 +207,15 @@ public class PayloadTypeRouterTests {
assertEquals(99, result.getPayload());
assertNull(numberChannel.receive(0));
assertNull(defaultChannel.receive(0));
// validate dynamics
QueueChannel newChannel = new QueueChannel();
beanFactory.registerSingleton("newChannel", newChannel);
router.setChannelMapping(Integer.class.getName(), "newChannel");
assertEquals(1, router.determineTargetChannels(message).size());
router.handleMessage(message);
result = newChannel.receive(10);
assertNotNull(result);
}
@Test(expected = IllegalStateException.class)
@@ -146,11 +226,20 @@ public class PayloadTypeRouterTests {
serializableChannel.setBeanName("serializableChannel");
QueueChannel comparableChannel = new QueueChannel();
comparableChannel.setBeanName("comparableChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Serializable.class, serializableChannel);
payloadTypeChannelMap.put(Comparable.class, comparableChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
beanFactory.registerSingleton("comparableChannel", comparableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
payloadTypeChannelMap.put(Comparable.class.getName(), "comparableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<String> message = new GenericMessage<String>("test");
try {
@@ -169,11 +258,22 @@ public class PayloadTypeRouterTests {
numberChannel.setBeanName("numberChannel");
QueueChannel serializableChannel = new QueueChannel();
serializableChannel.setBeanName("serializableChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(Number.class, numberChannel);
payloadTypeChannelMap.put(Serializable.class, serializableChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("defaultChannel", defaultChannel);
beanFactory.registerSingleton("numberChannel", numberChannel);
beanFactory.registerSingleton("serializableChannel", serializableChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(Number.class.getName(), "numberChannel");
payloadTypeChannelMap.put(Serializable.class.getName(), "serializableChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<Integer> message = new GenericMessage<Integer>(99);
router.handleMessage(message);
@@ -190,11 +290,19 @@ public class PayloadTypeRouterTests {
QueueChannel integerChannel = new QueueChannel();
stringChannel.setBeanName("stringChannel");
integerChannel.setBeanName("integerChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(String.class, stringChannel);
payloadTypeChannelMap.put(Integer.class, integerChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("integerChannel", integerChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
payloadTypeChannelMap.put(Integer.class.getName(), "integerChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
router.handleMessage(message1);
@@ -211,10 +319,19 @@ public class PayloadTypeRouterTests {
stringChannel.setBeanName("stringChannel");
QueueChannel defaultChannel = new QueueChannel();
defaultChannel.setBeanName("defaultChannel");
Map<Class<?>, MessageChannel> payloadTypeChannelMap = new ConcurrentHashMap<Class<?>, MessageChannel>();
payloadTypeChannelMap.put(String.class, stringChannel);
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerSingleton("stringChannel", stringChannel);
beanFactory.registerSingleton("defaultChannel", defaultChannel);
Map<String, String> payloadTypeChannelMap = new ConcurrentHashMap<String, String>();
payloadTypeChannelMap.put(String.class.getName(), "stringChannel");
PayloadTypeRouter router = new PayloadTypeRouter();
router.setPayloadTypeChannelMap(payloadTypeChannelMap);
router.setBeanFactory(beanFactory);
router.setChannelIdentifierMap(payloadTypeChannelMap);
router.setDefaultOutputChannel(defaultChannel);
Message<String> message1 = new GenericMessage<String>("test");
Message<Integer> message2 = new GenericMessage<Integer>(123);
@@ -227,5 +344,4 @@ public class PayloadTypeRouterTests {
assertNotNull(result2);
assertEquals(123, result2.getPayload());
}
}

View File

@@ -17,16 +17,15 @@
package org.springframework.integration.router;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.QueueChannel;
@@ -36,15 +35,17 @@ import org.springframework.util.CollectionUtils;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
*/
public class RouterTests {
@Test
public void nullChannelIgnoredByDefault() {
AbstractMessageRouter router = new AbstractMessageRouter() {
public List<MessageChannel> determineTargetChannels(Message<?> message) {
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
}
};
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
@@ -53,7 +54,8 @@ public class RouterTests {
@Test(expected = MessageDeliveryException.class)
public void nullChannelThrowsExceptionWhenResolutionRequired() {
AbstractMessageRouter router = new AbstractMessageRouter() {
public List<MessageChannel> determineTargetChannels(Message<?> message) {
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
};
@@ -65,8 +67,9 @@ public class RouterTests {
@Test
public void emptyChannelListIgnoredByDefault() {
AbstractMessageRouter router = new AbstractMessageRouter() {
public List<MessageChannel> determineTargetChannels(Message<?> message) {
return Collections.emptyList();
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
};
Message<String> message = new GenericMessage<String>("test");
@@ -76,8 +79,9 @@ public class RouterTests {
@Test(expected = MessageDeliveryException.class)
public void emptyChannelListThrowsExceptionWhenResolutionRequired() {
AbstractMessageRouter router = new AbstractMessageRouter() {
public List<MessageChannel> determineTargetChannels(Message<?> message) {
return Collections.emptyList();
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
};
router.setResolutionRequired(true);
@@ -87,8 +91,9 @@ public class RouterTests {
@Test
public void nullChannelNameArrayIgnoredByDefault() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
protected List<Object> getChannelIndicatorList(Message<?> message) {
AbstractMessageRouter router = new AbstractMessageRouter() {
@Override
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
};
@@ -100,7 +105,7 @@ public class RouterTests {
@Test(expected = MessageDeliveryException.class)
public void nullChannelNameArrayThrowsExceptionWhenResolutionRequired() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
protected List<Object> getChannelIndicatorList(Message<?> message) {
return null;
}
@@ -115,7 +120,7 @@ public class RouterTests {
@Test
public void emptyChannelNameArrayIgnoredByDefault() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
protected List<Object> getChannelIndicatorList(Message<?> message) {
return new ArrayList<Object>();
}
@@ -128,7 +133,7 @@ public class RouterTests {
@Test(expected = MessageDeliveryException.class)
public void emptyChannelNameArrayThrowsExceptionWhenResolutionRequired() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
protected List<Object> getChannelIndicatorList(Message<?> message) {
return CollectionUtils.arrayToList(new String[] {});
@@ -148,17 +153,19 @@ public class RouterTests {
return "notImportant";
}
};
router.setBeanFactory(mock(BeanFactory.class));
router.handleMessage(new GenericMessage<String>("this should fail"));
}
@Test(expected = MessagingException.class)
public void channelMappingIsRequiredWhenResolvingChannelNamesWithMultiChannelRouter() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
protected List<Object> getChannelIndicatorList(Message<?> message){
return CollectionUtils.arrayToList(new String[] { "notImportant" });
}
};
router.setBeanFactory(mock(BeanFactory.class));
router.handleMessage(new GenericMessage<String>("this should fail"));
}
@@ -180,7 +187,7 @@ public class RouterTests {
@Test
public void beanFactoryWithMultiChannelRouter() {
AbstractChannelNameResolvingMessageRouter router = new AbstractChannelNameResolvingMessageRouter() {
AbstractMessageRouter router = new AbstractMessageRouter() {
@SuppressWarnings("unchecked")
protected List<Object> getChannelIndicatorList(Message<?> message) {
return CollectionUtils.arrayToList(new String[] { "testChannel" });

View File

@@ -1,93 +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.router;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessagingException;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.channel.TestChannelResolver;
import org.springframework.integration.message.GenericMessage;
/**
* @author Mark Fisher
*/
public class SingleChannelRouterTests {
@Test
public void routeWithChannelResolver() {
final QueueChannel channel = new QueueChannel();
AbstractSingleChannelRouter router = new AbstractSingleChannelRouter() {
public MessageChannel determineTargetChannel(Message<?> message) {
return channel;
}
};
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
Message<?> result = channel.receive(25);
assertNotNull(result);
assertEquals("test", result.getPayload());
}
@Test
public void routeWithChannelNameResolver() {
AbstractSingleChannelNameRouter router = new AbstractSingleChannelNameRouter() {
public String determineTargetChannelName(Message<?> message) {
return "testChannel";
}
};
QueueChannel channel = new QueueChannel();
TestChannelResolver channelResolver = new TestChannelResolver();
channelResolver.addChannel("testChannel", channel);
router.setChannelResolver(channelResolver);
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
Message<?> result = channel.receive(25);
assertNotNull(result);
assertEquals("test", result.getPayload());
}
@Test
public void nullChannelResultIgnored() {
AbstractSingleChannelRouter router = new AbstractSingleChannelRouter() {
public MessageChannel determineTargetChannel(Message<?> message) {
return null;
}
};
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
}
@Test(expected = MessagingException.class)
public void channelNameResolutionFailure() {
AbstractSingleChannelNameRouter router = new AbstractSingleChannelNameRouter() {
public String determineTargetChannelName(Message<?> message) {
return "noSuchChannel";
}
};
TestChannelResolver channelResolver = new TestChannelResolver();
router.setChannelResolver(channelResolver);
Message<String> message = new GenericMessage<String>("test");
router.handleMessage(message);
}
}

View File

@@ -0,0 +1,26 @@
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="even">
<queue/>
</channel>
<channel id="odd">
<queue/>
</channel>
<router input-channel="input">
<expression key="router.oddeven" source="testExpressionSource"/>
</router>
<beans:bean id="testExpressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="org/springframework/integration/router/config/expressions"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,85 @@
/*
* 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.router.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
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.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DynamicExpressionRouterIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel even;
@Autowired
private PollableChannel odd;
@Test
public void dynamicExpressionBasedRouter() {
TestBean testBean1 = new TestBean(1);
TestBean testBean2 = new TestBean(2);
TestBean testBean3 = new TestBean(3);
TestBean testBean4 = new TestBean(4);
Message<?> message1 = MessageBuilder.withPayload(testBean1).build();
Message<?> message2 = MessageBuilder.withPayload(testBean2).build();
Message<?> message3 = MessageBuilder.withPayload(testBean3).build();
Message<?> message4 = MessageBuilder.withPayload(testBean4).build();
this.input.send(message1);
this.input.send(message2);
this.input.send(message3);
this.input.send(message4);
assertEquals(testBean1, odd.receive(0).getPayload());
assertEquals(testBean2, even.receive(0).getPayload());
assertEquals(testBean3, odd.receive(0).getPayload());
assertEquals(testBean4, even.receive(0).getPayload());
assertNull(odd.receive(0));
assertNull(even.receive(0));
}
static class TestBean {
private final int number;
public TestBean(int number) {
this.number = number;
}
public int getNumber() {
return this.number;
}
}
}

View File

@@ -61,15 +61,6 @@ public class PayloadTypeRouterParserTests {
assertTrue(chanel2.receive(0).getPayload() instanceof Integer);
}
@Test(expected=BeanDefinitionStoreException.class)
public void testFakeTypes(){
ByteArrayInputStream stream = new ByteArrayInputStream(routerConfigFakeType.getBytes());
GenericApplicationContext ac = new GenericApplicationContext();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ac);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
reader.loadBeanDefinitions(new InputStreamResource(stream));
}
@Test(expected=BeanDefinitionStoreException.class)
public void testNoMappingElement(){
ByteArrayInputStream stream = new ByteArrayInputStream(routerConfigNoMaping.getBytes());

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.mockito.Mockito;
@@ -205,9 +206,10 @@ public class RouterParserTests {
this.channel = channel;
}
@Override
protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
return Collections.singletonList(this.channel);
protected List<Object> getChannelIndicatorList(Message<?> message) {
return Collections.singletonList((Object)this.channel);
}
}

View File

@@ -19,7 +19,7 @@
<queue capacity="1" />
</channel>
<router input-channel="expressionRouter" expression="payload.name"
<router id="spelRouter" input-channel="expressionRouter" expression="payload.name"
default-output-channel="defaultChannelForExpression"
ignore-channel-name-resolution-failures="true">
<mapping value="foo" channel="fooChannelForExpression"/>

View File

@@ -23,10 +23,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.router.AbstractMessageRouter;
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;
@@ -39,6 +43,10 @@ public class RouterWithMappingTests {
@Autowired
private MessageChannel expressionRouter;
@Autowired
@Qualifier("spelRouter")
private ConsumerEndpointFactoryBean spelRouter;
@Autowired
private MessageChannel pojoRouter;
@@ -79,6 +87,13 @@ public class RouterWithMappingTests {
assertNotNull(defaultChannelForExpression.receive(0));
assertNull(fooChannelForExpression.receive(0));
assertNull(barChannelForExpression.receive(0));
// validate dynamics
AbstractMessageRouter router = (AbstractMessageRouter) TestUtils.getPropertyValue(spelRouter, "handler");
router.setChannelMapping("baz", "fooChannelForExpression");
expressionRouter.send(message3);
assertNull(defaultChannelForExpression.receive(10));
assertNotNull(fooChannelForExpression.receive(10));
assertNull(barChannelForExpression.receive(0));
}
@Test

View File

@@ -0,0 +1 @@
router.oddeven=payload.number % 2 == 0 ? 'even' : 'odd'

View File

@@ -0,0 +1,22 @@
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="output">
<queue/>
</channel>
<splitter input-channel="input" output-channel="output">
<expression key="split.lessThan5" source="testExpressionSource"/>
</splitter>
<beans:bean id="testExpressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="org/springframework/integration/splitter/expressions"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,89 @@
/*
* 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.splitter;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.List;
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.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DynamicExpressionSplitterIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel output;
@Test
public void simple() {
Message<?> message = MessageBuilder.withPayload(new TestBean()).setHeader("foo", "foo").build();
this.input.send(message);
Message<?> one = output.receive(0);
Message<?> two = output.receive(0);
Message<?> three = output.receive(0);
Message<?> four = output.receive(0);
assertEquals(new Integer(1), one.getPayload());
assertEquals("foo", one.getHeaders().get("foo"));
assertEquals(new Integer(2), two.getPayload());
assertEquals("foo", two.getHeaders().get("foo"));
assertEquals(new Integer(3), three.getPayload());
assertEquals("foo", three.getHeaders().get("foo"));
assertEquals(new Integer(4), four.getPayload());
assertEquals("foo", four.getHeaders().get("foo"));
assertNull(output.receive(0));
}
static class TestBean {
private final List<Integer> numbers = new ArrayList<Integer>();
public TestBean() {
for (int i = 1; i <= 10; i++) {
this.numbers.add(i);
}
}
public List<Integer> getNumbers() {
return this.numbers;
}
public String[] split(String s) {
return s.split(",");
}
}
}

View File

@@ -0,0 +1 @@
split.lessThan5=payload.numbers.?[#this < 5]

View File

@@ -0,0 +1,24 @@
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="output">
<queue/>
</channel>
<header-enricher input-channel="input" output-channel="output">
<header name="testHeader">
<expression key="test.header"/>
</header>
</header-enricher>
<beans:bean id="expressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="org/springframework/integration/transformer/expressions"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,53 @@
/*
* 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.transformer;
import static org.junit.Assert.assertEquals;
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.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DynamicExpressionHeaderEnricherIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel output;
@Test
public void dynamicExpressionHeader() {
Message<?> message = MessageBuilder.withPayload("test").build();
this.input.send(message);
Message<?> result = output.receive(0);
assertEquals("foo", result.getHeaders().get("testHeader"));
}
}

View File

@@ -0,0 +1,22 @@
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd">
<channel id="output">
<queue/>
</channel>
<transformer input-channel="input" output-channel="output">
<expression key="test.transform"/>
</transformer>
<beans:bean id="expressionSource" class="org.springframework.integration.expression.ReloadableResourceBundleExpressionSource">
<beans:property name="basename" value="org/springframework/integration/transformer/expressions"/>
</beans:bean>
</beans:beans>

View File

@@ -0,0 +1,61 @@
/*
* 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.transformer;
import static org.junit.Assert.assertEquals;
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.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Mark Fisher
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class DynamicExpressionTransformerIntegrationTests {
@Autowired
private MessageChannel input;
@Autowired
private PollableChannel output;
@Test
public void transformWithDynamicExpression() {
Message<?> message = MessageBuilder.withPayload(new TestBean()).setHeader("bar", 123).build();
this.input.send(message);
Message<?> result = output.receive(0);
assertEquals("test123", result.getPayload());
}
static class TestBean {
public String getFoo() {
return "test";
}
}
}

View File

@@ -0,0 +1,2 @@
test.transform=payload.foo + headers.bar
test.header='foo'

View File

@@ -0,0 +1,29 @@
/**
*
*/
package org.springframework.integration.util;
import static junit.framework.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
/**
* @author Oleg Zhurakousky
*
*/
public class BeanFactoryTypeConverterTests {
@Test
public void testEmptyCollectionConversion(){
BeanFactoryTypeConverter typeConverter = new BeanFactoryTypeConverter();
List<String> sourceObject = new ArrayList<String>();
// source type doesn't even matter
ArrayList<BeanFactoryTypeConverterTests> convertedCollection =
(ArrayList<BeanFactoryTypeConverterTests>) typeConverter.convertValue(sourceObject, null, TypeDescriptor.forObject(new ArrayList<BeanFactoryTypeConverterTests>()));
assertEquals(sourceObject, convertedCollection);
}
}

View File

@@ -1,3 +1,3 @@
#Wed Sep 22 11:50:06 EDT 2010
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/integration\:/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1040" endstart\="1026" start\="985" startend\="1009"/>\n<bounds height\="112" width\="116" x\="19" y\="17"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1508" endstart\="1494" start\="1445" startend\="1477"/>\n<bounds height\="112" width\="116" x\="19" y\="149"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1731" endstart\="1717" start\="1657" startend\="1700"/>\n<bounds height\="112" width\="116" x\="19" y\="281"/>\n</element>\n</graph>
#Fri Oct 08 14:30:53 EDT 2010
//com.springsource.sts.config.flow.coordinates\:http\://www.springframework.org/schema/integration\:/spring-integration-event/src/test/java/org/springframework/integration/event/config/EventInboundChannelAdapterParserTests-context.xml=<?xml version\="1.0" encoding\="UTF-8"?>\n<graph>\n<element clazz\="InboundChannelAdapterModelElement" type\="inbound-channel-adapter">\n<structure end\="982" endstart\="982" start\="906" startend\="982"/>\n<bounds height\="112" width\="116" x\="19" y\="17"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1040" endstart\="1026" start\="985" startend\="1009"/>\n<bounds height\="112" width\="116" x\="155" y\="17"/>\n</element>\n<element clazz\="InboundChannelAdapterModelElement" type\="inbound-channel-adapter">\n<structure end\="1442" endstart\="1442" start\="1044" startend\="1442"/>\n<bounds height\="112" width\="116" x\="19" y\="149"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1508" endstart\="1494" start\="1445" startend\="1477"/>\n<bounds height\="112" width\="116" x\="155" y\="149"/>\n</element>\n<element clazz\="InboundChannelAdapterModelElement" type\="inbound-channel-adapter">\n<structure end\="1654" endstart\="1654" start\="1512" startend\="1654"/>\n<bounds height\="112" width\="116" x\="19" y\="281"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1731" endstart\="1717" start\="1657" startend\="1700"/>\n<bounds height\="112" width\="116" x\="155" y\="281"/>\n</element>\n<element clazz\="InboundChannelAdapterModelElement" type\="inbound-channel-adapter">\n<structure end\="1850" endstart\="1850" start\="1734" startend\="1850"/>\n<bounds height\="112" width\="116" x\="19" y\="413"/>\n</element>\n<element clazz\="ChannelModelElement" type\="channel">\n<structure end\="1912" endstart\="1898" start\="1853" startend\="1881"/>\n<bounds height\="112" width\="116" x\="155" y\="413"/>\n</element>\n</graph>
eclipse.preferences.version=1

View File

@@ -21,14 +21,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.integration.endpoint.MessageProducerSupport;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* An inbound Channel Adapter that passes Spring
* {@link ApplicationEvent ApplicationEvents} within messages.
* An inbound Channel Adapter that passes Spring {@link ApplicationEvent ApplicationEvents} within messages.
* If a {@link #setPayloadExpression(String) payloadExpression} is provided, it will be evaluated against
* the ApplicationEvent instance to create the Message payload.
*
* @author Mark Fisher
*/
@@ -36,6 +39,10 @@ public class ApplicationEventInboundChannelAdapter extends MessageProducerSuppor
private final Set<Class<? extends ApplicationEvent>> eventTypes = new CopyOnWriteArraySet<Class<? extends ApplicationEvent>>();
private volatile Expression payloadExpression;
private final SpelExpressionParser parser = new SpelExpressionParser();
/**
* Set the list of event types (classes that extend ApplicationEvent) that
@@ -51,6 +58,24 @@ public class ApplicationEventInboundChannelAdapter extends MessageProducerSuppor
}
}
/**
* Provide an expression to be evaluated against the received ApplicationEvent
* instance (the "root object") in order to create the Message payload. If none
* is provided, the ApplicationEvent itself will be used as the payload.
*/
public void setPayloadExpression(String payloadExpression) {
if (payloadExpression == null) {
this.payloadExpression = null;
}
else {
this.payloadExpression = this.parser.parseExpression(payloadExpression);
}
}
public String getComponentType() {
return "event:inbound-channel-adapter";
}
public void onApplicationEvent(ApplicationEvent event) {
if (CollectionUtils.isEmpty(this.eventTypes)) {
this.sendEventAsMessage(event);
@@ -65,11 +90,8 @@ public class ApplicationEventInboundChannelAdapter extends MessageProducerSuppor
}
private void sendEventAsMessage(ApplicationEvent event) {
this.sendMessage(MessageBuilder.withPayload(event).build());
}
public String getComponentType(){
return "event:inbound-channel-adapter";
Object payload = (this.payloadExpression != null) ? this.payloadExpression.getValue(event) : event;
this.sendMessage(MessageBuilder.withPayload(payload).build());
}
@Override

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.event.config;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
@@ -30,11 +31,11 @@ import org.w3c.dom.Element;
public class EventInboundChannelAdapterParser extends AbstractChannelAdapterParser{
@Override
protected AbstractBeanDefinition doParse(Element element,
ParserContext parserContext, String channelName) {
protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
BeanDefinitionBuilder adapterBuilder = BeanDefinitionBuilder.rootBeanDefinition(ApplicationEventInboundChannelAdapter.class);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(adapterBuilder, element, "channel", "outputChannel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "event-types");
IntegrationNamespaceUtils.setValueIfAttributeDefined(adapterBuilder, element, "payload-expression");
return adapterBuilder.getBeanDefinition();
}

View File

@@ -47,7 +47,15 @@
types will be sent [OPTIONAL]
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:attribute>
<xsd:attribute name="payload-expression">
<xsd:annotation>
<xsd:documentation><![CDATA[
SpEL expression to be evaluated against the ApplicationEvent to create the payload instance.
If not provided, the ApplicationEvent itself will be used as the payload.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
</xsd:element>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -53,8 +53,8 @@ public class ApplicationEventInboundChannelAdapterTests {
assertEquals("event2", ((ApplicationEvent) message3.getPayload()).getSource());
}
@SuppressWarnings("unchecked")
@Test
@SuppressWarnings("unchecked")
public void onlyConfiguredEventTypesAreSent() {
QueueChannel channel = new QueueChannel();
ApplicationEventInboundChannelAdapter adapter = new ApplicationEventInboundChannelAdapter();
@@ -93,6 +93,24 @@ public class ApplicationEventInboundChannelAdapterTests {
assertEquals(ContextClosedEvent.class, closedEventMessage.getPayload().getClass());
}
@Test
public void payloadExpressionEvaluatedAgainstApplicationEvent() {
QueueChannel channel = new QueueChannel();
ApplicationEventInboundChannelAdapter adapter = new ApplicationEventInboundChannelAdapter();
adapter.setPayloadExpression("'received: ' + source");
adapter.setOutputChannel(channel);
Message<?> message1 = channel.receive(0);
assertNull(message1);
adapter.onApplicationEvent(new TestApplicationEvent1());
adapter.onApplicationEvent(new TestApplicationEvent2());
Message<?> message2 = channel.receive(20);
assertNotNull(message2);
assertEquals("received: event1", message2.getPayload());
Message<?> message3 = channel.receive(20);
assertNotNull(message3);
assertEquals("received: event2", message3.getPayload());
}
@SuppressWarnings("serial")
private static class TestApplicationEvent1 extends ApplicationEvent {
@@ -100,6 +118,8 @@ public class ApplicationEventInboundChannelAdapterTests {
public TestApplicationEvent1() {
super("event1");
}
}

View File

@@ -30,7 +30,13 @@
<int:channel id="inputFilteredPlaceHolder">
<int:queue/>
</int:channel>
<int-event:inbound-channel-adapter id="eventAdapterSpel" channel="inputSpel" payload-expression="source + '-test'"/>
<int:channel id="inputSpel">
<int:queue/>
</int:channel>
<context:property-placeholder location="classpath:org/springframework/integration/event/config/inbound-adapter.properties"/>
</beans>

View File

@@ -33,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.expression.Expression;
import org.springframework.integration.Message;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.event.ApplicationEventInboundChannelAdapter;
@@ -62,9 +63,9 @@ public class EventInboundChannelAdapterParserTests {
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
Assert.assertEquals(context.getBean("input"), adapterAccessor.getPropertyValue("outputChannel"));
}
@SuppressWarnings("unchecked")
@Test
@SuppressWarnings("unchecked")
public void validateEventParserWithEventTypes() {
Object adapter = context.getBean("eventAdapterFiltered");
Assert.assertNotNull(adapter);
@@ -77,9 +78,9 @@ public class EventInboundChannelAdapterParserTests {
assertTrue(eventTypes.contains(SampleEvent.class));
assertTrue(eventTypes.contains(AnotherSampleEvent.class));
}
@SuppressWarnings("unchecked")
@Test
@SuppressWarnings("unchecked")
public void validateEventParserWithEventTypesAndPlaceholder() {
Object adapter = context.getBean("eventAdapterFilteredPlaceHolder");
Assert.assertNotNull(adapter);
@@ -108,6 +109,16 @@ public class EventInboundChannelAdapterParserTests {
assertEquals(SampleEvent.class, message.getPayload().getClass());
}
@Test
public void validatePayloadExpression() {
Object adapter = context.getBean("eventAdapterSpel");
Assert.assertNotNull(adapter);
Assert.assertTrue(adapter instanceof ApplicationEventInboundChannelAdapter);
DirectFieldAccessor adapterAccessor = new DirectFieldAccessor(adapter);
Expression expression = (Expression) adapterAccessor.getPropertyValue("payloadExpression");
Assert.assertEquals("source + '-test'", expression.getExpressionString());
}
@SuppressWarnings("serial")
public static class SampleEvent extends ApplicationEvent {

View File

@@ -4,8 +4,9 @@ Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Template:
org.springframework.integration.*;version="[2.0.0, 2.0.1)",
org.springframework.context;version="[3.0.3, 4.0.0)",
org.springframework.util;version="[3.0.3, 4.0.0)",
org.springframework.beans.*;version="[3.0.3, 4.0.0)",
org.springframework.context;version="[3.0.3, 4.0.0)",
org.springframework.expression.*;version="[3.0.3, 4.0.0)",
org.springframework.util;version="[3.0.3, 4.0.0)",
org.apache.commons.logging;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0"

View File

@@ -5,6 +5,7 @@
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-integration-parent/pom.xml</relativePath>
</parent>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-ftp</artifactId>
@@ -30,37 +31,31 @@
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${org.easymock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymockclassextension</artifactId>
<version>${org.easymock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2008 the original author or authors.
* 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.
@@ -13,8 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.SocketException;
import java.nio.charset.Charset;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.InitializingBean;
@@ -22,18 +32,12 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.integration.Message;
import org.springframework.integration.MessageDeliveryException;
import org.springframework.integration.MessageHandlingException;
import org.springframework.integration.MessageRejectedException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.file.DefaultFileNameGenerator;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import java.io.*;
import java.net.SocketException;
/**
* A {@link org.springframework.integration.core.MessageHandler} implementation that sends files to an FTP server.
*
@@ -41,14 +45,21 @@ import java.net.SocketException;
* @author Mark Fisher
* @author Josh Long
*/
public class FtpSendingMessageHandler implements MessageHandler,
InitializingBean {
public class FtpSendingMessageHandler implements MessageHandler, InitializingBean {
private static final String TEMPORARY_FILE_SUFFIX = ".writing";
private FtpClientPool ftpClientPool;
private FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
private File temporaryBufferFolderFile;
private Resource temporaryBufferFolder = new FileSystemResource(SystemUtils.getJavaIoTmpDir());
private String charset;
private volatile FtpClientPool ftpClientPool;
private volatile FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
private volatile File temporaryBufferFolderFile;
private volatile Resource temporaryBufferFolder = new FileSystemResource(SystemUtils.getJavaIoTmpDir());
private volatile String charset = Charset.defaultCharset().name();
public FtpSendingMessageHandler() {
}
@@ -57,10 +68,23 @@ public class FtpSendingMessageHandler implements MessageHandler,
this.ftpClientPool = ftpClientPool;
}
public void setFtpClientPool(FtpClientPool ftpClientPool) {
this.ftpClientPool = ftpClientPool;
}
public void setTemporaryBufferFolder(Resource temporaryBufferFolder) {
this.temporaryBufferFolder = temporaryBufferFolder;
}
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
this.fileNameGenerator = fileNameGenerator;
}
public void setCharset(String charset) {
this.charset = charset;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(ftpClientPool, "'ftpClientPool' must not be null");
Assert.notNull(temporaryBufferFolder,
@@ -70,95 +94,65 @@ public class FtpSendingMessageHandler implements MessageHandler,
/* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */
private File handleFileMessage(File sourceFile, File tempFile,
File resultFile) throws IOException {
private File handleFileMessage(File sourceFile, File tempFile, File resultFile) throws IOException {
if (sourceFile.renameTo(resultFile)) {
return resultFile;
}
FileCopyUtils.copy(sourceFile, tempFile);
tempFile.renameTo(resultFile);
return resultFile;
}
private File handleByteArrayMessage(byte[] bytes, File tempFile,
File resultFile) throws IOException {
private File handleByteArrayMessage(byte[] bytes, File tempFile, File resultFile) throws IOException {
FileCopyUtils.copy(bytes, tempFile);
tempFile.renameTo(resultFile);
return resultFile;
}
private File handleStringMessage(String content, File tempFile,
File resultFile, String charset) throws IOException {
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(
tempFile), charset);
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile), charset);
FileCopyUtils.copy(content, writer);
tempFile.renameTo(resultFile);
return resultFile;
}
public void setTemporaryBufferFolder(Resource temporaryBufferFolder) {
this.temporaryBufferFolder = temporaryBufferFolder;
}
public void setFileNameGenerator(FileNameGenerator fileNameGenerator) {
this.fileNameGenerator = fileNameGenerator;
}
private File redeemForStorableFile(Message<?> msg)
throws MessageDeliveryException {
private File redeemForStorableFile(Message<?> message) throws MessageDeliveryException {
try {
Object payload = msg.getPayload();
String generateFileName = this.fileNameGenerator.generateFileName(msg);
File tempFile = new File(temporaryBufferFolderFile,
generateFileName + TEMPORARY_FILE_SUFFIX);
File resultFile = new File(temporaryBufferFolderFile,
generateFileName);
Object payload = message.getPayload();
String generateFileName = this.fileNameGenerator.generateFileName(message);
File tempFile = new File(temporaryBufferFolderFile, generateFileName + TEMPORARY_FILE_SUFFIX);
File resultFile = new File(temporaryBufferFolderFile, generateFileName);
File sendableFile;
if (payload instanceof String) {
sendableFile = this.handleStringMessage((String) payload,
tempFile, resultFile, this.charset);
} else if (payload instanceof File) {
sendableFile = this.handleFileMessage((File) payload, tempFile,
resultFile);
} else if (payload instanceof byte[]) {
sendableFile = this.handleByteArrayMessage((byte[]) payload,
tempFile, resultFile);
} else {
sendableFile = this.handleStringMessage((String) payload, tempFile, resultFile, this.charset);
}
else if (payload instanceof File) {
sendableFile = this.handleFileMessage((File) payload, tempFile, resultFile);
}
else if (payload instanceof byte[]) {
sendableFile = this.handleByteArrayMessage((byte[]) payload, tempFile, resultFile);
}
else {
sendableFile = null;
}
return sendableFile;
} catch (Throwable th) {
throw new MessageDeliveryException(msg);
}
}
public void setCharset(String charset) {
this.charset = charset;
catch (Throwable th) {
throw new MessageDeliveryException(message);
}
}
/* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */
public void handleMessage(Message<?> message)
throws MessageRejectedException, MessageHandlingException,
MessageDeliveryException {
public void handleMessage(Message<?> message) {
Assert.notNull(message, "'message' must not be null");
Object payload = message.getPayload();
Assert.notNull(payload, "Message payload must not be null");
File file = this.redeemForStorableFile(message);
if ((file != null) && file.exists()) {
FTPClient client = null;
boolean sentSuccesfully;
try {
client = getFtpClient();
sentSuccesfully = sendFile(file, client);
@@ -167,14 +161,17 @@ public class FtpSendingMessageHandler implements MessageHandler,
"File [" + file +
"] not found in local working directory; it was moved or deleted unexpectedly",
e);
} catch (IOException e) {
}
catch (IOException e) {
throw new MessageDeliveryException(message,
"Error transferring file [" + file +
"] from local working directory to remote FTP directory", e);
} catch (Exception e) {
}
catch (Exception e) {
throw new MessageDeliveryException(message,
"Error handling message for file [" + file + "]", e);
} finally {
}
finally {
if (file.exists()) {
try {
file.delete();
@@ -182,12 +179,10 @@ public class FtpSendingMessageHandler implements MessageHandler,
/// noop
}
}
if (client != null) {
ftpClientPool.releaseClient(client);
}
}
if (!sentSuccesfully) {
throw new MessageDeliveryException(message,
"Failed to store file '" + file + "'");
@@ -195,22 +190,19 @@ public class FtpSendingMessageHandler implements MessageHandler,
}
}
private boolean sendFile(File file, FTPClient client)
throws FileNotFoundException, IOException {
private boolean sendFile(File file, FTPClient client) throws FileNotFoundException, IOException {
FileInputStream fileInputStream = new FileInputStream(file);
boolean sent = client.storeFile(file.getName(), fileInputStream);
fileInputStream.close();
return sent;
}
private FTPClient getFtpClient() throws SocketException, IOException {
FTPClient client;
client = this.ftpClientPool.getClient();
Assert.state(client != null,
FtpClientPool.class.getSimpleName() +
" returned 'null' client this most likely a bug in the pool implementation.");
Assert.state(client != null, FtpClientPool.class.getSimpleName() +
" returned 'null' client this most likely a bug in the pool implementation.");
return client;
}
}

View File

@@ -71,9 +71,10 @@ public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean<Ftp
defaultFtpClientFactory);
FtpSendingMessageHandler ftpSendingMessageHandler = new FtpSendingMessageHandler(queuedFtpClientPool);
ftpSendingMessageHandler.setCharset( this.charset);
if (this.charset != null) {
ftpSendingMessageHandler.setCharset(this.charset);
}
ftpSendingMessageHandler.afterPropertiesSet();
return ftpSendingMessageHandler;
}

View File

@@ -5,6 +5,7 @@
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-parent</artifactId>
<version>2.0.0.BUILD-SNAPSHOT</version>
<relativePath>../spring-integration-parent/pom.xml</relativePath>
</parent>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-groovy</artifactId>
@@ -24,19 +25,16 @@
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework.version}</version>
</dependency>
<!-- test dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -1,355 +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.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.integration.Message;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
/**
* Default implementation of {@link InboundRequestMapper} for inbound HttpServletRequests.
* The request will be mapped according to the following rules:
* <ul>
* <li>For a GET request or a POST request with a Content-Type of
* "application/x-www-form-urlencoded", the parameter Map will be copied as the
* payload. The map will be an instance of {@link MultiValueMap} where the keys are
* Strings and the values are Lists of Strings. Those Lists are populated from the
* String array values of the original request parameter Map as described for the
* {@link ServletRequest#getParameterMap()} method.</li>
* <li>If a MultipartResolver has been provided, and a multipart request is
* detected, the multipart file content will be converted to String for any
* "text" content type, or byte arrays otherwise.</li>
* <li>For other request types, the request body will be used as the payload
* and the type will depend on the Content-Type header value. If it begins with
* "text", a String will be created. If the Content-Type is
* "application/x-java-serialized-object", the request body will be expected to
* contain a Serializable Object, and that will be used as the message payload.
* Otherwise, the payload will be a byte array.</li>
* </ul>
* In all cases, the original request headers will be passed in the
* MessageHeaders. Likewise, the following headers will be added:
* <ul>
* <li>{@link HttpHeaders#REQUEST_URL}</li>
* <li>{@link HttpHeaders#REQUEST_METHOD}</li>
* <li>{@link HttpHeaders#USER_PRINCIPAL} (if available)</li>
* </ul>
*
* @author Mark Fisher
* @author Oleg Zhurakousky
* @since 1.0.2
*/
public class DefaultInboundRequestMapper implements InboundRequestMapper {
private final Log logger = LogFactory.getLog(getClass());
private volatile MultipartResolver multipartResolver;
private volatile String multipartCharset = null;
private volatile boolean copyUploadedFiles;
/**
* Specify the {@link MultipartResolver} to use when checking requests.
* If no resolver is provided, this mapper will not support multipart
* requests.
*/
public void setMultipartResolver(MultipartResolver multipartResolver) {
this.multipartResolver = multipartResolver;
}
/**
* Specify the charset name to use when converting multipart file content
* into Strings.
*/
public void setMultipartCharset(String multipartCharset) {
this.multipartCharset = multipartCharset;
}
/**
* Specify whether uploaded multipart files should be copied to a temporary
* file on the server. If this is set to 'true', the payload map will
* contain a File instance as the value for each multipart file entry.
* Otherwise the uploaded file's content will be converted to either a
* String or byte array based on the content-type (String for "text/*" and
* byte array otherwise). The default value is false.
*/
public void setCopyUploadedFiles(boolean copyUploadedFiles) {
this.copyUploadedFiles = copyUploadedFiles;
}
public Message<?> toMessage(HttpServletRequest request) throws Exception {
try {
request = this.checkMultipart(request);
Object payload = createPayloadFromRequest(request);
MessageBuilder<?> builder = MessageBuilder.withPayload(payload);
this.populateHeaders(request, builder);
return builder.build();
}
finally {
this.cleanupMultipart(request);
}
}
/**
* Convert the request into a multipart request to make multiparts available.
* If no multipart resolver is set, simply use the existing request.
* @param request current HTTP request
* @return the processed request (multipart wrapper if necessary)
* @see MultipartResolver#resolveMultipart
*/
private HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (request instanceof MultipartHttpServletRequest) {
logger.debug("Request is already a MultipartHttpServletRequest");
}
else {
return this.multipartResolver.resolveMultipart(request);
}
}
return request;
}
/**
* Clean up any resources used by the given multipart request (if any).
* @param request current HTTP request
* @see MultipartResolver#cleanupMultipart
*/
private void cleanupMultipart(HttpServletRequest request) {
if (this.multipartResolver != null && request instanceof MultipartHttpServletRequest) {
this.multipartResolver.cleanupMultipart((MultipartHttpServletRequest) request);
}
}
private Object createPayloadFromRequest(HttpServletRequest request) throws Exception {
Object payload = null;
String contentType = request.getContentType() != null ? request.getContentType() : "";
if (request instanceof MultipartHttpServletRequest) {
payload = this.createPayloadFromMultipartRequest((MultipartHttpServletRequest) request);
}
else if (contentType.startsWith("multipart/form-data")) {
throw new IllegalArgumentException("Content-Type of 'multipart/form-data' requires a MultipartResolver." +
" Try configuring a MultipartResolver within the ApplicationContext.");
}
else if (request.getMethod().equals("GET")) {
if (logger.isDebugEnabled()) {
logger.debug("received GET request, using parameter map as payload");
}
payload = this.createPayloadFromParameterMap(request);
}
else if (contentType.startsWith("application/x-www-form-urlencoded")) {
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod()
+ " request with form data, using parameter map as payload");
}
payload = createPayloadFromParameterMap(request);
}
else if (contentType.startsWith("text")) {
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod()
+ " request, creating payload with text content");
}
payload = createPayloadFromTextContent(request);
}
else if (contentType.startsWith("application/x-java-serialized-object")) {
payload = createPayloadFromSerializedObject(request);
}
else {
payload = createPayloadFromInputStream(request);
}
return payload;
}
@SuppressWarnings("unchecked")
private Object createPayloadFromMultipartRequest(MultipartHttpServletRequest multipartRequest) {
Map<String, Object> payloadMap = new HashMap<String, Object>(multipartRequest.getParameterMap());
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
MultipartFile multipartFile = entry.getValue();
if (multipartFile.isEmpty()) {
continue;
}
try {
if (this.copyUploadedFiles) {
File tmpFile = File.createTempFile("si_", null);
multipartFile.transferTo(tmpFile);
payloadMap.put(entry.getKey(), tmpFile);
if (logger.isDebugEnabled()) {
logger.debug("copied uploaded file [" + multipartFile.getOriginalFilename() +
"] to temporary file [" + tmpFile.getAbsolutePath() + "]");
}
}
else if (multipartFile.getContentType() != null && multipartFile.getContentType().startsWith("text")) {
String multipartFileAsString = this.multipartCharset != null ?
new String(multipartFile.getBytes(), this.multipartCharset) :
new String(multipartFile.getBytes());
payloadMap.put(entry.getKey(), multipartFileAsString);
}
else {
payloadMap.put(entry.getKey(), multipartFile.getBytes());
}
}
catch (IOException e) {
throw new IllegalArgumentException("Cannot read contents of multipart file", e);
}
}
return Collections.unmodifiableMap(payloadMap);
}
@SuppressWarnings("unchecked")
private Object createPayloadFromParameterMap(HttpServletRequest request) {
return new UnmodifiableRequestParameterMap(request.getParameterMap());
}
private Object createPayloadFromTextContent(HttpServletRequest request) throws IOException {
String charset = request.getCharacterEncoding() != null ? request.getCharacterEncoding() : "utf-8";
return new String(FileCopyUtils.copyToByteArray(request.getInputStream()), charset);
}
private Object createPayloadFromSerializedObject(HttpServletRequest request) {
try {
return new ObjectInputStream(request.getInputStream()).readObject();
}
catch (Exception e) {
throw new IllegalArgumentException("failed to deserialize Object in request", e);
}
}
private byte[] createPayloadFromInputStream(HttpServletRequest request) throws Exception {
InputStream stream = request.getInputStream();
int length = request.getContentLength();
if (length == -1) {
throw new ResponseStatusCodeException(HttpServletResponse.SC_LENGTH_REQUIRED);
}
if (logger.isDebugEnabled()) {
logger.debug("received " + request.getMethod() + " request, "
+ "creating byte array payload with content lenth: " + length);
}
byte[] bytes = new byte[length];
stream.read(bytes, 0, length);
return bytes;
}
private void populateHeaders(HttpServletRequest request, MessageBuilder<?> builder) {
Enumeration<?> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
Enumeration<?> headerEnum = request.getHeaders(headerName);
if (headerEnum != null) {
List<Object> headers = new ArrayList<Object>();
while (headerEnum.hasMoreElements()) {
headers.add(headerEnum.nextElement());
}
if (headers.size() == 1) {
builder.setHeader(headerName, headers.get(0));
}
else if (headers.size() > 1) {
builder.setHeader(headerName, headers);
}
}
}
}
builder.setHeader(HttpHeaders.REQUEST_URL, request.getRequestURL().toString());
builder.setHeader(HttpHeaders.REQUEST_METHOD, request.getMethod());
builder.setHeader(HttpHeaders.USER_PRINCIPAL, request.getUserPrincipal());
}
/**
* Map class that extends {@link LinkedMultiValueMap} and implements Serializable.
* The contents of the map are unmodifiable, so calling any modification operation
* (e.g. put, add, or remove) will result in an UnsupportedOperationException.
*/
@SuppressWarnings("serial")
private static class UnmodifiableRequestParameterMap
extends LinkedMultiValueMap<String, String> implements Serializable { // TODO: in 3.0.1 LMVM implements Serializable
UnmodifiableRequestParameterMap(Map<String, String[]> parameters) {
for (Map.Entry<String, String[]> entry : parameters.entrySet()) {
super.put(entry.getKey(), Arrays.asList(entry.getValue()));
}
}
@Override
public void add(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public List<String> put(String key, List<String> value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends String, ? extends List<String>> m) {
throw new UnsupportedOperationException();
}
@Override
public List<String> remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void set(String key, String value) {
throw new UnsupportedOperationException();
}
@Override
public void setAll(Map<String, String> values) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, String> toSingleValueMap() {
return Collections.unmodifiableMap(super.toSingleValueMap());
}
}
}

View File

@@ -69,6 +69,7 @@ public class HttpOutboundChannelAdapterParser extends AbstractOutboundChannelAda
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "extract-payload");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expected-response-type");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-factory");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-handler");
List<Element> uriVariableElements = DomUtils.getChildElementsByTagName(element, "uri-variable");
if (!CollectionUtils.isEmpty(uriVariableElements)) {
Map<String, String> uriVariableExpressions = new HashMap<String, String>();

View File

@@ -75,6 +75,7 @@ public class HttpOutboundGatewayParser extends AbstractConsumerEndpointParser {
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "expected-response-type");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-timeout", "sendTimeout");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "request-factory");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-handler");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "reply-channel", "outputChannel");
List<Element> uriVariableElements = DomUtils.getChildElementsByTagName(element, "uri-variable");
if (!CollectionUtils.isEmpty(uriVariableElements)) {

View File

@@ -245,6 +245,9 @@
</xsd:attribute>
<xsd:attribute name="request-factory" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a ClientHttpRequestFactory to be used by the underlying RestTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.http.client.ClientHttpRequestFactory" />
@@ -252,6 +255,18 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a ResponseErrorHandler to be used by the underlying RestTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.client.ResponseErrorHandler" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
@@ -347,6 +362,9 @@
<xsd:attribute name="charset" type="xsd:string" />
<xsd:attribute name="request-factory" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a ClientHttpRequestFactory to be used by the underlying RestTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.http.client.ClientHttpRequestFactory" />
@@ -354,6 +372,18 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-handler" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Reference to a ResponseErrorHandler to be used by the underlying RestTemplate.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.web.client.ResponseErrorHandler" />
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="order" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -1,127 +0,0 @@
/*
* Copyright 2002-2009 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 static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.integration.Message;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
/**
* @author Iwein Fuld
* @author Mark Fisher
*/
@SuppressWarnings("unchecked")
public class DefaultInboundRequestMapperTests {
private static final String SIMPLE_STRING = "just ascii";
private static final String COMPLEX_STRING = "A\u00ea\u00f1\u00fcC";
private DefaultInboundRequestMapper mapper = new DefaultInboundRequestMapper();
@Test
public void simpleUtf8TextMapping() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
request.setCharacterEncoding("utf-8");
byte[] bytes = SIMPLE_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(SIMPLE_STRING));
}
@Test
public void complexUtf8TextMapping() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
// don't forget to specify the character encoding on the request or you
// will end up with unpredictable results!
request.setCharacterEncoding("utf-8");
byte[] bytes = COMPLEX_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(COMPLEX_STRING));
}
@Test
public void newlineTest() throws Exception {
String content = "foo\nbar\n";
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
byte[] bytes = content.getBytes();
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(content));
}
@Test
public void emptyStringTest() throws Exception {
String content = "";
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
byte[] bytes = content.getBytes();
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
assertThat(message.getPayload(), is(content));
}
@Test
public void multipartUpload() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>();
MultipartFile file = new StubMultipartFile("file", "testFile.txt", "foo");
files.add("file", file);
Map<String, String[]> params = new HashMap<String, String[]>();
MultipartHttpServletRequest multipartRequest = new DefaultMultipartHttpServletRequest(request, files, params);
mapper.setCopyUploadedFiles(true);
Message<?> result = mapper.toMessage(multipartRequest);
File tmpFile = (File) ((Map) result.getPayload()).get("file");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileCopyUtils.copy(new FileInputStream(tmpFile), baos);
assertThat(baos.toString(), is("foo"));
tmpFile.deleteOnExit();
}
@Test
public void testProcessMessageWithDollar() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setContentType("text");
request.setCharacterEncoding("utf-8");
byte[] bytes = SIMPLE_STRING.getBytes("utf-8");
request.setContent(bytes);
Message<String> message = (Message<String>) mapper.toMessage(request);
ExpressionEvaluatingMessageProcessor processor = new ExpressionEvaluatingMessageProcessor("headers['$http_requestUrl']");
assertEquals(message.getHeaders().get(HttpHeaders.REQUEST_URL), processor.processMessage(message));
}
}

View File

@@ -24,6 +24,7 @@
expected-response-type="java.lang.Boolean"
mapped-request-headers="requestHeader1, requestHeader2"
request-factory="testRequestFactory"
error-handler="testErrorHandler"
order="77"
auto-startup="false">
<uri-variable name="foo" expression="headers.bar"/>
@@ -31,6 +32,8 @@
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundChannelAdapterParserTests$StubErrorHandler"/>
<util:list id="converterList">
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>

View File

@@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Map;
import org.junit.Test;
@@ -32,12 +33,14 @@ import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.http.HttpRequestExecutingMessageHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.ResponseErrorHandler;
/**
* @author Mark Fisher
@@ -94,6 +97,8 @@ public class HttpOutboundChannelAdapterParserTests {
assertEquals(converterListBean, templateAccessor.getPropertyValue("messageConverters"));
Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory");
assertEquals(requestFactoryBean, requestFactory);
Object errorHandlerBean = this.applicationContext.getBean("testErrorHandler");
assertEquals(errorHandlerBean, templateAccessor.getPropertyValue("errorHandler"));
assertEquals("http://localhost/test2/{foo}", handlerAccessor.getPropertyValue("uri"));
assertEquals(HttpMethod.GET, handlerAccessor.getPropertyValue("httpMethod"));
assertEquals("UTF-8", handlerAccessor.getPropertyValue("charset"));
@@ -111,4 +116,15 @@ public class HttpOutboundChannelAdapterParserTests {
assertTrue(ObjectUtils.containsElement(mappedRequestHeaders, "requestHeader2"));
}
public static class StubErrorHandler implements ResponseErrorHandler {
public boolean hasError(ClientHttpResponse response) throws IOException {
return false;
}
public void handleError(ClientHttpResponse response) throws IOException {
}
}
}

View File

@@ -31,6 +31,7 @@
expected-response-type="java.lang.String"
mapped-request-headers="requestHeader1, requestHeader2"
mapped-response-headers="responseHeader"
error-handler="testErrorHandler"
reply-channel="replies"
charset="UTF-8"
order="77"
@@ -40,6 +41,8 @@
<beans:bean id="testRequestFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory"/>
<beans:bean id="testErrorHandler" class="org.springframework.integration.http.config.HttpOutboundGatewayParserTests$StubErrorHandler"/>
<util:list id="converterList">
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>
<beans:bean class="org.springframework.integration.http.config.StubHttpMessageConverter"/>

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.Map;
import org.junit.Test;
@@ -33,6 +34,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.endpoint.AbstractEndpoint;
@@ -40,6 +42,7 @@ import org.springframework.integration.http.HttpRequestExecutingMessageHandler;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.ResponseErrorHandler;
/**
* @author Mark Fisher
@@ -105,6 +108,8 @@ public class HttpOutboundGatewayParserTests {
assertEquals(false, handlerAccessor.getPropertyValue("extractPayload"));
Object requestFactoryBean = this.applicationContext.getBean("testRequestFactory");
assertEquals(requestFactoryBean, requestFactory);
Object errorHandlerBean = this.applicationContext.getBean("testErrorHandler");
assertEquals(errorHandlerBean, templateAccessor.getPropertyValue("errorHandler"));
Object sendTimeout = new DirectFieldAccessor(
handlerAccessor.getPropertyValue("messagingTemplate")).getPropertyValue("sendTimeout");
assertEquals(new Long("1234"), sendTimeout);
@@ -122,4 +127,15 @@ public class HttpOutboundGatewayParserTests {
assertEquals("responseHeader", mappedResponseHeaders[0]);
}
public static class StubErrorHandler implements ResponseErrorHandler {
public boolean hasError(ClientHttpResponse response) throws IOException {
return false;
}
public void handleError(ClientHttpResponse response) throws IOException {
}
}
}

Some files were not shown because too many files have changed in this diff Show More